Skip to content

Commit b7ca138

Browse files
authored
[Feature] Expressions example plugin (#1438)
* feat(Expressions): Initial demo plugin Signed-off-by: Ashwin Pc <ashwinpc@amazon.com> * feat(Expressions): Adds handlers tab Signed-off-by: Ashwin Pc <ashwinpc@amazon.com> * feat(Expressions): Adds playground tab Signed-off-by: Ashwin Pc <ashwinpc@amazon.com> * chore: Better expression playground messaging Signed-off-by: Ashwin Pc <ashwinpc@amazon.com> * feat(Expressions): Adds expression explorer Signed-off-by: Ashwin Pc <ashwinpc@amazon.com> * feat(Expressions): A better explorer Signed-off-by: Ashwin Pc <ashwinpc@amazon.com> * fix(Expressions): Fix basic demo abort error Signed-off-by: Ashwin Pc <ashwinpc@amazon.com> * Updates readme and fixes rendering visualizations Signed-off-by: Ashwin Pc <ashwinpc@amazon.com>
1 parent 1caf907 commit b7ca138

33 files changed

+1400
-24
lines changed
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
module.exports = {
7+
root: true,
8+
extends: ['@elastic/eslint-config-kibana', 'plugin:@elastic/eui/recommended'],
9+
rules: {
10+
'@osd/eslint/require-license-header': 'off',
11+
},
12+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"prefix": "expressionsExample",
3+
"paths": {
4+
"expressionsExample": "."
5+
},
6+
"translations": ["translations/ja-JP.json"]
7+
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# expressions_example
2+
3+
An OpenSearch Dashboards example plugin to demonstrate the expressions plugin
4+
5+
---
6+
7+
## Development
8+
9+
See the [OpenSearch Dashboards contributing
10+
guide](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/master/CONTRIBUTING.md) for instructions
11+
setting up your development environment.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
export * from './quick_form_fn';
7+
export * from './quick_form_renderer';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { i18n } from '@osd/i18n';
7+
import {
8+
ExpressionFunctionDefinition,
9+
Render,
10+
} from '../../../../../src/plugins/expressions/public';
11+
import { QuickFormRenderValue } from './quick_form_renderer';
12+
13+
type Arguments = QuickFormRenderValue;
14+
15+
export const quickFormFn = (): ExpressionFunctionDefinition<
16+
'quick-form',
17+
unknown,
18+
Arguments,
19+
Render<QuickFormRenderValue>
20+
> => ({
21+
name: 'quick-form',
22+
type: 'render',
23+
help: i18n.translate('expressionsExample.function.avatar.help', {
24+
defaultMessage: 'Render a simple form that sends the value back as an event on click',
25+
}),
26+
args: {
27+
label: {
28+
types: ['string'],
29+
help: i18n.translate('expressionsExample.function.form.args.label.help', {
30+
defaultMessage: 'Form label',
31+
}),
32+
default: i18n.translate('expressionsExample.function.form.args.label.default', {
33+
defaultMessage: 'Input',
34+
}),
35+
},
36+
buttonLabel: {
37+
types: ['string'],
38+
help: i18n.translate('expressionsExample.function.form.args.buttonLabel.help', {
39+
defaultMessage: 'Button label',
40+
}),
41+
default: i18n.translate('expressionsExample.function.form.args.buttonLabel.default', {
42+
defaultMessage: 'Submit',
43+
}),
44+
},
45+
},
46+
fn: (input, args) => {
47+
return {
48+
type: 'render',
49+
as: 'quick-form-renderer',
50+
value: { ...args },
51+
};
52+
},
53+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React, { useCallback, useState } from 'react';
7+
import { EuiForm, EuiFormRow, EuiButton, EuiFieldText } from '@elastic/eui';
8+
import { i18n } from '@osd/i18n';
9+
import { render, unmountComponentAtNode } from 'react-dom';
10+
import { ExpressionRenderDefinition } from '../../../../../src/plugins/expressions/public';
11+
12+
export interface QuickFormRenderValue {
13+
label: string;
14+
buttonLabel: string;
15+
}
16+
17+
export const quickFormRenderer: ExpressionRenderDefinition<QuickFormRenderValue> = {
18+
name: 'quick-form-renderer',
19+
displayName: i18n.translate('expressionsExample.form.render.help', {
20+
defaultMessage: 'Render a simple input form',
21+
}),
22+
reuseDomNode: true,
23+
render: (domNode, config, handlers) => {
24+
handlers.onDestroy(() => {
25+
unmountComponentAtNode(domNode);
26+
});
27+
28+
render(
29+
<QuickForm
30+
{...config}
31+
onSubmit={(value) =>
32+
handlers.event({
33+
data: value,
34+
})
35+
}
36+
/>,
37+
domNode,
38+
handlers.done
39+
);
40+
},
41+
};
42+
43+
interface QuickFormProps extends QuickFormRenderValue {
44+
onSubmit: Function;
45+
}
46+
47+
const QuickForm = ({ onSubmit, buttonLabel, label }: QuickFormProps) => {
48+
const [value, setValue] = useState('');
49+
const handleClick = useCallback(() => {
50+
onSubmit(value);
51+
}, [onSubmit, value]);
52+
53+
return (
54+
<EuiForm>
55+
<EuiFormRow label={label}>
56+
<EuiFieldText value={value} onChange={(e) => setValue(e.target.value)} />
57+
</EuiFormRow>
58+
<EuiButton onClick={handleClick}>{buttonLabel}</EuiButton>
59+
</EuiForm>
60+
);
61+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
export * from './sleep';
7+
export * from './square';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { i18n } from '@osd/i18n';
7+
import { ExpressionFunctionDefinition } from '../../../../../src/plugins/expressions/public';
8+
9+
interface Arguments {
10+
time: number;
11+
}
12+
13+
export const sleep = (): ExpressionFunctionDefinition<'sleep', any, Arguments, any> => ({
14+
name: 'sleep',
15+
help: i18n.translate('expressionsExample.function.sleep.help', {
16+
defaultMessage: 'Generates range object',
17+
}),
18+
args: {
19+
time: {
20+
types: ['number'],
21+
help: i18n.translate('expressionsExample.function.sleep.time.help', {
22+
defaultMessage: 'Time for settimeout',
23+
}),
24+
required: false,
25+
},
26+
},
27+
fn: async (input, args, context) => {
28+
await new Promise((r) => setTimeout(r, args.time));
29+
return input;
30+
},
31+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { i18n } from '@osd/i18n';
7+
import { ExpressionFunctionDefinition } from '../../../../../src/plugins/expressions/public';
8+
9+
export const square = (): ExpressionFunctionDefinition<'square', number, {}, any> => ({
10+
name: 'square',
11+
help: i18n.translate('expressionsExample.function.square.help', {
12+
defaultMessage: 'Squares the input',
13+
}),
14+
args: {},
15+
fn: async (input, args, context) => {
16+
return input * input;
17+
},
18+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
export * from './basic';
7+
export * from './render';
8+
export * from './action';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { i18n } from '@osd/i18n';
7+
import {
8+
ExpressionFunctionDefinition,
9+
Render,
10+
} from '../../../../../src/plugins/expressions/public';
11+
import { AvatarRenderValue } from './avatar_renderer';
12+
13+
type Arguments = AvatarRenderValue;
14+
15+
export const avatarFn = (): ExpressionFunctionDefinition<
16+
'avatar',
17+
unknown,
18+
Arguments,
19+
Render<AvatarRenderValue>
20+
> => ({
21+
name: 'avatar',
22+
type: 'render',
23+
help: i18n.translate('expressionsExample.function.avatar.help', {
24+
defaultMessage: 'Avatar expression function',
25+
}),
26+
args: {
27+
name: {
28+
types: ['string'],
29+
help: i18n.translate('expressionsExample.function.avatar.args.name.help', {
30+
defaultMessage: 'Enter Name',
31+
}),
32+
required: true,
33+
},
34+
size: {
35+
types: ['string'],
36+
help: i18n.translate('expressionsExample.function.avatar.args.size.help', {
37+
defaultMessage: 'Size of the avatar',
38+
}),
39+
default: 'l',
40+
},
41+
},
42+
fn: (input, args) => {
43+
return {
44+
type: 'render',
45+
as: 'avatar',
46+
value: {
47+
name: args.name,
48+
size: args.size,
49+
},
50+
};
51+
},
52+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import { EuiAvatar, EuiAvatarProps } from '@elastic/eui';
8+
import { i18n } from '@osd/i18n';
9+
import { render, unmountComponentAtNode } from 'react-dom';
10+
import { ExpressionRenderDefinition } from '../../../../../src/plugins/expressions/public';
11+
12+
export interface AvatarRenderValue {
13+
name: string;
14+
size: EuiAvatarProps['size'];
15+
}
16+
17+
export const avatar: ExpressionRenderDefinition<AvatarRenderValue> = {
18+
name: 'avatar',
19+
displayName: i18n.translate('expressionsExample.render.help', {
20+
defaultMessage: 'Render an avatar',
21+
}),
22+
reuseDomNode: true,
23+
render: (domNode, { name, size }, handlers) => {
24+
handlers.onDestroy(() => {
25+
unmountComponentAtNode(domNode);
26+
});
27+
28+
render(<EuiAvatar size={size} name={name} />, domNode, handlers.done);
29+
},
30+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
export * from './avatar_fn';
7+
export * from './avatar_renderer';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
export const PLUGIN_ID = 'expressionsExample';
7+
export const PLUGIN_NAME = 'expressions_example';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"id": "expressionsExample",
3+
"version": "1.0.0",
4+
"opensearchDashboardsVersion": "opensearchDashboards",
5+
"server": false,
6+
"ui": true,
7+
"requiredPlugins": [
8+
"navigation",
9+
"expressions",
10+
"developerExamples",
11+
"opensearchDashboardsReact"
12+
],
13+
"optionalPlugins": []
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import ReactDOM from 'react-dom';
8+
import { AppMountParameters, CoreStart } from '../../../src/core/public';
9+
import { ExpressionsExampleStartDependencies } from './types';
10+
import { ExpressionsExampleApp } from './components/app';
11+
import { OpenSearchDashboardsContextProvider } from '../../../src/plugins/opensearch_dashboards_react/public';
12+
13+
export const renderApp = (
14+
{ notifications, http }: CoreStart,
15+
{ navigation, expressions }: ExpressionsExampleStartDependencies,
16+
{ appBasePath, element }: AppMountParameters
17+
) => {
18+
const services = { expressions, notifications };
19+
ReactDOM.render(
20+
<OpenSearchDashboardsContextProvider services={services}>
21+
<ExpressionsExampleApp
22+
basename={appBasePath}
23+
notifications={notifications}
24+
http={http}
25+
navigation={navigation}
26+
/>
27+
</OpenSearchDashboardsContextProvider>,
28+
element
29+
);
30+
31+
return () => ReactDOM.unmountComponentAtNode(element);
32+
};

0 commit comments

Comments
 (0)