[Canvas] Remove recompose and convert to Typescript expression component (#86969)
* Remove recompose from expression component * Fix type check * Fix expression not updating Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
1b6f737546
commit
19687765b1
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { FC, MutableRefObject } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
EuiPanel,
|
||||
|
@ -16,19 +16,26 @@ import {
|
|||
EuiLink,
|
||||
EuiPortal,
|
||||
} from '@elastic/eui';
|
||||
// @ts-expect-error
|
||||
import { Shortcuts } from 'react-shortcuts';
|
||||
import { ComponentStrings } from '../../../i18n';
|
||||
import { ExpressionInput } from '../expression_input';
|
||||
import { ToolTipShortcut } from '../tool_tip_shortcut';
|
||||
import { ExpressionFunction } from '../../../types';
|
||||
import { FormState } from './';
|
||||
|
||||
const { Expression: strings } = ComponentStrings;
|
||||
|
||||
const { useRef } = React;
|
||||
|
||||
const shortcut = (ref, cmd, callback) => (
|
||||
const shortcut = (
|
||||
ref: MutableRefObject<ExpressionInput | null>,
|
||||
cmd: string,
|
||||
callback: () => void
|
||||
) => (
|
||||
<Shortcuts
|
||||
name="EXPRESSION"
|
||||
handler={(command) => {
|
||||
handler={(command: string) => {
|
||||
const isInputActive = ref.current && ref.current.editor && ref.current.editor.hasTextFocus();
|
||||
if (isInputActive && command === cmd) {
|
||||
callback();
|
||||
|
@ -40,18 +47,28 @@ const shortcut = (ref, cmd, callback) => (
|
|||
/>
|
||||
);
|
||||
|
||||
export const Expression = ({
|
||||
interface Props {
|
||||
functionDefinitions: ExpressionFunction[];
|
||||
formState: FormState;
|
||||
updateValue: (expression?: string) => void;
|
||||
setExpression: (expression: string) => void;
|
||||
done: () => void;
|
||||
error?: string;
|
||||
isCompact: boolean;
|
||||
toggleCompactView: () => void;
|
||||
}
|
||||
|
||||
export const Expression: FC<Props> = ({
|
||||
functionDefinitions,
|
||||
formState,
|
||||
updateValue,
|
||||
setExpression,
|
||||
done,
|
||||
error,
|
||||
fontSize,
|
||||
isCompact,
|
||||
toggleCompactView,
|
||||
}) => {
|
||||
const refExpressionInput = useRef(null);
|
||||
const refExpressionInput = useRef<null | ExpressionInput>(null);
|
||||
|
||||
const handleRun = () => {
|
||||
setExpression(formState.expression);
|
||||
|
@ -78,7 +95,6 @@ export const Expression = ({
|
|||
|
||||
<ExpressionInput
|
||||
ref={refExpressionInput}
|
||||
fontSize={fontSize}
|
||||
isCompact={isCompact}
|
||||
functionDefinitions={functionDefinitions}
|
||||
error={error ? error : `\u00A0`}
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
compose,
|
||||
withState,
|
||||
withHandlers,
|
||||
lifecycle,
|
||||
withPropsOnChange,
|
||||
branch,
|
||||
renderComponent,
|
||||
} from 'recompose';
|
||||
import { fromExpression } from '@kbn/interpreter/common';
|
||||
import { withServices } from '../../services';
|
||||
import { getSelectedPage, getSelectedElement } from '../../state/selectors/workpad';
|
||||
import { setExpression, flushContext } from '../../state/actions/elements';
|
||||
import { ElementNotSelected } from './element_not_selected';
|
||||
import { Expression as Component } from './expression';
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
pageId: getSelectedPage(state),
|
||||
element: getSelectedElement(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setExpression: (elementId, pageId) => (expression) => {
|
||||
// destroy the context cache
|
||||
dispatch(flushContext(elementId));
|
||||
|
||||
// update the element's expression
|
||||
dispatch(setExpression(expression, elementId, pageId));
|
||||
},
|
||||
});
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
const { element, pageId } = stateProps;
|
||||
const allProps = { ...ownProps, ...stateProps, ...dispatchProps };
|
||||
|
||||
if (!element) {
|
||||
return allProps;
|
||||
}
|
||||
|
||||
const { expression } = element;
|
||||
|
||||
const functions = Object.values(allProps.services.expressions.getFunctions());
|
||||
|
||||
return {
|
||||
...allProps,
|
||||
expression,
|
||||
functionDefinitions: functions,
|
||||
setExpression: dispatchProps.setExpression(element.id, pageId),
|
||||
};
|
||||
};
|
||||
|
||||
const expressionLifecycle = lifecycle({
|
||||
componentDidUpdate({ expression }) {
|
||||
if (
|
||||
this.props.expression !== expression &&
|
||||
this.props.expression !== this.props.formState.expression
|
||||
) {
|
||||
this.props.setFormState({
|
||||
expression: this.props.expression,
|
||||
dirty: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const Expression = compose(
|
||||
withServices,
|
||||
connect(mapStateToProps, mapDispatchToProps, mergeProps),
|
||||
withState('formState', 'setFormState', ({ expression }) => ({
|
||||
expression,
|
||||
dirty: false,
|
||||
})),
|
||||
withState('isCompact', 'setCompact', true),
|
||||
withHandlers({
|
||||
toggleCompactView: ({ isCompact, setCompact }) => () => {
|
||||
setCompact(!isCompact);
|
||||
},
|
||||
updateValue: ({ setFormState }) => (expression) => {
|
||||
setFormState({
|
||||
expression,
|
||||
dirty: true,
|
||||
});
|
||||
},
|
||||
setExpression: ({ setExpression, setFormState }) => (exp) => {
|
||||
setFormState((prev) => ({
|
||||
...prev,
|
||||
dirty: false,
|
||||
}));
|
||||
setExpression(exp);
|
||||
},
|
||||
}),
|
||||
expressionLifecycle,
|
||||
withPropsOnChange(['formState'], ({ formState }) => ({
|
||||
error: (function () {
|
||||
try {
|
||||
// TODO: We should merge the advanced UI input and this into a single validated expression input.
|
||||
fromExpression(formState.expression);
|
||||
return null;
|
||||
} catch (e) {
|
||||
return e.message;
|
||||
}
|
||||
})(),
|
||||
})),
|
||||
branch((props) => !props.element, renderComponent(ElementNotSelected))
|
||||
)(Component);
|
124
x-pack/plugins/canvas/public/components/expression/index.tsx
Normal file
124
x-pack/plugins/canvas/public/components/expression/index.tsx
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC, useState, useCallback, useMemo, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { fromExpression } from '@kbn/interpreter/common';
|
||||
import { useServices } from '../../services';
|
||||
import { getSelectedPage, getSelectedElement } from '../../state/selectors/workpad';
|
||||
// @ts-expect-error
|
||||
import { setExpression, flushContext } from '../../state/actions/elements';
|
||||
// @ts-expect-error
|
||||
import { ElementNotSelected } from './element_not_selected';
|
||||
import { Expression as Component } from './expression';
|
||||
import { State, CanvasElement } from '../../../types';
|
||||
|
||||
interface ExpressionProps {
|
||||
done: () => void;
|
||||
}
|
||||
|
||||
interface ExpressionContainerProps extends ExpressionProps {
|
||||
element: CanvasElement;
|
||||
pageId: string;
|
||||
}
|
||||
|
||||
export interface FormState {
|
||||
dirty: boolean;
|
||||
expression: string;
|
||||
}
|
||||
|
||||
export const Expression: FC<ExpressionProps> = ({ done }) => {
|
||||
const { element, pageId } = useSelector((state: State) => ({
|
||||
pageId: getSelectedPage(state),
|
||||
element: getSelectedElement(state),
|
||||
}));
|
||||
|
||||
if (!element) {
|
||||
return <ElementNotSelected done={done} />;
|
||||
}
|
||||
|
||||
return <ExpressionContainer key={element.id} done={done} element={element} pageId={pageId} />;
|
||||
};
|
||||
|
||||
const ExpressionContainer: FC<ExpressionContainerProps> = ({ done, element, pageId }) => {
|
||||
const services = useServices();
|
||||
const dispatch = useDispatch();
|
||||
const [isCompact, setCompact] = useState<boolean>(true);
|
||||
const toggleCompactView = useCallback(() => {
|
||||
setCompact(!isCompact);
|
||||
}, [isCompact, setCompact]);
|
||||
|
||||
const dispatchSetExpression = useCallback(
|
||||
(expression: string) => {
|
||||
// destroy the context cache
|
||||
dispatch(flushContext(element.id));
|
||||
|
||||
// update the element's expression
|
||||
dispatch(setExpression(expression, element.id, pageId));
|
||||
},
|
||||
[dispatch, element, pageId]
|
||||
);
|
||||
|
||||
const [formState, setFormState] = useState<FormState>({
|
||||
dirty: false,
|
||||
expression: element.expression,
|
||||
});
|
||||
|
||||
const updateValue = useCallback(
|
||||
(expression: string = '') => {
|
||||
setFormState({
|
||||
expression,
|
||||
dirty: true,
|
||||
});
|
||||
},
|
||||
[setFormState]
|
||||
);
|
||||
|
||||
const onSetExpression = useCallback(
|
||||
(expression: string) => {
|
||||
setFormState({
|
||||
...formState,
|
||||
dirty: false,
|
||||
});
|
||||
dispatchSetExpression(expression);
|
||||
},
|
||||
[setFormState, dispatchSetExpression, formState]
|
||||
);
|
||||
|
||||
const currentExpression = formState.expression;
|
||||
|
||||
const error = useMemo(() => {
|
||||
try {
|
||||
// TODO: We should merge the advanced UI input and this into a single validated expression input.
|
||||
fromExpression(currentExpression);
|
||||
return null;
|
||||
} catch (e) {
|
||||
return e.message;
|
||||
}
|
||||
}, [currentExpression]);
|
||||
|
||||
useEffect(() => {
|
||||
if (element.expression !== formState.expression && !formState.dirty) {
|
||||
setFormState({
|
||||
dirty: false,
|
||||
expression: element.expression,
|
||||
});
|
||||
}
|
||||
}, [element, setFormState, formState]);
|
||||
|
||||
return (
|
||||
<Component
|
||||
done={done}
|
||||
isCompact={isCompact}
|
||||
functionDefinitions={Object.values(services.expressions.getFunctions())}
|
||||
formState={formState}
|
||||
setExpression={onSetExpression}
|
||||
toggleCompactView={toggleCompactView}
|
||||
updateValue={updateValue}
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -21,7 +21,6 @@ import {
|
|||
import { WorkpadManager } from '../workpad_manager';
|
||||
import { RouterContext } from '../router';
|
||||
import { PageManager } from '../page_manager';
|
||||
// @ts-expect-error untyped local
|
||||
import { Expression } from '../expression';
|
||||
import { Tray } from './tray';
|
||||
|
||||
|
|
Loading…
Reference in a new issue