From 4d19323150e9f09fab8fdd5e85cd81a2e494bbf6 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 4 May 2020 10:29:29 +0200 Subject: [PATCH] onEvent prop for expression component (#64995) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 add onEvent prop to expression component * feat: 🎸 add type safety to onEvent prop in expression component Co-authored-by: Elastic Machine --- src/plugins/expressions/public/index.ts | 2 +- .../public/react_expression_renderer.test.tsx | 41 +++++++++++++++++++ .../public/react_expression_renderer.tsx | 12 +++++- src/plugins/expressions/public/render.ts | 6 +-- 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts index c57db6029ec2e..6814764ee5faa 100644 --- a/src/plugins/expressions/public/index.ts +++ b/src/plugins/expressions/public/index.ts @@ -37,7 +37,7 @@ export { ReactExpressionRendererProps, ReactExpressionRendererType, } from './react_expression_renderer'; -export { ExpressionRenderHandler } from './render'; +export { ExpressionRenderHandler, ExpressionRendererEvent } from './render'; export { AnyExpressionFunctionDefinition, AnyExpressionTypeDefinition, diff --git a/src/plugins/expressions/public/react_expression_renderer.test.tsx b/src/plugins/expressions/public/react_expression_renderer.test.tsx index 65cc5fc1569cb..caa9bc68dffb8 100644 --- a/src/plugins/expressions/public/react_expression_renderer.test.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.test.tsx @@ -26,6 +26,7 @@ import { ExpressionLoader } from './loader'; import { mount } from 'enzyme'; import { EuiProgress } from '@elastic/eui'; import { RenderErrorHandlerFnType } from './types'; +import { ExpressionRendererEvent } from './render'; jest.mock('./loader', () => { return { @@ -135,4 +136,44 @@ describe('ExpressionRenderer', () => { expect(instance.find(EuiProgress)).toHaveLength(0); expect(instance.find('[data-test-subj="custom-error"]')).toHaveLength(0); }); + + it('should fire onEvent prop on every events$ observable emission in loader', () => { + const dataSubject = new Subject(); + const data$ = dataSubject.asObservable().pipe(share()); + const renderSubject = new Subject(); + const render$ = renderSubject.asObservable().pipe(share()); + const loadingSubject = new Subject(); + const loading$ = loadingSubject.asObservable().pipe(share()); + const eventsSubject = new Subject(); + const events$ = eventsSubject.asObservable().pipe(share()); + + const onEvent = jest.fn(); + const event: ExpressionRendererEvent = { + name: 'foo', + data: { + bar: 'baz', + }, + }; + + (ExpressionLoader as jest.Mock).mockImplementation(() => { + return { + render$, + data$, + loading$, + events$, + update: jest.fn(), + }; + }); + + mount(); + + expect(onEvent).toHaveBeenCalledTimes(0); + + act(() => { + eventsSubject.next(event); + }); + + expect(onEvent).toHaveBeenCalledTimes(1); + expect(onEvent.mock.calls[0][0]).toBe(event); + }); }); diff --git a/src/plugins/expressions/public/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer.tsx index 2c99f173c9f33..9e237d36ef627 100644 --- a/src/plugins/expressions/public/react_expression_renderer.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.tsx @@ -27,6 +27,7 @@ import theme from '@elastic/eui/dist/eui_theme_light.json'; import { IExpressionLoaderParams, RenderError } from './types'; import { ExpressionAstExpression, IInterpreterRenderHandlers } from '../common'; import { ExpressionLoader } from './loader'; +import { ExpressionRendererEvent } from './render'; // Accept all options of the runner as props except for the // dom element which is provided by the component itself @@ -36,6 +37,7 @@ export interface ReactExpressionRendererProps extends IExpressionLoaderParams { expression: string | ExpressionAstExpression; renderError?: (error?: string | null) => React.ReactElement | React.ReactElement[]; padding?: 'xs' | 's' | 'm' | 'l' | 'xl'; + onEvent?: (event: ExpressionRendererEvent) => void; } export type ReactExpressionRendererType = React.ComponentType; @@ -60,6 +62,7 @@ export const ReactExpressionRenderer = ({ padding, renderError, expression, + onEvent, ...expressionLoaderOptions }: ReactExpressionRendererProps) => { const mountpoint: React.MutableRefObject = useRef(null); @@ -99,6 +102,13 @@ export const ReactExpressionRenderer = ({ } : expressionLoaderOptions.onRenderError, }); + if (onEvent) { + subs.push( + expressionLoaderRef.current.events$.subscribe(event => { + onEvent(event); + }) + ); + } subs.push( expressionLoaderRef.current.loading$.subscribe(() => { hasHandledErrorRef.current = false; @@ -123,7 +133,7 @@ export const ReactExpressionRenderer = ({ errorRenderHandlerRef.current = null; }; - }, [hasCustomRenderErrorHandler]); + }, [hasCustomRenderErrorHandler, onEvent]); // Re-fetch data automatically when the inputs change useShallowCompareEffect( diff --git a/src/plugins/expressions/public/render.ts b/src/plugins/expressions/public/render.ts index 4aaf0da60fc60..c8a4022a01131 100644 --- a/src/plugins/expressions/public/render.ts +++ b/src/plugins/expressions/public/render.ts @@ -32,7 +32,7 @@ export interface ExpressionRenderHandlerParams { onRenderError: RenderErrorHandlerFnType; } -interface Event { +export interface ExpressionRendererEvent { name: string; data: any; } @@ -45,7 +45,7 @@ interface UpdateValue { export class ExpressionRenderHandler { render$: Observable; update$: Observable; - events$: Observable; + events$: Observable; private element: HTMLElement; private destroyFn?: any; @@ -63,7 +63,7 @@ export class ExpressionRenderHandler { this.element = element; this.eventsSubject = new Rx.Subject(); - this.events$ = this.eventsSubject.asObservable() as Observable; + this.events$ = this.eventsSubject.asObservable() as Observable; this.onRenderError = onRenderError || defaultRenderErrorHandler;