[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:
parent
25a3fcea52
commit
eef9ecefe0
27 changed files with 188 additions and 88 deletions
|
@ -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,
|
||||||
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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(),
|
||||||
|
|
|
@ -713,3 +713,6 @@ export const ActionForm = ({
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
export { ActionForm as default };
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -254,3 +254,6 @@ export const ConnectorEditFlyout = ({
|
||||||
</EuiFlyout>
|
</EuiFlyout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
export { ConnectorEditFlyout as default };
|
||||||
|
|
|
@ -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')));
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -201,3 +201,6 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => {
|
||||||
</EuiPortal>
|
</EuiPortal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
export { AlertEdit as default };
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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';
|
|
|
@ -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')));
|
|
@ -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'))
|
||||||
|
);
|
|
@ -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?:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue