[Alerting] Adds lazy loading to AlertType and Flyout components (#65678)

This PR:
1. Adds support for lazy loading AlertType components and migrates the built-in IndexThreshold components to lazy load.
2. Adds lazy loading of the components contained in the flyout so that only the wrapper component is imported by other plugins and the internal components are loaded when needed.
This commit is contained in:
Gidi Meir Morris 2020-05-12 17:05:46 +01:00 committed by GitHub
parent 25a3fcea52
commit eef9ecefe0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 188 additions and 88 deletions

View file

@ -69,13 +69,13 @@ export function getAlertType(): AlertTypeModel {
id: '.index-threshold', id: '.index-threshold',
name: 'Index threshold', name: 'Index threshold',
iconClass: 'alert', iconClass: 'alert',
alertParamsExpression: IndexThresholdAlertTypeExpression, alertParamsExpression: lazy(() => import('./index_threshold_expression')),
validate: validateAlertType, validate: validateAlertType,
}; };
} }
``` ```
alertParamsExpression form represented as an expression using `EuiExpression` components: alertParamsExpression should be a lazy loaded React component extending an expression using `EuiExpression` components:
![Index Threshold Alert expression form](https://i.imgur.com/Ysk1ljY.png) ![Index Threshold Alert expression form](https://i.imgur.com/Ysk1ljY.png)
``` ```
@ -171,6 +171,7 @@ export const alertReducer = (state: any, action: AlertReducerAction) => {
``` ```
The Expression component should be lazy loaded which means it'll have to be the default export in `index_threshold_expression.ts`:
``` ```
export const IndexThresholdAlertTypeExpression: React.FunctionComponent<IndexThresholdProps> = ({ export const IndexThresholdAlertTypeExpression: React.FunctionComponent<IndexThresholdProps> = ({
@ -224,6 +225,9 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent<IndexThr
</Fragment> </Fragment>
); );
}; };
// Export as default in order to support lazy loading
export {IndexThresholdAlertTypeExpression as default};
``` ```
Index Threshold Alert form with validation: Index Threshold Alert form with validation:
@ -237,7 +241,9 @@ Each alert type should be defined as `AlertTypeModel` object with the these prop
name: string; name: string;
iconClass: string; iconClass: string;
validate: (alertParams: any) => ValidationResult; validate: (alertParams: any) => ValidationResult;
alertParamsExpression: React.FunctionComponent<any>; alertParamsExpression: React.LazyExoticComponent<
ComponentType<AlertTypeParamsExpressionProps<AlertParamsType, AlertsContextValue>>
>;
defaultActionMessage?: string; defaultActionMessage?: string;
``` ```
|Property|Description| |Property|Description|
@ -246,7 +252,7 @@ Each alert type should be defined as `AlertTypeModel` object with the these prop
|name|Name of the alert type that will be displayed on the select card in the UI.| |name|Name of the alert type that will be displayed on the select card in the UI.|
|iconClass|Icon of the alert type that will be displayed on the select card in the UI.| |iconClass|Icon of the alert type that will be displayed on the select card in the UI.|
|validate|Validation function for the alert params.| |validate|Validation function for the alert params.|
|alertParamsExpression|React functional component for building UI of the current alert type params.| |alertParamsExpression| A lazy loaded React component for building UI of the current alert type params.|
|defaultActionMessage|Optional property for providing default message for all added actions with `message` property.| |defaultActionMessage|Optional property for providing default message for all added actions with `message` property.|
IMPORTANT: The current UI supports a single action group only. IMPORTANT: The current UI supports a single action group only.
@ -295,8 +301,8 @@ Below is a list of steps that should be done to build and register a new alert t
1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [AlertTypeModel](https://github.com/elastic/kibana/blob/55b7905fb5265b73806006e7265739545d7521d0/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/types.ts#L83). Example: 1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [AlertTypeModel](https://github.com/elastic/kibana/blob/55b7905fb5265b73806006e7265739545d7521d0/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/types.ts#L83). Example:
``` ```
import { lazy } from 'react';
import { AlertTypeModel } from '../../../../types'; import { AlertTypeModel } from '../../../../types';
import { ExampleExpression } from './expression';
import { validateExampleAlertType } from './validation'; import { validateExampleAlertType } from './validation';
export function getAlertType(): AlertTypeModel { export function getAlertType(): AlertTypeModel {
@ -304,7 +310,7 @@ export function getAlertType(): AlertTypeModel {
id: 'example', id: 'example',
name: 'Example Alert Type', name: 'Example Alert Type',
iconClass: 'bell', iconClass: 'bell',
alertParamsExpression: ExampleExpression, alertParamsExpression: lazy(() => import('./expression')),
validate: validateExampleAlertType, validate: validateExampleAlertType,
defaultActionMessage: 'Alert [{{ctx.metadata.name}}] has exceeded the threshold', defaultActionMessage: 'Alert [{{ctx.metadata.name}}] has exceeded the threshold',
}; };
@ -361,6 +367,9 @@ export const ExampleExpression: React.FunctionComponent<ExampleProps> = ({
); );
}; };
// Export as default in order to support lazy loading
export {ExampleExpression as default};
``` ```
This alert type form becomes available, when the card of `Example Alert Type` is selected. This alert type form becomes available, when the card of `Example Alert Type` is selected.
Each expression word here is `EuiExpression` component and implements the basic aggregation, grouping and comparison methods. Each expression word here is `EuiExpression` component and implements the basic aggregation, grouping and comparison methods.
@ -1017,7 +1026,7 @@ Below is a list of steps that should be done to build and register a new action
1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [ActionTypeModel]: 1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [ActionTypeModel]:
``` ```
import React, { Fragment } from 'react'; import React, { Fragment, lazy } from 'react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { import {
ActionTypeModel, ActionTypeModel,

View file

@ -3,8 +3,8 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import React, { lazy, Suspense } from 'react'; import React, { lazy } from 'react';
import { Switch, Route, Redirect, HashRouter, RouteComponentProps } from 'react-router-dom'; import { Switch, Route, Redirect, HashRouter } from 'react-router-dom';
import { import {
ChromeStart, ChromeStart,
DocLinksStart, DocLinksStart,
@ -15,7 +15,6 @@ import {
ChromeBreadcrumb, ChromeBreadcrumb,
CoreStart, CoreStart,
} from 'kibana/public'; } from 'kibana/public';
import { EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { BASE_PATH, Section, routeToAlertDetails } from './constants'; import { BASE_PATH, Section, routeToAlertDetails } from './constants';
import { AppContextProvider, useAppDependencies } from './app_context'; import { AppContextProvider, useAppDependencies } from './app_context';
import { hasShowAlertsCapability } from './lib/capabilities'; import { hasShowAlertsCapability } from './lib/capabilities';
@ -24,6 +23,7 @@ import { TypeRegistry } from './type_registry';
import { ChartsPluginStart } from '../../../../../src/plugins/charts/public'; import { ChartsPluginStart } from '../../../../../src/plugins/charts/public';
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
import { PluginStartContract as AlertingStart } from '../../../alerting/public'; import { PluginStartContract as AlertingStart } from '../../../alerting/public';
import { suspendedComponentWithProps } from './lib/suspended_component_with_props';
const TriggersActionsUIHome = lazy(async () => import('./home')); const TriggersActionsUIHome = lazy(async () => import('./home'));
const AlertDetailsRoute = lazy(() => const AlertDetailsRoute = lazy(() =>
@ -68,30 +68,15 @@ export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) =
<Switch> <Switch>
<Route <Route
path={`${BASE_PATH}/:section(${sectionsRegex})`} path={`${BASE_PATH}/:section(${sectionsRegex})`}
component={suspendedRouteComponent(TriggersActionsUIHome)} component={suspendedComponentWithProps(TriggersActionsUIHome, 'xl')}
/> />
{canShowAlerts && ( {canShowAlerts && (
<Route path={routeToAlertDetails} component={suspendedRouteComponent(AlertDetailsRoute)} /> <Route
path={routeToAlertDetails}
component={suspendedComponentWithProps(AlertDetailsRoute, 'xl')}
/>
)} )}
<Redirect from={`${BASE_PATH}`} to={`${BASE_PATH}/${DEFAULT_SECTION}`} /> <Redirect from={`${BASE_PATH}`} to={`${BASE_PATH}/${DEFAULT_SECTION}`} />
</Switch> </Switch>
); );
}; };
function suspendedRouteComponent<T = unknown>(
RouteComponent: React.ComponentType<RouteComponentProps<T>>
) {
return (props: RouteComponentProps<T>) => (
<Suspense
fallback={
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="xl" />
</EuiFlexItem>
</EuiFlexGroup>
}
>
<RouteComponent {...props} />
</Suspense>
);
}

View file

@ -42,6 +42,7 @@ import {
} from '../../../../common'; } from '../../../../common';
import { builtInAggregationTypes } from '../../../../common/constants'; import { builtInAggregationTypes } from '../../../../common/constants';
import { IndexThresholdAlertParams } from './types'; import { IndexThresholdAlertParams } from './types';
import { AlertTypeParamsExpressionProps } from '../../../../types';
import { AlertsContextValue } from '../../../context/alerts_context'; import { AlertsContextValue } from '../../../context/alerts_context';
import './expression.scss'; import './expression.scss';
@ -66,23 +67,10 @@ const expressionFieldsWithValidation = [
'timeWindowSize', 'timeWindowSize',
]; ];
interface IndexThresholdProps { export const IndexThresholdAlertTypeExpression: React.FunctionComponent<AlertTypeParamsExpressionProps<
alertParams: IndexThresholdAlertParams; IndexThresholdAlertParams,
alertInterval: string; AlertsContextValue
setAlertParams: (property: string, value: any) => void; >> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, alertsContext }) => {
setAlertProperty: (key: string, value: any) => void;
errors: { [key: string]: string[] };
alertsContext: AlertsContextValue;
}
export const IndexThresholdAlertTypeExpression: React.FunctionComponent<IndexThresholdProps> = ({
alertParams,
alertInterval,
setAlertParams,
setAlertProperty,
errors,
alertsContext,
}) => {
const { const {
index, index,
timeField, timeField,
@ -476,3 +464,6 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent<IndexThr
</Fragment> </Fragment>
); );
}; };
// eslint-disable-next-line import/no-default-export
export { IndexThresholdAlertTypeExpression as default };

View file

@ -3,16 +3,19 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { AlertTypeModel } from '../../../../types'; import { lazy } from 'react';
import { IndexThresholdAlertTypeExpression } from './expression';
import { validateExpression } from './validation';
export function getAlertType(): AlertTypeModel { import { AlertTypeModel } from '../../../../types';
import { validateExpression } from './validation';
import { IndexThresholdAlertParams } from './types';
import { AlertsContextValue } from '../../../context/alerts_context';
export function getAlertType(): AlertTypeModel<IndexThresholdAlertParams, AlertsContextValue> {
return { return {
id: '.index-threshold', id: '.index-threshold',
name: 'Index threshold', name: 'Index threshold',
iconClass: 'alert', iconClass: 'alert',
alertParamsExpression: IndexThresholdAlertTypeExpression, alertParamsExpression: lazy(() => import('./expression')),
validate: validateExpression, validate: validateExpression,
}; };
} }

View file

@ -0,0 +1,27 @@
/*
* 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, { Suspense } from 'react';
import { EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiLoadingSpinnerSize } from '@elastic/eui/src/components/loading/loading_spinner';
export function suspendedComponentWithProps<T = unknown>(
ComponentToSuspend: React.ComponentType<T>,
size?: EuiLoadingSpinnerSize
) {
return (props: T) => (
<Suspense
fallback={
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size={size ?? 'm'} />
</EuiFlexItem>
</EuiFlexGroup>
}
>
<ComponentToSuspend {...props} />
</Suspense>
);
}

View file

@ -10,7 +10,7 @@ import { ReactWrapper } from 'enzyme';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { actionTypeRegistryMock } from '../../action_type_registry.mock';
import { ValidationResult, Alert, AlertAction } from '../../../types'; import { ValidationResult, Alert, AlertAction } from '../../../types';
import { ActionForm } from './action_form'; import ActionForm from './action_form';
jest.mock('../../lib/action_connector_api', () => ({ jest.mock('../../lib/action_connector_api', () => ({
loadAllActions: jest.fn(), loadAllActions: jest.fn(),
loadActionTypes: jest.fn(), loadActionTypes: jest.fn(),

View file

@ -713,3 +713,6 @@ export const ActionForm = ({
</Fragment> </Fragment>
); );
}; };
// eslint-disable-next-line import/no-default-export
export { ActionForm as default };

View file

@ -6,7 +6,7 @@
import * as React from 'react'; import * as React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { coreMock } from '../../../../../../../src/core/public/mocks'; import { coreMock } from '../../../../../../../src/core/public/mocks';
import { ConnectorAddFlyout } from './connector_add_flyout'; import ConnectorAddFlyout from './connector_add_flyout';
import { ActionsConnectorsContextProvider } from '../../context/actions_connectors_context'; import { ActionsConnectorsContextProvider } from '../../context/actions_connectors_context';
import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { actionTypeRegistryMock } from '../../action_type_registry.mock';
import { ValidationResult } from '../../../types'; import { ValidationResult } from '../../../types';

View file

@ -319,3 +319,6 @@ const UpgradeYourLicenseCallOut = ({ http }: { http: HttpSetup }) => (
</EuiFlexGroup> </EuiFlexGroup>
</EuiCallOut> </EuiCallOut>
); );
// eslint-disable-next-line import/no-default-export
export { ConnectorAddFlyout as default };

View file

@ -9,7 +9,7 @@ import { coreMock } from '../../../../../../../src/core/public/mocks';
import { ActionsConnectorsContextProvider } from '../../context/actions_connectors_context'; import { ActionsConnectorsContextProvider } from '../../context/actions_connectors_context';
import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { actionTypeRegistryMock } from '../../action_type_registry.mock';
import { ValidationResult } from '../../../types'; import { ValidationResult } from '../../../types';
import { ConnectorEditFlyout } from './connector_edit_flyout'; import ConnectorEditFlyout from './connector_edit_flyout';
import { AppContextProvider } from '../../app_context'; import { AppContextProvider } from '../../app_context';
const actionTypeRegistry = actionTypeRegistryMock.create(); const actionTypeRegistry = actionTypeRegistryMock.create();

View file

@ -254,3 +254,6 @@ export const ConnectorEditFlyout = ({
</EuiFlyout> </EuiFlyout>
); );
}; };
// eslint-disable-next-line import/no-default-export
export { ConnectorEditFlyout as default };

View file

@ -4,6 +4,13 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
export { ConnectorAddFlyout } from './connector_add_flyout'; import { lazy } from 'react';
export { ConnectorEditFlyout } from './connector_edit_flyout'; import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props';
export { ActionForm } from './action_form';
export const ConnectorAddFlyout = suspendedComponentWithProps(
lazy(() => import('./connector_add_flyout'))
);
export const ConnectorEditFlyout = suspendedComponentWithProps(
lazy(() => import('./connector_edit_flyout'))
);
export const ActionForm = suspendedComponentWithProps(lazy(() => import('./action_form')));

View file

@ -202,11 +202,12 @@ describe('actions_connectors_list component with items', () => {
expect(wrapper.find('[data-test-subj="preConfiguredTitleMessage"]')).toHaveLength(2); expect(wrapper.find('[data-test-subj="preConfiguredTitleMessage"]')).toHaveLength(2);
}); });
test('if select item for edit should render ConnectorEditFlyout', () => { test('if select item for edit should render ConnectorEditFlyout', async () => {
wrapper await wrapper
.find('[data-test-subj="edit1"]') .find('[data-test-subj="edit1"]')
.first() .first()
.simulate('click'); .simulate('click');
expect(wrapper.find('ConnectorEditFlyout')).toHaveLength(1); expect(wrapper.find('ConnectorEditFlyout')).toHaveLength(1);
}); });
}); });

View file

@ -22,7 +22,9 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import { useAppDependencies } from '../../../app_context'; import { useAppDependencies } from '../../../app_context';
import { loadAllActions, loadActionTypes, deleteActions } from '../../../lib/action_connector_api'; import { loadAllActions, loadActionTypes, deleteActions } from '../../../lib/action_connector_api';
import { ConnectorAddFlyout, ConnectorEditFlyout } from '../../action_connector_form'; import ConnectorAddFlyout from '../../action_connector_form/connector_add_flyout';
import ConnectorEditFlyout from '../../action_connector_form/connector_edit_flyout';
import { hasDeleteActionsCapability, hasSaveActionsCapability } from '../../../lib/capabilities'; import { hasDeleteActionsCapability, hasSaveActionsCapability } from '../../../lib/capabilities';
import { DeleteModalConfirmation } from '../../../components/delete_modal_confirmation'; import { DeleteModalConfirmation } from '../../../components/delete_modal_confirmation';
import { ActionsConnectorsContextProvider } from '../../../context/actions_connectors_context'; import { ActionsConnectorsContextProvider } from '../../../context/actions_connectors_context';

View file

@ -9,7 +9,7 @@ import { act } from 'react-dom/test-utils';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import { EuiFormLabel } from '@elastic/eui'; import { EuiFormLabel } from '@elastic/eui';
import { coreMock } from '../../../../../../../src/core/public/mocks'; import { coreMock } from '../../../../../../../src/core/public/mocks';
import { AlertAdd } from './alert_add'; import AlertAdd from './alert_add';
import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { actionTypeRegistryMock } from '../../action_type_registry.mock';
import { ValidationResult } from '../../../types'; import { ValidationResult } from '../../../types';
import { AlertsContextProvider, useAlertsContext } from '../../context/alerts_context'; import { AlertsContextProvider, useAlertsContext } from '../../context/alerts_context';

View file

@ -219,3 +219,6 @@ const parseErrors: (errors: IErrorObject) => boolean = errors =>
if (isObject(errorList)) return parseErrors(errorList as IErrorObject); if (isObject(errorList)) return parseErrors(errorList as IErrorObject);
return errorList.length >= 1; return errorList.length >= 1;
}); });
// eslint-disable-next-line import/no-default-export
export { AlertAdd as default };

View file

@ -12,7 +12,7 @@ import { ValidationResult } from '../../../types';
import { AlertsContextProvider } from '../../context/alerts_context'; import { AlertsContextProvider } from '../../context/alerts_context';
import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; import { alertTypeRegistryMock } from '../../alert_type_registry.mock';
import { ReactWrapper } from 'enzyme'; import { ReactWrapper } from 'enzyme';
import { AlertEdit } from './alert_edit'; import AlertEdit from './alert_edit';
import { AppContextProvider } from '../../app_context'; import { AppContextProvider } from '../../app_context';
const actionTypeRegistry = actionTypeRegistryMock.create(); const actionTypeRegistry = actionTypeRegistryMock.create();
const alertTypeRegistry = alertTypeRegistryMock.create(); const alertTypeRegistry = alertTypeRegistryMock.create();

View file

@ -201,3 +201,6 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => {
</EuiPortal> </EuiPortal>
); );
}; };
// eslint-disable-next-line import/no-default-export
export { AlertEdit as default };

View file

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import React, { Fragment, useState, useEffect } from 'react'; import React, { Fragment, useState, useEffect, Suspense } from 'react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
import { import {
@ -23,6 +23,7 @@ import {
EuiIconTip, EuiIconTip,
EuiButtonIcon, EuiButtonIcon,
EuiHorizontalRule, EuiHorizontalRule,
EuiLoadingSpinner,
} from '@elastic/eui'; } from '@elastic/eui';
import { some, filter, map, fold } from 'fp-ts/lib/Option'; import { some, filter, map, fold } from 'fp-ts/lib/Option';
import { pipe } from 'fp-ts/lib/pipeable'; import { pipe } from 'fp-ts/lib/pipeable';
@ -36,7 +37,7 @@ import { AlertReducerAction } from './alert_reducer';
import { AlertTypeModel, Alert, IErrorObject, AlertAction, AlertTypeIndex } from '../../../types'; import { AlertTypeModel, Alert, IErrorObject, AlertAction, AlertTypeIndex } from '../../../types';
import { getTimeOptions } from '../../../common/lib/get_time_options'; import { getTimeOptions } from '../../../common/lib/get_time_options';
import { useAlertsContext } from '../../context/alerts_context'; import { useAlertsContext } from '../../context/alerts_context';
import { ActionForm } from '../action_connector_form/action_form'; import { ActionForm } from '../action_connector_form';
export function validateBaseProperties(alertObject: Alert) { export function validateBaseProperties(alertObject: Alert) {
const validationResult = { errors: {} }; const validationResult = { errors: {} };
@ -222,14 +223,24 @@ export const AlertForm = ({
) : null} ) : null}
</EuiFlexGroup> </EuiFlexGroup>
{AlertParamsExpressionComponent ? ( {AlertParamsExpressionComponent ? (
<AlertParamsExpressionComponent <Suspense
alertParams={alert.params} fallback={
alertInterval={`${alertInterval ?? 1}${alertIntervalUnit}`} <EuiFlexGroup justifyContent="center">
errors={errors} <EuiFlexItem grow={false}>
setAlertParams={setAlertParams} <EuiLoadingSpinner size="m" />
setAlertProperty={setAlertProperty} </EuiFlexItem>
alertsContext={alertsContext} </EuiFlexGroup>
/> }
>
<AlertParamsExpressionComponent
alertParams={alert.params}
alertInterval={`${alertInterval ?? 1}${alertIntervalUnit}`}
errors={errors}
setAlertParams={setAlertParams}
setAlertProperty={setAlertProperty}
alertsContext={alertsContext}
/>
</Suspense>
) : null} ) : null}
{defaultActionGroupId ? ( {defaultActionGroupId ? (
<ActionForm <ActionForm

View file

@ -1,8 +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.
*/
export { AlertAdd } from './alert_add';
export { AlertEdit } from './alert_edit';

View file

@ -0,0 +1,10 @@
/*
* 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 { lazy } from 'react';
import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props';
export const AlertAdd = suspendedComponentWithProps(lazy(() => import('./alert_add')));
export const AlertEdit = suspendedComponentWithProps(lazy(() => import('./alert_edit')));

View file

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
import { lazy } from 'react';
import { suspendedComponentWithProps } from '../lib/suspended_component_with_props';
export const AlertAdd = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_add')));
export const AlertEdit = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_edit')));
export const ConnectorAddFlyout = suspendedComponentWithProps(
lazy(() => import('./action_connector_form/connector_add_flyout'))
);
export const ConnectorEditFlyout = suspendedComponentWithProps(
lazy(() => import('./action_connector_form/connector_edit_flyout'))
);
export const ActionForm = suspendedComponentWithProps(
lazy(() => import('./action_connector_form/action_form'))
);

View file

@ -20,11 +20,12 @@ import { getTimeUnitLabel } from '../lib/get_time_unit_label';
import { TIME_UNITS } from '../../application/constants'; import { TIME_UNITS } from '../../application/constants';
import { getTimeOptions } from '../lib/get_time_options'; import { getTimeOptions } from '../lib/get_time_options';
import { ClosablePopoverTitle } from './components'; import { ClosablePopoverTitle } from './components';
import { IErrorObject } from '../../types';
interface ForLastExpressionProps { interface ForLastExpressionProps {
timeWindowSize?: number; timeWindowSize?: number;
timeWindowUnit?: string; timeWindowUnit?: string;
errors: { [key: string]: string[] }; errors: IErrorObject;
onChangeWindowSize: (selectedWindowSize: number | undefined) => void; onChangeWindowSize: (selectedWindowSize: number | undefined) => void;
onChangeWindowUnit: (selectedWindowUnit: string) => void; onChangeWindowUnit: (selectedWindowUnit: string) => void;
popupPosition?: popupPosition?:

View file

@ -19,10 +19,11 @@ import {
import { builtInGroupByTypes } from '../constants'; import { builtInGroupByTypes } from '../constants';
import { GroupByType } from '../types'; import { GroupByType } from '../types';
import { ClosablePopoverTitle } from './components'; import { ClosablePopoverTitle } from './components';
import { IErrorObject } from '../../types';
interface GroupByExpressionProps { interface GroupByExpressionProps {
groupBy: string; groupBy: string;
errors: { [key: string]: string[] }; errors: IErrorObject;
onChangeSelectedTermSize: (selectedTermSize?: number) => void; onChangeSelectedTermSize: (selectedTermSize?: number) => void;
onChangeSelectedTermField: (selectedTermField?: string) => void; onChangeSelectedTermField: (selectedTermField?: string) => void;
onChangeSelectedGroupBy: (selectedGroupBy?: string) => void; onChangeSelectedGroupBy: (selectedGroupBy?: string) => void;

View file

@ -11,7 +11,13 @@ export { AlertsContextProvider } from './application/context/alerts_context';
export { ActionsConnectorsContextProvider } from './application/context/actions_connectors_context'; export { ActionsConnectorsContextProvider } from './application/context/actions_connectors_context';
export { AlertAdd } from './application/sections/alert_form'; export { AlertAdd } from './application/sections/alert_form';
export { ActionForm } from './application/sections/action_connector_form'; export { ActionForm } from './application/sections/action_connector_form';
export { AlertAction, Alert, AlertTypeModel, ActionType } from './types'; export {
AlertAction,
Alert,
AlertTypeModel,
AlertTypeParamsExpressionProps,
ActionType,
} from './types';
export { export {
ConnectorAddFlyout, ConnectorAddFlyout,
ConnectorEditFlyout, ConnectorEditFlyout,

View file

@ -110,12 +110,28 @@ export interface AlertTableItem extends Alert {
tagsText: string; tagsText: string;
} }
export interface AlertTypeModel { export interface AlertTypeParamsExpressionProps<
AlertParamsType = unknown,
AlertsContextValue = unknown
> {
alertParams: AlertParamsType;
alertInterval: string;
setAlertParams: (property: string, value: any) => void;
setAlertProperty: (key: string, value: any) => void;
errors: IErrorObject;
alertsContext: AlertsContextValue;
}
export interface AlertTypeModel<AlertParamsType = any, AlertsContextValue = any> {
id: string; id: string;
name: string | JSX.Element; name: string | JSX.Element;
iconClass: string; iconClass: string;
validate: (alertParams: any) => ValidationResult; validate: (alertParams: AlertParamsType) => ValidationResult;
alertParamsExpression: React.FunctionComponent<any>; alertParamsExpression:
| React.FunctionComponent<any>
| React.LazyExoticComponent<
ComponentType<AlertTypeParamsExpressionProps<AlertParamsType, AlertsContextValue>>
>;
defaultActionMessage?: string; defaultActionMessage?: string;
} }

View file

@ -63,7 +63,9 @@ export const initMonitorStatusAlertType: AlertTypeInitializer = ({
id: CLIENT_ALERT_TYPES.MONITOR_STATUS, id: CLIENT_ALERT_TYPES.MONITOR_STATUS,
name: <MonitorStatusTitle />, name: <MonitorStatusTitle />,
iconClass: 'uptimeApp', iconClass: 'uptimeApp',
alertParamsExpression: params => <AlertMonitorStatus {...params} autocomplete={autocomplete} />, alertParamsExpression: (params: any) => (
<AlertMonitorStatus {...params} autocomplete={autocomplete} />
),
validate, validate,
defaultActionMessage, defaultActionMessage,
}); });