Skip to content

Commit

Permalink
[Feature] Expressions example plugin (#1438) (#2602)
Browse files Browse the repository at this point in the history
* 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>
(cherry picked from commit b7ca138)

Co-authored-by: Ashwin P Chandran <ashwinpc@amazon.com>
  • Loading branch information
opensearch-trigger-bot[bot] and ashwin-pc authored Oct 19, 2022
1 parent c08c61a commit e228532
Show file tree
Hide file tree
Showing 33 changed files with 1,400 additions and 24 deletions.
12 changes: 12 additions & 0 deletions examples/expressions_example/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

module.exports = {
root: true,
extends: ['@elastic/eslint-config-kibana', 'plugin:@elastic/eui/recommended'],
rules: {
'@osd/eslint/require-license-header': 'off',
},
};
7 changes: 7 additions & 0 deletions examples/expressions_example/.i18nrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"prefix": "expressionsExample",
"paths": {
"expressionsExample": "."
},
"translations": ["translations/ja-JP.json"]
}
11 changes: 11 additions & 0 deletions examples/expressions_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# expressions_example

An OpenSearch Dashboards example plugin to demonstrate the expressions plugin

---

## Development

See the [OpenSearch Dashboards contributing
guide](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/master/CONTRIBUTING.md) for instructions
setting up your development environment.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './quick_form_fn';
export * from './quick_form_renderer';
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { i18n } from '@osd/i18n';
import {
ExpressionFunctionDefinition,
Render,
} from '../../../../../src/plugins/expressions/public';
import { QuickFormRenderValue } from './quick_form_renderer';

type Arguments = QuickFormRenderValue;

export const quickFormFn = (): ExpressionFunctionDefinition<
'quick-form',
unknown,
Arguments,
Render<QuickFormRenderValue>
> => ({
name: 'quick-form',
type: 'render',
help: i18n.translate('expressionsExample.function.avatar.help', {
defaultMessage: 'Render a simple form that sends the value back as an event on click',
}),
args: {
label: {
types: ['string'],
help: i18n.translate('expressionsExample.function.form.args.label.help', {
defaultMessage: 'Form label',
}),
default: i18n.translate('expressionsExample.function.form.args.label.default', {
defaultMessage: 'Input',
}),
},
buttonLabel: {
types: ['string'],
help: i18n.translate('expressionsExample.function.form.args.buttonLabel.help', {
defaultMessage: 'Button label',
}),
default: i18n.translate('expressionsExample.function.form.args.buttonLabel.default', {
defaultMessage: 'Submit',
}),
},
},
fn: (input, args) => {
return {
type: 'render',
as: 'quick-form-renderer',
value: { ...args },
};
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useCallback, useState } from 'react';
import { EuiForm, EuiFormRow, EuiButton, EuiFieldText } from '@elastic/eui';
import { i18n } from '@osd/i18n';
import { render, unmountComponentAtNode } from 'react-dom';
import { ExpressionRenderDefinition } from '../../../../../src/plugins/expressions/public';

export interface QuickFormRenderValue {
label: string;
buttonLabel: string;
}

export const quickFormRenderer: ExpressionRenderDefinition<QuickFormRenderValue> = {
name: 'quick-form-renderer',
displayName: i18n.translate('expressionsExample.form.render.help', {
defaultMessage: 'Render a simple input form',
}),
reuseDomNode: true,
render: (domNode, config, handlers) => {
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});

render(
<QuickForm
{...config}
onSubmit={(value) =>
handlers.event({
data: value,
})
}
/>,
domNode,
handlers.done
);
},
};

interface QuickFormProps extends QuickFormRenderValue {
onSubmit: Function;
}

const QuickForm = ({ onSubmit, buttonLabel, label }: QuickFormProps) => {
const [value, setValue] = useState('');
const handleClick = useCallback(() => {
onSubmit(value);
}, [onSubmit, value]);

return (
<EuiForm>
<EuiFormRow label={label}>
<EuiFieldText value={value} onChange={(e) => setValue(e.target.value)} />
</EuiFormRow>
<EuiButton onClick={handleClick}>{buttonLabel}</EuiButton>
</EuiForm>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './sleep';
export * from './square';
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { i18n } from '@osd/i18n';
import { ExpressionFunctionDefinition } from '../../../../../src/plugins/expressions/public';

interface Arguments {
time: number;
}

export const sleep = (): ExpressionFunctionDefinition<'sleep', any, Arguments, any> => ({
name: 'sleep',
help: i18n.translate('expressionsExample.function.sleep.help', {
defaultMessage: 'Generates range object',
}),
args: {
time: {
types: ['number'],
help: i18n.translate('expressionsExample.function.sleep.time.help', {
defaultMessage: 'Time for settimeout',
}),
required: false,
},
},
fn: async (input, args, context) => {
await new Promise((r) => setTimeout(r, args.time));
return input;
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { i18n } from '@osd/i18n';
import { ExpressionFunctionDefinition } from '../../../../../src/plugins/expressions/public';

export const square = (): ExpressionFunctionDefinition<'square', number, {}, any> => ({
name: 'square',
help: i18n.translate('expressionsExample.function.square.help', {
defaultMessage: 'Squares the input',
}),
args: {},
fn: async (input, args, context) => {
return input * input;
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './basic';
export * from './render';
export * from './action';
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { i18n } from '@osd/i18n';
import {
ExpressionFunctionDefinition,
Render,
} from '../../../../../src/plugins/expressions/public';
import { AvatarRenderValue } from './avatar_renderer';

type Arguments = AvatarRenderValue;

export const avatarFn = (): ExpressionFunctionDefinition<
'avatar',
unknown,
Arguments,
Render<AvatarRenderValue>
> => ({
name: 'avatar',
type: 'render',
help: i18n.translate('expressionsExample.function.avatar.help', {
defaultMessage: 'Avatar expression function',
}),
args: {
name: {
types: ['string'],
help: i18n.translate('expressionsExample.function.avatar.args.name.help', {
defaultMessage: 'Enter Name',
}),
required: true,
},
size: {
types: ['string'],
help: i18n.translate('expressionsExample.function.avatar.args.size.help', {
defaultMessage: 'Size of the avatar',
}),
default: 'l',
},
},
fn: (input, args) => {
return {
type: 'render',
as: 'avatar',
value: {
name: args.name,
size: args.size,
},
};
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { EuiAvatar, EuiAvatarProps } from '@elastic/eui';
import { i18n } from '@osd/i18n';
import { render, unmountComponentAtNode } from 'react-dom';
import { ExpressionRenderDefinition } from '../../../../../src/plugins/expressions/public';

export interface AvatarRenderValue {
name: string;
size: EuiAvatarProps['size'];
}

export const avatar: ExpressionRenderDefinition<AvatarRenderValue> = {
name: 'avatar',
displayName: i18n.translate('expressionsExample.render.help', {
defaultMessage: 'Render an avatar',
}),
reuseDomNode: true,
render: (domNode, { name, size }, handlers) => {
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});

render(<EuiAvatar size={size} name={name} />, domNode, handlers.done);
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './avatar_fn';
export * from './avatar_renderer';
7 changes: 7 additions & 0 deletions examples/expressions_example/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export const PLUGIN_ID = 'expressionsExample';
export const PLUGIN_NAME = 'expressions_example';
14 changes: 14 additions & 0 deletions examples/expressions_example/opensearch_dashboards.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"id": "expressionsExample",
"version": "1.0.0",
"opensearchDashboardsVersion": "opensearchDashboards",
"server": false,
"ui": true,
"requiredPlugins": [
"navigation",
"expressions",
"developerExamples",
"opensearchDashboardsReact"
],
"optionalPlugins": []
}
32 changes: 32 additions & 0 deletions examples/expressions_example/public/application.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import ReactDOM from 'react-dom';
import { AppMountParameters, CoreStart } from '../../../src/core/public';
import { ExpressionsExampleStartDependencies } from './types';
import { ExpressionsExampleApp } from './components/app';
import { OpenSearchDashboardsContextProvider } from '../../../src/plugins/opensearch_dashboards_react/public';

export const renderApp = (
{ notifications, http }: CoreStart,
{ navigation, expressions }: ExpressionsExampleStartDependencies,
{ appBasePath, element }: AppMountParameters
) => {
const services = { expressions, notifications };
ReactDOM.render(
<OpenSearchDashboardsContextProvider services={services}>
<ExpressionsExampleApp
basename={appBasePath}
notifications={notifications}
http={http}
navigation={navigation}
/>
</OpenSearchDashboardsContextProvider>,
element
);

return () => ReactDOM.unmountComponentAtNode(element);
};
Loading

0 comments on commit e228532

Please sign in to comment.