mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
parent
73d1f644cd
commit
d4084f50e2
31 changed files with 1085 additions and 3 deletions
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) > [ast](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md)
|
||||
|
||||
## ExpressionsInspectorAdapter.ast property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
get ast(): any;
|
||||
```
|
|
@ -0,0 +1,22 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) > [logAST](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md)
|
||||
|
||||
## ExpressionsInspectorAdapter.logAST() method
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
logAST(ast: any): void;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| ast | <code>any</code> | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
`void`
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md)
|
||||
|
||||
## ExpressionsInspectorAdapter class
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare class ExpressionsInspectorAdapter extends EventEmitter
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [ast](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md) | | <code>any</code> | |
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Modifiers | Description |
|
||||
| --- | --- | --- |
|
||||
| [logAST(ast)](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md) | | |
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
| [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) | |
|
||||
| [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.md) | |
|
||||
| [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) | |
|
||||
| [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) | |
|
||||
| [ExpressionsPublicPlugin](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.md) | |
|
||||
| [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) | <code>ExpressionsService</code> class is used for multiple purposes:<!-- -->1. It implements the same Expressions service that can be used on both: (1) server-side and (2) browser-side. 2. It implements the same Expressions service that users can fork/clone, thus have their own instance of the Expressions plugin. 3. <code>ExpressionsService</code> defines the public contracts of \*setup\* and \*start\* Kibana Platform life-cycles for ease-of-use on server-side and browser-side. 4. <code>ExpressionsService</code> creates a bound version of all exported contract functions. 5. Functions are bound the way there are:<!-- -->\`\`\`<!-- -->ts registerFunction = (...args: Parameters<!-- --><<!-- -->Executor\['registerFunction'\]<!-- -->> ): ReturnType<!-- --><<!-- -->Executor\['registerFunction'\]<!-- -->> =<!-- -->> this.executor.registerFunction(...args); \`\`\`<!-- -->so that JSDoc appears in developers IDE when they use those <code>plugins.expressions.registerFunction(</code>. |
|
||||
| [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) | |
|
||||
|
|
8
examples/expressions_explorer/README.md
Normal file
8
examples/expressions_explorer/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
## expressions explorer
|
||||
|
||||
This example expressions explorer app shows how to:
|
||||
- to run expression
|
||||
- to render expression output
|
||||
- emit events from expression renderer and handle them
|
||||
|
||||
To run this example, use the command `yarn start --run-examples`.
|
10
examples/expressions_explorer/kibana.json
Normal file
10
examples/expressions_explorer/kibana.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"id": "expressionsExplorer",
|
||||
"version": "0.0.1",
|
||||
"kibanaVersion": "kibana",
|
||||
"server": false,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["expressions", "inspector", "uiActions", "developerExamples"],
|
||||
"optionalPlugins": [],
|
||||
"requiredBundles": []
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createAction } from '../../../../src/plugins/ui_actions/public';
|
||||
|
||||
export const ACTION_NAVIGATE = 'ACTION_NAVIGATE';
|
||||
|
||||
export const createNavigateAction = () =>
|
||||
createAction({
|
||||
id: ACTION_NAVIGATE,
|
||||
type: ACTION_NAVIGATE,
|
||||
getDisplayName: () => 'Navigate',
|
||||
execute: async (event: any) => {
|
||||
window.location.href = event.href;
|
||||
},
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Trigger } from '../../../../src/plugins/ui_actions/public';
|
||||
|
||||
export const NAVIGATE_TRIGGER_ID = 'NAVIGATE_TRIGGER_ID';
|
||||
|
||||
export const navigateTrigger: Trigger = {
|
||||
id: NAVIGATE_TRIGGER_ID,
|
||||
};
|
102
examples/expressions_explorer/public/actions_and_expressions.tsx
Normal file
102
examples/expressions_explorer/public/actions_and_expressions.tsx
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
EuiFlexItem,
|
||||
EuiFlexGroup,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageContentBody,
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiPanel,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import {
|
||||
ExpressionsStart,
|
||||
ReactExpressionRenderer,
|
||||
ExpressionsInspectorAdapter,
|
||||
} from '../../../src/plugins/expressions/public';
|
||||
import { ExpressionEditor } from './editor/expression_editor';
|
||||
import { UiActionsStart } from '../../../src/plugins/ui_actions/public';
|
||||
import { NAVIGATE_TRIGGER_ID } from './actions/navigate_trigger';
|
||||
|
||||
interface Props {
|
||||
expressions: ExpressionsStart;
|
||||
actions: UiActionsStart;
|
||||
}
|
||||
|
||||
export function ActionsExpressionsExample({ expressions, actions }: Props) {
|
||||
const [expression, updateExpression] = useState(
|
||||
'button name="click me" href="http://www.google.com"'
|
||||
);
|
||||
|
||||
const expressionChanged = (value: string) => {
|
||||
updateExpression(value);
|
||||
};
|
||||
|
||||
const inspectorAdapters = {
|
||||
expression: new ExpressionsInspectorAdapter(),
|
||||
};
|
||||
|
||||
const handleEvents = (event: any) => {
|
||||
if (event.id !== 'NAVIGATE') return;
|
||||
// enrich event context with some extra data
|
||||
event.baseUrl = 'http://www.google.com';
|
||||
|
||||
actions.executeTriggerActions(NAVIGATE_TRIGGER_ID, event.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiPageBody>
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle size="l">
|
||||
<h1>Actions from expression renderers</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentBody>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiText>
|
||||
Here you can play with sample `button` which takes a url as configuration and
|
||||
displays a button which emits custom BUTTON_CLICK trigger to which we have attached
|
||||
a custom action which performs the navigation.
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiFlexGroup gutterSize="l">
|
||||
<EuiFlexItem>
|
||||
<EuiPanel data-test-subj="expressionEditor" paddingSize="none" role="figure">
|
||||
<ExpressionEditor value={expression} onChange={expressionChanged} />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiPanel paddingSize="none" role="figure">
|
||||
<ReactExpressionRenderer
|
||||
expression={expression}
|
||||
debug={true}
|
||||
inspectorAdapters={inspectorAdapters}
|
||||
onEvent={handleEvents}
|
||||
renderError={(message: any) => {
|
||||
return <div>{message}</div>;
|
||||
}}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
);
|
||||
}
|
78
examples/expressions_explorer/public/app.tsx
Normal file
78
examples/expressions_explorer/public/app.tsx
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {
|
||||
EuiPage,
|
||||
EuiPageHeader,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageContentBody,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
import { AppMountParameters } from '../../../src/core/public';
|
||||
import { ExpressionsStart } from '../../../src/plugins/expressions/public';
|
||||
import { Start as InspectorStart } from '../../../src/plugins/inspector/public';
|
||||
import { RunExpressionsExample } from './run_expressions';
|
||||
import { RenderExpressionsExample } from './render_expressions';
|
||||
import { ActionsExpressionsExample } from './actions_and_expressions';
|
||||
import { UiActionsStart } from '../../../src/plugins/ui_actions/public';
|
||||
|
||||
interface Props {
|
||||
expressions: ExpressionsStart;
|
||||
inspector: InspectorStart;
|
||||
actions: UiActionsStart;
|
||||
}
|
||||
|
||||
const ExpressionsExplorer = ({ expressions, inspector, actions }: Props) => {
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageHeader>Expressions Explorer</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentBody>
|
||||
<EuiText>
|
||||
<p>
|
||||
There are a couple of ways to run the expressions. Below some of the options are
|
||||
demonstrated. You can read more about it{' '}
|
||||
<EuiLink
|
||||
href={
|
||||
'https://github.com/elastic/kibana/blob/master/src/plugins/expressions/README.asciidoc'
|
||||
}
|
||||
>
|
||||
here
|
||||
</EuiLink>
|
||||
</p>
|
||||
</EuiText>
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<RunExpressionsExample expressions={expressions} inspector={inspector} />
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<RenderExpressionsExample expressions={expressions} inspector={inspector} />
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<ActionsExpressionsExample expressions={expressions} actions={actions} />
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
};
|
||||
|
||||
export const renderApp = (props: Props, { element }: AppMountParameters) => {
|
||||
ReactDOM.render(<ExpressionsExplorer {...props} />, element);
|
||||
|
||||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiCodeEditor } from '@elastic/eui';
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
export function ExpressionEditor({ value, onChange }: Props) {
|
||||
return (
|
||||
<EuiCodeEditor
|
||||
mode="javascript"
|
||||
theme="github"
|
||||
width="100%"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
setOptions={{
|
||||
fontSize: '14px',
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
}}
|
||||
onBlur={() => {}}
|
||||
aria-label="Code Editor"
|
||||
/>
|
||||
);
|
||||
}
|
50
examples/expressions_explorer/public/functions/button.ts
Normal file
50
examples/expressions_explorer/public/functions/button.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExpressionFunctionDefinition } from '../../../../src/plugins/expressions/common';
|
||||
|
||||
interface Arguments {
|
||||
href: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type ExpressionFunctionButton = ExpressionFunctionDefinition<
|
||||
'button',
|
||||
unknown,
|
||||
Arguments,
|
||||
unknown
|
||||
>;
|
||||
|
||||
export const buttonFn: ExpressionFunctionButton = {
|
||||
name: 'button',
|
||||
args: {
|
||||
href: {
|
||||
help: i18n.translate('expressions.functions.font.args.href', {
|
||||
defaultMessage: 'Link to which to navigate',
|
||||
}),
|
||||
types: ['string'],
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
help: i18n.translate('expressions.functions.font.args.name', {
|
||||
defaultMessage: 'Name of the button',
|
||||
}),
|
||||
types: ['string'],
|
||||
default: 'button',
|
||||
},
|
||||
},
|
||||
help: 'Configures the button',
|
||||
fn: (input: unknown, args: Arguments) => {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'button',
|
||||
value: args,
|
||||
};
|
||||
},
|
||||
};
|
11
examples/expressions_explorer/public/index.ts
Normal file
11
examples/expressions_explorer/public/index.ts
Normal 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
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ExpressionsExplorerPlugin } from './plugin';
|
||||
|
||||
export const plugin = () => new ExpressionsExplorerPlugin();
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { EuiTreeView, EuiDescriptionList, EuiCodeBlock, EuiText, EuiSpacer } from '@elastic/eui';
|
||||
|
||||
interface Props {
|
||||
ast: any;
|
||||
}
|
||||
|
||||
const decorateAst = (ast: any, nodeClicked: any) => {
|
||||
return ast.chain.map((link: any) => {
|
||||
return {
|
||||
id: link.function + Math.random(),
|
||||
label: link.function,
|
||||
callback: () => {
|
||||
nodeClicked(link.debug);
|
||||
},
|
||||
children: Object.keys(link.arguments).reduce((result: any, key: string) => {
|
||||
if (typeof link.arguments[key] === 'object') {
|
||||
// result[key] = decorateAst(link.arguments[key]);
|
||||
}
|
||||
return result;
|
||||
}, []),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const prepareNode = (key: string, value: any) => {
|
||||
if (key === 'args') {
|
||||
return (
|
||||
<EuiCodeBlock language="json" fontSize="m" paddingSize="m" isCopyable>
|
||||
{JSON.stringify(value, null, '\t')}
|
||||
</EuiCodeBlock>
|
||||
);
|
||||
} else if (key === 'output' || key === 'input') {
|
||||
return (
|
||||
<EuiCodeBlock language="json" fontSize="m" paddingSize="m" isCopyable>
|
||||
{JSON.stringify(value, null, '\t')}
|
||||
</EuiCodeBlock>
|
||||
);
|
||||
} else if (key === 'success') {
|
||||
return value ? 'true' : 'false';
|
||||
} else return <span>{value}</span>;
|
||||
};
|
||||
|
||||
export function AstDebugView({ ast }: Props) {
|
||||
const [nodeInfo, setNodeInfo] = useState([] as any[]);
|
||||
const items = decorateAst(ast, (node: any) => {
|
||||
setNodeInfo(
|
||||
Object.keys(node).map((key) => ({
|
||||
title: key,
|
||||
description: prepareNode(key, node[key]),
|
||||
}))
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<EuiText>List of executed expression functions:</EuiText>
|
||||
<EuiTreeView
|
||||
items={items}
|
||||
display="compressed"
|
||||
expandByDefault
|
||||
showExpansionArrows
|
||||
aria-label="Document Outline"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiText>Details of selected function:</EuiText>
|
||||
<EuiDescriptionList type="column" listItems={nodeInfo} />
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { EuiEmptyPrompt } from '@elastic/eui';
|
||||
import { InspectorViewProps, Adapters } from '../../../../src/plugins/inspector/public';
|
||||
import { AstDebugView } from './ast_debug_view';
|
||||
|
||||
interface ExpressionsInspectorViewComponentState {
|
||||
ast: any;
|
||||
adapters: Adapters;
|
||||
}
|
||||
|
||||
class ExpressionsInspectorViewComponent extends Component<
|
||||
InspectorViewProps,
|
||||
ExpressionsInspectorViewComponentState
|
||||
> {
|
||||
static propTypes = {
|
||||
adapters: PropTypes.object.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
state = {} as ExpressionsInspectorViewComponentState;
|
||||
|
||||
static getDerivedStateFromProps(
|
||||
nextProps: Readonly<InspectorViewProps>,
|
||||
state: ExpressionsInspectorViewComponentState
|
||||
) {
|
||||
if (state && nextProps.adapters === state.adapters) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { ast } = nextProps.adapters.expression;
|
||||
|
||||
return {
|
||||
adapters: nextProps.adapters,
|
||||
ast,
|
||||
};
|
||||
}
|
||||
|
||||
onUpdateData = (ast: any) => {
|
||||
this.setState({
|
||||
ast,
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.props.adapters.expression!.on('change', this.onUpdateData);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.adapters.expression!.removeListener('change', this.onUpdateData);
|
||||
}
|
||||
|
||||
static renderNoData() {
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
title={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="data.inspector.table.noDataAvailableTitle"
|
||||
defaultMessage="No data available"
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
body={
|
||||
<React.Fragment>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="data.inspector.table.noDataAvailableDescription"
|
||||
defaultMessage="The element did not provide any data."
|
||||
/>
|
||||
</p>
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state.ast) {
|
||||
return ExpressionsInspectorViewComponent.renderNoData();
|
||||
}
|
||||
|
||||
return <AstDebugView ast={this.state.ast} />;
|
||||
}
|
||||
}
|
||||
|
||||
// default export required for React.Lazy
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ExpressionsInspectorViewComponent;
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { lazy } from 'react';
|
||||
|
||||
const ExpressionsInspectorViewComponent = lazy(() => import('./expressions_inspector_view'));
|
||||
|
||||
export const getExpressionsInspectorViewComponentWrapper = () => {
|
||||
return (props: any) => {
|
||||
return <ExpressionsInspectorViewComponent adapters={props.adapters} title={props.title} />;
|
||||
};
|
||||
};
|
25
examples/expressions_explorer/public/inspector/index.ts
Normal file
25
examples/expressions_explorer/public/inspector/index.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Adapters, InspectorViewDescription } from '../../../../src/plugins/inspector/public';
|
||||
import { getExpressionsInspectorViewComponentWrapper } from './expressions_inspector_view_wrapper';
|
||||
|
||||
export const getExpressionsInspectorViewDescription = (): InspectorViewDescription => ({
|
||||
title: i18n.translate('data.inspector.table.dataTitle', {
|
||||
defaultMessage: 'Expression',
|
||||
}),
|
||||
order: 100,
|
||||
help: i18n.translate('data.inspector.table..dataDescriptionTooltip', {
|
||||
defaultMessage: 'View the expression behind the visualization',
|
||||
}),
|
||||
shouldShow(adapters: Adapters) {
|
||||
return Boolean(adapters.expression);
|
||||
},
|
||||
component: getExpressionsInspectorViewComponentWrapper(),
|
||||
});
|
87
examples/expressions_explorer/public/plugin.tsx
Normal file
87
examples/expressions_explorer/public/plugin.tsx
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Plugin, CoreSetup, AppMountParameters, AppNavLinkStatus } from '../../../src/core/public';
|
||||
import { DeveloperExamplesSetup } from '../../developer_examples/public';
|
||||
import { ExpressionsSetup, ExpressionsStart } from '../../../src/plugins/expressions/public';
|
||||
import {
|
||||
Setup as InspectorSetup,
|
||||
Start as InspectorStart,
|
||||
} from '../../../src/plugins/inspector/public';
|
||||
import { getExpressionsInspectorViewDescription } from './inspector';
|
||||
import { UiActionsStart, UiActionsSetup } from '../../../src/plugins/ui_actions/public';
|
||||
import { NAVIGATE_TRIGGER_ID, navigateTrigger } from './actions/navigate_trigger';
|
||||
import { ACTION_NAVIGATE, createNavigateAction } from './actions/navigate_action';
|
||||
import { buttonRenderer } from './renderers/button';
|
||||
import { buttonFn } from './functions/button';
|
||||
|
||||
interface StartDeps {
|
||||
expressions: ExpressionsStart;
|
||||
inspector: InspectorStart;
|
||||
uiActions: UiActionsStart;
|
||||
}
|
||||
|
||||
interface SetupDeps {
|
||||
uiActions: UiActionsSetup;
|
||||
expressions: ExpressionsSetup;
|
||||
inspector: InspectorSetup;
|
||||
developerExamples: DeveloperExamplesSetup;
|
||||
}
|
||||
|
||||
export class ExpressionsExplorerPlugin implements Plugin<void, void, SetupDeps, StartDeps> {
|
||||
public setup(core: CoreSetup<StartDeps>, deps: SetupDeps) {
|
||||
// register custom inspector adapter & view
|
||||
deps.inspector.registerView(getExpressionsInspectorViewDescription());
|
||||
|
||||
// register custom actions
|
||||
deps.uiActions.registerTrigger(navigateTrigger);
|
||||
deps.uiActions.registerAction(createNavigateAction());
|
||||
deps.uiActions.attachAction(NAVIGATE_TRIGGER_ID, ACTION_NAVIGATE);
|
||||
|
||||
// register custom functions and renderers
|
||||
deps.expressions.registerRenderer(buttonRenderer);
|
||||
deps.expressions.registerFunction(buttonFn);
|
||||
|
||||
core.application.register({
|
||||
id: 'expressionsExplorer',
|
||||
title: 'Expressions Explorer',
|
||||
navLinkStatus: AppNavLinkStatus.hidden,
|
||||
async mount(params: AppMountParameters) {
|
||||
const [, depsStart] = await core.getStartServices();
|
||||
const { renderApp } = await import('./app');
|
||||
return renderApp(
|
||||
{
|
||||
expressions: depsStart.expressions,
|
||||
inspector: depsStart.inspector,
|
||||
actions: depsStart.uiActions,
|
||||
},
|
||||
params
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
deps.developerExamples.register({
|
||||
appId: 'expressionsExplorer',
|
||||
title: 'Expressions',
|
||||
description: `Expressions is a plugin that allows to execute Kibana expressions and render content using expression renderers. This example plugin showcases various usage scenarios.`,
|
||||
links: [
|
||||
{
|
||||
label: 'README',
|
||||
href: 'https://github.com/elastic/kibana/blob/master/src/plugins/expressions/README.md',
|
||||
iconType: 'logoGithub',
|
||||
size: 's',
|
||||
target: '_blank',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
public start() {}
|
||||
|
||||
public stop() {}
|
||||
}
|
99
examples/expressions_explorer/public/render_expressions.tsx
Normal file
99
examples/expressions_explorer/public/render_expressions.tsx
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
EuiFlexItem,
|
||||
EuiFlexGroup,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageContentBody,
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiPanel,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
EuiButton,
|
||||
} from '@elastic/eui';
|
||||
import {
|
||||
ExpressionsStart,
|
||||
ReactExpressionRenderer,
|
||||
ExpressionsInspectorAdapter,
|
||||
} from '../../../src/plugins/expressions/public';
|
||||
import { ExpressionEditor } from './editor/expression_editor';
|
||||
import { Start as InspectorStart } from '../../../src/plugins/inspector/public';
|
||||
|
||||
interface Props {
|
||||
expressions: ExpressionsStart;
|
||||
inspector: InspectorStart;
|
||||
}
|
||||
|
||||
export function RenderExpressionsExample({ expressions, inspector }: Props) {
|
||||
const [expression, updateExpression] = useState('markdown "## expressions explorer rendering"');
|
||||
|
||||
const expressionChanged = (value: string) => {
|
||||
updateExpression(value);
|
||||
};
|
||||
|
||||
const inspectorAdapters = {
|
||||
expression: new ExpressionsInspectorAdapter(),
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiPageBody>
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle size="l">
|
||||
<h1>Render expressions</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentBody>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiText>
|
||||
In the below editor you can enter your expression and render it. Using
|
||||
ReactExpressionRenderer component makes that very easy.
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
onClick={() => {
|
||||
inspector.open(inspectorAdapters);
|
||||
}}
|
||||
>
|
||||
Open Inspector
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiFlexGroup gutterSize="l">
|
||||
<EuiFlexItem>
|
||||
<EuiPanel data-test-subj="expressionEditor" paddingSize="none" role="figure">
|
||||
<ExpressionEditor value={expression} onChange={expressionChanged} />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiPanel data-test-subj="expressionRender" paddingSize="none" role="figure">
|
||||
<ReactExpressionRenderer
|
||||
expression={expression}
|
||||
debug={true}
|
||||
inspectorAdapters={inspectorAdapters}
|
||||
renderError={(message: any) => {
|
||||
return <div>{message}</div>;
|
||||
}}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
);
|
||||
}
|
40
examples/expressions_explorer/public/renderers/button.tsx
Normal file
40
examples/expressions_explorer/public/renderers/button.tsx
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { ExpressionRenderDefinition } from '../../../../src/plugins/expressions/common/expression_renderers';
|
||||
|
||||
export const buttonRenderer: ExpressionRenderDefinition<any> = {
|
||||
name: 'button',
|
||||
displayName: 'Button',
|
||||
reuseDomNode: true,
|
||||
render(domNode, config, handlers) {
|
||||
const buttonClick = () => {
|
||||
handlers.event({
|
||||
id: 'NAVIGATE',
|
||||
value: {
|
||||
href: config.href,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const renderDebug = () => (
|
||||
<div style={{ width: domNode.offsetWidth, height: domNode.offsetHeight }}>
|
||||
<EuiButton data-test-subj="testExpressionButton" onClick={buttonClick}>
|
||||
{config.name}
|
||||
</EuiButton>
|
||||
</div>
|
||||
);
|
||||
|
||||
ReactDOM.render(renderDebug(), domNode, () => handlers.done());
|
||||
|
||||
handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
|
||||
},
|
||||
};
|
118
examples/expressions_explorer/public/run_expressions.tsx
Normal file
118
examples/expressions_explorer/public/run_expressions.tsx
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
EuiCodeBlock,
|
||||
EuiFlexItem,
|
||||
EuiFlexGroup,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageContentBody,
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiPanel,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
EuiButton,
|
||||
} from '@elastic/eui';
|
||||
import {
|
||||
ExpressionsStart,
|
||||
ExpressionsInspectorAdapter,
|
||||
} from '../../../src/plugins/expressions/public';
|
||||
import { ExpressionEditor } from './editor/expression_editor';
|
||||
import { Start as InspectorStart } from '../../../src/plugins/inspector/public';
|
||||
|
||||
interface Props {
|
||||
expressions: ExpressionsStart;
|
||||
inspector: InspectorStart;
|
||||
}
|
||||
|
||||
export function RunExpressionsExample({ expressions, inspector }: Props) {
|
||||
const [expression, updateExpression] = useState('markdown "## expressions explorer"');
|
||||
const [result, updateResult] = useState({});
|
||||
|
||||
const expressionChanged = (value: string) => {
|
||||
updateExpression(value);
|
||||
};
|
||||
|
||||
const inspectorAdapters = useMemo(
|
||||
() => ({
|
||||
expression: new ExpressionsInspectorAdapter(),
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const runExpression = async () => {
|
||||
const execution = expressions.execute(expression, null, {
|
||||
debug: true,
|
||||
inspectorAdapters,
|
||||
});
|
||||
|
||||
const data: any = await execution.getData();
|
||||
updateResult(data);
|
||||
};
|
||||
|
||||
runExpression();
|
||||
}, [expression, expressions, inspectorAdapters]);
|
||||
|
||||
return (
|
||||
<EuiPageBody>
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle size="l">
|
||||
<h1>Run expressions</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentBody>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiText>
|
||||
In the below editor you can enter your expression and execute it. Using
|
||||
expressions.execute allows you to easily run the expression.
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
onClick={() => {
|
||||
inspector.open(inspectorAdapters);
|
||||
}}
|
||||
>
|
||||
Open Inspector
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiFlexGroup gutterSize="l">
|
||||
<EuiFlexItem>
|
||||
<EuiPanel data-test-subj="expressionEditor" paddingSize="none" role="figure">
|
||||
<ExpressionEditor value={expression} onChange={expressionChanged} />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiPanel paddingSize="none" role="figure">
|
||||
<EuiCodeBlock
|
||||
language="json"
|
||||
fontSize="m"
|
||||
paddingSize="m"
|
||||
isCopyable
|
||||
data-test-subj="expressionResult"
|
||||
>
|
||||
{JSON.stringify(result, null, '\t')}
|
||||
</EuiCodeBlock>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
);
|
||||
}
|
18
examples/expressions_explorer/tsconfig.json
Normal file
18
examples/expressions_explorer/tsconfig.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./target",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"index.ts",
|
||||
"public/**/*.ts",
|
||||
"public/**/*.tsx",
|
||||
"../../typings/**/*",
|
||||
],
|
||||
"exclude": [],
|
||||
"references": [
|
||||
{ "path": "../../src/core/tsconfig.json" },
|
||||
{ "path": "../../src/plugins/kibana_react/tsconfig.json" },
|
||||
]
|
||||
}
|
|
@ -29,6 +29,7 @@ import { getByAlias } from '../util/get_by_alias';
|
|||
import { ExecutionContract } from './execution_contract';
|
||||
import { ExpressionExecutionParams } from '../service';
|
||||
import { TablesAdapter } from '../util/tables_adapter';
|
||||
import { ExpressionsInspectorAdapter } from '../util/expressions_inspector_adapter';
|
||||
|
||||
/**
|
||||
* AbortController is not available in Node until v15, so we
|
||||
|
@ -63,6 +64,7 @@ export interface ExecutionParams {
|
|||
const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({
|
||||
requests: new RequestAdapter(),
|
||||
tables: new TablesAdapter(),
|
||||
expression: new ExpressionsInspectorAdapter(),
|
||||
});
|
||||
|
||||
export class Execution<
|
||||
|
@ -208,6 +210,9 @@ export class Execution<
|
|||
this.firstResultFuture.promise
|
||||
.then(
|
||||
(result) => {
|
||||
if (this.context.inspectorAdapters.expression) {
|
||||
this.context.inspectorAdapters.expression.logAST(this.state.get().ast);
|
||||
}
|
||||
this.state.transitions.setResult(result);
|
||||
},
|
||||
(error) => {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
export class ExpressionsInspectorAdapter extends EventEmitter {
|
||||
private _ast: any = {};
|
||||
|
||||
public logAST(ast: any): void {
|
||||
this._ast = ast;
|
||||
this.emit('change', this._ast);
|
||||
}
|
||||
|
||||
public get ast() {
|
||||
return this._ast;
|
||||
}
|
||||
}
|
|
@ -9,3 +9,4 @@
|
|||
export * from './create_error';
|
||||
export * from './get_by_alias';
|
||||
export * from './tables_adapter';
|
||||
export * from './expressions_inspector_adapter';
|
||||
|
|
|
@ -107,4 +107,5 @@ export {
|
|||
ExpressionsServiceSetup,
|
||||
ExpressionsServiceStart,
|
||||
TablesAdapter,
|
||||
ExpressionsInspectorAdapter,
|
||||
} from '../common';
|
||||
|
|
|
@ -551,6 +551,16 @@ export class ExpressionRenderHandler {
|
|||
update$: Observable<UpdateValue | null>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ExpressionsInspectorAdapter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export class ExpressionsInspectorAdapter extends EventEmitter {
|
||||
// (undocumented)
|
||||
get ast(): any;
|
||||
// (undocumented)
|
||||
logAST(ast: any): void;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "ExpressionsPublicPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
|
|
|
@ -19,6 +19,7 @@ export default async function ({ readConfigFile }) {
|
|||
require.resolve('./ui_actions'),
|
||||
require.resolve('./state_sync'),
|
||||
require.resolve('./routing'),
|
||||
require.resolve('./expressions_explorer'),
|
||||
],
|
||||
services: {
|
||||
...functionalConfig.get('services'),
|
||||
|
|
44
test/examples/expressions_explorer/expressions.ts
Normal file
44
test/examples/expressions_explorer/expressions.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
import { PluginFunctionalProviderContext } from 'test/plugin_functional/services';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ({ getService }: PluginFunctionalProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
const browser = getService('browser');
|
||||
|
||||
describe('', () => {
|
||||
it('runs expression', async () => {
|
||||
await retry.try(async () => {
|
||||
const text = await testSubjects.getVisibleText('expressionResult');
|
||||
expect(text).to.be(
|
||||
'{\n "type": "error",\n "error": {\n "message": "Function markdown could not be found.",\n "name": "fn not found"\n }\n}'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders expression', async () => {
|
||||
await retry.try(async () => {
|
||||
const text = await testSubjects.getVisibleText('expressionRender');
|
||||
expect(text).to.be('Function markdown could not be found.');
|
||||
});
|
||||
});
|
||||
|
||||
it('emits an action and navigates', async () => {
|
||||
await testSubjects.click('testExpressionButton');
|
||||
await retry.try(async () => {
|
||||
const text = await browser.getCurrentUrl();
|
||||
expect(text).to.be('https://www.google.com/?gws_rd=ssl');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
28
test/examples/expressions_explorer/index.ts
Normal file
28
test/examples/expressions_explorer/index.ts
Normal file
|
@ -0,0 +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
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { PluginFunctionalProviderContext } from 'test/plugin_functional/services';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ({
|
||||
getService,
|
||||
getPageObjects,
|
||||
loadTestFile,
|
||||
}: PluginFunctionalProviderContext) {
|
||||
const browser = getService('browser');
|
||||
const PageObjects = getPageObjects(['common', 'header']);
|
||||
|
||||
describe('expressions explorer', function () {
|
||||
before(async () => {
|
||||
await browser.setWindowSize(1300, 900);
|
||||
await PageObjects.common.navigateToApp('expressionsExplorer');
|
||||
});
|
||||
|
||||
loadTestFile(require.resolve('./expressions'));
|
||||
});
|
||||
}
|
|
@ -26,9 +26,11 @@ export const debug: RendererFactory<any> = () => ({
|
|||
|
||||
ReactDOM.render(renderDebug(), domNode, () => handlers.done());
|
||||
|
||||
handlers.onResize(() => {
|
||||
ReactDOM.render(renderDebug(), domNode, () => handlers.done());
|
||||
});
|
||||
if (handlers.onResize) {
|
||||
handlers.onResize(() => {
|
||||
ReactDOM.render(renderDebug(), domNode, () => handlers.done());
|
||||
});
|
||||
}
|
||||
|
||||
handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue