onEvent prop for expression component (#64995)

* feat: 🎸 add onEvent prop to expression component

* feat: 🎸 add type safety to onEvent prop in expression component

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Vadim Dalecky 2020-05-04 10:29:29 +02:00 committed by GitHub
parent c995a333de
commit 4d19323150
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 5 deletions

View file

@ -37,7 +37,7 @@ export {
ReactExpressionRendererProps,
ReactExpressionRendererType,
} from './react_expression_renderer';
export { ExpressionRenderHandler } from './render';
export { ExpressionRenderHandler, ExpressionRendererEvent } from './render';
export {
AnyExpressionFunctionDefinition,
AnyExpressionTypeDefinition,

View file

@ -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<ExpressionRendererEvent>();
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(<ReactExpressionRenderer expression="" onEvent={onEvent} />);
expect(onEvent).toHaveBeenCalledTimes(0);
act(() => {
eventsSubject.next(event);
});
expect(onEvent).toHaveBeenCalledTimes(1);
expect(onEvent.mock.calls[0][0]).toBe(event);
});
});

View file

@ -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<ReactExpressionRendererProps>;
@ -60,6 +62,7 @@ export const ReactExpressionRenderer = ({
padding,
renderError,
expression,
onEvent,
...expressionLoaderOptions
}: ReactExpressionRendererProps) => {
const mountpoint: React.MutableRefObject<null | HTMLDivElement> = 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(

View file

@ -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<number>;
update$: Observable<UpdateValue | null>;
events$: Observable<Event>;
events$: Observable<ExpressionRendererEvent>;
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<Event>;
this.events$ = this.eventsSubject.asObservable() as Observable<ExpressionRendererEvent>;
this.onRenderError = onRenderError || defaultRenderErrorHandler;