mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Response Ops] [Rule Form] Add new flyout to rule form library, responsive design and illustration to rule form page (#206141)
## Summary Part of #195211 Adds components for the new rule form flyout, and duplicates some of its design elements as responsive design on the Rule Page. This PR makes use of CSS `@container` queries, which EUI doesn't yet support natively. I've opened https://github.com/elastic/eui/issues/8265 to get native EUI support for this functionality, but for now we can apply it through class names and SCSS. The reason we're using `@container` is so the Rule Form can be responsive regardless of whether it's bound by the window size (in the case of the Rule Page) or a container element on a larger screen (in the Rule Flyout). When responsive design just relies on `@media screen` queries, we can have a situation where we're trying to render the rule form in a 500px wide flyout, but because the window is 1920px wide, it still tries to apply wide screen styling. `@container` instead responds to the width of an enclosing element, which can either be the body of the Rule Page, or the width of the Rule Flyout. ### Non-User Facing Changes - Adds the new rule flyout to `@kbn/response-ops-rule-form`. ***It is not yet actually user-facing anywhere in the application, this will be done in a second PR.*** <details> <summary><h4>Screenshots</h4></summary> <img width="508" alt="Screenshot 2025-01-08 at 4 29 55 PM" src="https://github.com/user-attachments/assets/7f03cd3a-5f37-4ac2-9992-ca4951664770" /> <img width="502" alt="Screenshot 2025-01-08 at 4 29 59 PM" src="https://github.com/user-attachments/assets/c0620fc2-83db-4603-90b7-1282e5b0e6ab" /> <img width="507" alt="Screenshot 2025-01-08 at 4 30 03 PM" src="https://github.com/user-attachments/assets/8440d551-46af-49e0-9c92-22d6b3ba1866" /> <img width="507" alt="Screenshot 2025-01-08 at 4 30 32 PM" src="https://github.com/user-attachments/assets/cf7493a7-6e4a-4e55-8027-89e9b36012fc" /> </details> ### User-Facing Changes These changes were added to the existing full page rule form to minimize the amount of code differences between the flyout and the full page - Adds some responsive styling to the rule form page to make it look more similar to the flyout when the browser window is narrow <details> <summary>Screenshot</summary> <img width="783" alt="Screenshot 2025-01-08 at 4 31 50 PM" src="https://github.com/user-attachments/assets/a3532b92-9f22-4e88-bcc3-e408fc53e64c" /> </details> - Adds the new illustrated "Add an action" empty prompt from the flyout designs to the existing rule form page <details> <summary>Screenshot</summary> <img width="1299" alt="Screenshot 2025-01-08 at 5 00 55 PM" src="https://github.com/user-attachments/assets/c4acd50d-9268-4874-b650-ecba532f3e9c" /> </details> ### Testing To test the new flyout, edit `packages/response-ops/rule_form/src/create_rule_form.tsx` and `packages/response-ops/rule_form/src/edit_rule_form.tsx` so that they render `<RuleFlyout>` instead of `<RulePage>`. <details> <summary><strong>Use this diff block</strong></summary> ```diff diff --git a/packages/response-ops/rule_form/src/create_rule_form.tsx b/packages/response-ops/rule_form/src/create_rule_form.tsx index 2f5e0472dcd..564744b96ec 100644 --- a/packages/response-ops/rule_form/src/create_rule_form.tsx +++ b/packages/response-ops/rule_form/src/create_rule_form.tsx @@ -31,6 +31,7 @@ import { parseRuleCircuitBreakerErrorMessage, } from './utils'; import { RULE_CREATE_SUCCESS_TEXT, RULE_CREATE_ERROR_TEXT } from './translations'; +import { RuleFlyout } from './rule_flyout'; export interface CreateRuleFormProps { ruleTypeId: string; @@ -199,7 +200,7 @@ export const CreateRuleForm = (props: CreateRuleFormProps) => { }), }} > - <RulePage isEdit={false} isSaving={isSaving} onCancel={onCancel} onSave={onSave} /> + <RuleFlyout isEdit={false} isSaving={isSaving} onCancel={onCancel} onSave={onSave} /> </RuleFormStateProvider> </div> ); diff --git a/packages/response-ops/rule_form/src/edit_rule_form.tsx b/packages/response-ops/rule_form/src/edit_rule_form.tsx index 392447114ed..41aecd7245a 100644 --- a/packages/response-ops/rule_form/src/edit_rule_form.tsx +++ b/packages/response-ops/rule_form/src/edit_rule_form.tsx @@ -26,6 +26,7 @@ import { import { RULE_EDIT_ERROR_TEXT, RULE_EDIT_SUCCESS_TEXT } from './translations'; import { getAvailableRuleTypes, parseRuleCircuitBreakerErrorMessage } from './utils'; import { DEFAULT_VALID_CONSUMERS, getDefaultFormData } from './constants'; +import { RuleFlyout } from './rule_flyout'; export interface EditRuleFormProps { id: string; @@ -193,7 +194,7 @@ export const EditRuleForm = (props: EditRuleFormProps) => { showMustacheAutocompleteSwitch, }} > - <RulePage isEdit={true} isSaving={isSaving} onSave={onSave} onCancel={onCancel} /> + <RuleFlyout isEdit={true} isSaving={isSaving} onSave={onSave} onCancel={onCancel} /> </RuleFormStateProvider> </div> ); ``` </details> ### Still Todo 1. Replace the action connector modal with an in-flyout UI as called for in the [design spec](https://www.figma.com/design/zetHXnUP0YnDG4YmvPwRb8/Adapt-new-Rule-form-to-work-in-flyout) 2. Add the Show Request UI 3. Replace all instances of the v1 rule flyout with this new one (it's used heavily in solutions, not in Stack Management) ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
bc47ff03b5
commit
471f948207
16 changed files with 1818 additions and 39 deletions
|
@ -14,6 +14,8 @@ import {
|
|||
RULE_FORM_PAGE_RULE_DEFINITION_TITLE,
|
||||
RULE_FORM_PAGE_RULE_ACTIONS_TITLE,
|
||||
RULE_FORM_PAGE_RULE_DETAILS_TITLE,
|
||||
RULE_FORM_PAGE_RULE_DEFINITION_TITLE_SHORT,
|
||||
RULE_FORM_PAGE_RULE_DETAILS_TITLE_SHORT,
|
||||
} from '../translations';
|
||||
import { RuleFormData } from '../types';
|
||||
import { EuiSteps, EuiStepsHorizontal } from '@elastic/eui';
|
||||
|
@ -145,9 +147,9 @@ describe('useRuleFormHorizontalSteps', () => {
|
|||
|
||||
render(<TestComponent />);
|
||||
|
||||
expect(screen.getByText(RULE_FORM_PAGE_RULE_DEFINITION_TITLE)).toBeInTheDocument();
|
||||
expect(screen.getByText(RULE_FORM_PAGE_RULE_DEFINITION_TITLE_SHORT)).toBeInTheDocument();
|
||||
expect(screen.getByText(RULE_FORM_PAGE_RULE_ACTIONS_TITLE)).toBeInTheDocument();
|
||||
expect(screen.getByText(RULE_FORM_PAGE_RULE_DETAILS_TITLE)).toBeInTheDocument();
|
||||
expect(screen.getByText(RULE_FORM_PAGE_RULE_DETAILS_TITLE_SHORT)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('tracks current step successfully', async () => {
|
||||
|
|
|
@ -18,6 +18,8 @@ import {
|
|||
RULE_FORM_PAGE_RULE_ACTIONS_TITLE,
|
||||
RULE_FORM_PAGE_RULE_DEFINITION_TITLE,
|
||||
RULE_FORM_PAGE_RULE_DETAILS_TITLE,
|
||||
RULE_FORM_PAGE_RULE_DEFINITION_TITLE_SHORT,
|
||||
RULE_FORM_PAGE_RULE_DETAILS_TITLE_SHORT,
|
||||
} from '../translations';
|
||||
import { hasActionsError, hasActionsParamsErrors, hasParamsErrors } from '../validation';
|
||||
import { RuleFormStepId } from '../constants';
|
||||
|
@ -27,6 +29,7 @@ interface UseRuleFormStepsOptions {
|
|||
touchedSteps: Record<RuleFormStepId, boolean>;
|
||||
/* Used to track the current step in horizontal steps, not used for vertical steps */
|
||||
currentStep?: RuleFormStepId;
|
||||
shortTitles?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +72,11 @@ const getStepStatus = ({
|
|||
};
|
||||
|
||||
// Create a common hook for both horizontal and vertical steps
|
||||
const useCommonRuleFormSteps = ({ touchedSteps, currentStep }: UseRuleFormStepsOptions) => {
|
||||
const useCommonRuleFormSteps = ({
|
||||
touchedSteps,
|
||||
currentStep,
|
||||
shortTitles,
|
||||
}: UseRuleFormStepsOptions) => {
|
||||
const {
|
||||
plugins: { application },
|
||||
baseErrors = {},
|
||||
|
@ -132,7 +139,9 @@ const useCommonRuleFormSteps = ({ touchedSteps, currentStep }: UseRuleFormStepsO
|
|||
const steps = useMemo(
|
||||
() => ({
|
||||
[RuleFormStepId.DEFINITION]: {
|
||||
title: RULE_FORM_PAGE_RULE_DEFINITION_TITLE,
|
||||
title: shortTitles
|
||||
? RULE_FORM_PAGE_RULE_DEFINITION_TITLE_SHORT
|
||||
: RULE_FORM_PAGE_RULE_DEFINITION_TITLE,
|
||||
status: ruleDefinitionStatus,
|
||||
children: <RuleDefinition />,
|
||||
},
|
||||
|
@ -150,7 +159,9 @@ const useCommonRuleFormSteps = ({ touchedSteps, currentStep }: UseRuleFormStepsO
|
|||
}
|
||||
: null,
|
||||
[RuleFormStepId.DETAILS]: {
|
||||
title: RULE_FORM_PAGE_RULE_DETAILS_TITLE,
|
||||
title: shortTitles
|
||||
? RULE_FORM_PAGE_RULE_DETAILS_TITLE_SHORT
|
||||
: RULE_FORM_PAGE_RULE_DETAILS_TITLE,
|
||||
status: ruleDetailsStatus,
|
||||
children: (
|
||||
<>
|
||||
|
@ -161,7 +172,7 @@ const useCommonRuleFormSteps = ({ touchedSteps, currentStep }: UseRuleFormStepsO
|
|||
),
|
||||
},
|
||||
}),
|
||||
[ruleDefinitionStatus, canReadConnectors, actionsStatus, ruleDetailsStatus]
|
||||
[ruleDefinitionStatus, canReadConnectors, actionsStatus, ruleDetailsStatus, shortTitles]
|
||||
);
|
||||
|
||||
const stepOrder: RuleFormStepId[] = useMemo(
|
||||
|
@ -247,6 +258,7 @@ export const useRuleFormHorizontalSteps: () => RuleFormHorizontalSteps = () => {
|
|||
const { steps, stepOrder } = useCommonRuleFormSteps({
|
||||
touchedSteps,
|
||||
currentStep,
|
||||
shortTitles: true,
|
||||
});
|
||||
|
||||
// Determine current navigation position
|
||||
|
|
|
@ -7,22 +7,41 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { EuiButton, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiImage, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import { RuleSystemAction } from '@kbn/alerting-types';
|
||||
import { ActionConnector } from '@kbn/alerts-ui-shared';
|
||||
import { ADD_ACTION_TEXT } from '../translations';
|
||||
import { RuleActionsConnectorsModal } from './rule_actions_connectors_modal';
|
||||
import { useRuleFormDispatch, useRuleFormState } from '../hooks';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import useEffectOnce from 'react-use/lib/useEffectOnce';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { RuleAction, RuleFormParamsErrors } from '../common/types';
|
||||
import { DEFAULT_FREQUENCY, MULTI_CONSUMER_RULE_TYPE_IDS } from '../constants';
|
||||
import { useRuleFormDispatch, useRuleFormState } from '../hooks';
|
||||
import {
|
||||
ADD_ACTION_DESCRIPTION_TEXT,
|
||||
ADD_ACTION_HEADER,
|
||||
ADD_ACTION_OPTIONAL_TEXT,
|
||||
ADD_ACTION_TEXT,
|
||||
} from '../translations';
|
||||
import { getDefaultParams } from '../utils';
|
||||
import { RuleActionsConnectorsModal } from './rule_actions_connectors_modal';
|
||||
import { RuleActionsItem } from './rule_actions_item';
|
||||
import { RuleActionsSystemActionsItem } from './rule_actions_system_actions_item';
|
||||
import { getDefaultParams } from '../utils';
|
||||
|
||||
const useRuleActionsIllustration = () => {
|
||||
const [imageData, setImageData] = useState('');
|
||||
useEffectOnce(() => {
|
||||
const fetchImage = async () => {
|
||||
const image = await import('./rule_actions_illustration.svg');
|
||||
setImageData(image.default);
|
||||
};
|
||||
fetchImage();
|
||||
});
|
||||
return imageData;
|
||||
};
|
||||
|
||||
export const RuleActions = () => {
|
||||
const [isConnectorModalOpen, setIsConnectorModalOpen] = useState<boolean>(false);
|
||||
const ruleActionsIllustration = useRuleActionsIllustration();
|
||||
|
||||
const {
|
||||
formData: { actions, consumer },
|
||||
|
@ -92,6 +111,8 @@ export const RuleActions = () => {
|
|||
return selectedRuleType.producer;
|
||||
}, [consumer, multiConsumerSelection, selectedRuleType]);
|
||||
|
||||
const hasActions = actions.length > 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlexGroup data-test-subj="ruleActions" direction="column">
|
||||
|
@ -120,15 +141,49 @@ export const RuleActions = () => {
|
|||
);
|
||||
})}
|
||||
</EuiFlexGroup>
|
||||
{!hasActions && (
|
||||
<EuiFlexGroup justifyContent="center">
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
direction="column"
|
||||
gutterSize="m"
|
||||
style={{ maxWidth: 356 }}
|
||||
>
|
||||
<EuiImage
|
||||
alt="Rule actions illustration"
|
||||
width={198}
|
||||
height={180}
|
||||
url={ruleActionsIllustration}
|
||||
/>
|
||||
<EuiFlexItem>
|
||||
<EuiText textAlign="center">
|
||||
<h3>{ADD_ACTION_HEADER}</h3>
|
||||
</EuiText>
|
||||
<EuiText size="s" textAlign="center" color="subdued">
|
||||
{ADD_ACTION_OPTIONAL_TEXT}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText size="s" textAlign="center" color="subdued">
|
||||
{ADD_ACTION_DESCRIPTION_TEXT}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
<EuiSpacer />
|
||||
<EuiButton
|
||||
data-test-subj="ruleActionsAddActionButton"
|
||||
iconType="push"
|
||||
iconSide="left"
|
||||
onClick={onModalOpen}
|
||||
>
|
||||
{ADD_ACTION_TEXT}
|
||||
</EuiButton>
|
||||
<EuiFlexGroup justifyContent={!hasActions ? 'center' : 'flexStart'}>
|
||||
<EuiFlexItem grow={0}>
|
||||
<EuiButton
|
||||
data-test-subj="ruleActionsAddActionButton"
|
||||
iconType="push"
|
||||
iconSide="left"
|
||||
onClick={onModalOpen}
|
||||
>
|
||||
{ADD_ACTION_TEXT}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{isConnectorModalOpen && (
|
||||
<RuleActionsConnectorsModal onClose={onModalClose} onSelectConnector={onSelectConnector} />
|
||||
)}
|
||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 379 KiB |
|
@ -193,20 +193,20 @@ export const RuleDefinition = () => {
|
|||
return (
|
||||
<EuiSplitPanel.Outer hasBorder hasShadow={false} data-test-subj="ruleDefinition">
|
||||
<EuiSplitPanel.Inner color="subdued">
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
<EuiFlexGroup gutterSize="s" className="ruleDefinitionHeader">
|
||||
<EuiFlexItem grow={false} data-test-subj="ruleDefinitionHeaderRuleTypeName">
|
||||
<EuiText size="xs">
|
||||
<EuiText size="xs" className="ruleDefinitionHeaderRuleTypeName">
|
||||
<strong>{selectedRuleType.name}</strong>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false} data-test-subj="ruleDefinitionHeaderRuleTypeDescription">
|
||||
<EuiText size="xs">
|
||||
<EuiText size="xs" className="ruleDefinitionHeaderRuleTypeDescription">
|
||||
<p>{selectedRuleTypeModel.description}</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
{docsUrl && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="xs">
|
||||
<EuiText size="xs" className="ruleDefinitionHeaderDocsLink">
|
||||
<EuiLink
|
||||
href={docsUrl}
|
||||
target="_blank"
|
||||
|
|
|
@ -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
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export * from './rule_flyout';
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { RuleFlyout } from './rule_flyout';
|
||||
import {
|
||||
RULE_FORM_PAGE_RULE_DEFINITION_TITLE_SHORT,
|
||||
RULE_FORM_PAGE_RULE_ACTIONS_TITLE,
|
||||
RULE_FORM_PAGE_RULE_DETAILS_TITLE_SHORT,
|
||||
} from '../translations';
|
||||
import { RuleFormData } from '../types';
|
||||
|
||||
jest.mock('../rule_definition', () => ({
|
||||
RuleDefinition: () => <div />,
|
||||
}));
|
||||
|
||||
jest.mock('../rule_actions', () => ({
|
||||
RuleActions: () => <div />,
|
||||
}));
|
||||
|
||||
jest.mock('../rule_details', () => ({
|
||||
RuleDetails: () => <div />,
|
||||
}));
|
||||
|
||||
jest.mock('../hooks/use_rule_form_state', () => ({
|
||||
useRuleFormState: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../hooks/use_rule_form_dispatch', () => ({
|
||||
useRuleFormDispatch: jest.fn(),
|
||||
}));
|
||||
|
||||
const { useRuleFormState } = jest.requireMock('../hooks/use_rule_form_state');
|
||||
|
||||
const navigateToUrl = jest.fn();
|
||||
|
||||
const formDataMock: RuleFormData = {
|
||||
params: {
|
||||
aggType: 'count',
|
||||
termSize: 5,
|
||||
thresholdComparator: '>',
|
||||
timeWindowSize: 5,
|
||||
timeWindowUnit: 'm',
|
||||
groupBy: 'all',
|
||||
threshold: [1000],
|
||||
index: ['.kibana'],
|
||||
timeField: 'alert.executionStatus.lastExecutionDate',
|
||||
},
|
||||
actions: [],
|
||||
consumer: 'stackAlerts',
|
||||
schedule: { interval: '1m' },
|
||||
tags: [],
|
||||
name: 'test',
|
||||
notifyWhen: 'onActionGroupChange',
|
||||
alertDelay: {
|
||||
active: 10,
|
||||
},
|
||||
};
|
||||
|
||||
const onCancel = jest.fn();
|
||||
|
||||
useRuleFormState.mockReturnValue({
|
||||
plugins: {
|
||||
application: {
|
||||
navigateToUrl,
|
||||
capabilities: {
|
||||
actions: {
|
||||
show: true,
|
||||
save: true,
|
||||
execute: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
baseErrors: {},
|
||||
paramsErrors: {},
|
||||
multiConsumerSelection: 'logs',
|
||||
formData: formDataMock,
|
||||
connectors: [],
|
||||
connectorTypes: [],
|
||||
aadTemplateFields: [],
|
||||
});
|
||||
|
||||
const onSave = jest.fn();
|
||||
|
||||
describe('ruleFlyout', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('renders correctly', () => {
|
||||
render(<RuleFlyout onCancel={onCancel} onSave={onSave} />);
|
||||
|
||||
expect(screen.getByText(RULE_FORM_PAGE_RULE_DEFINITION_TITLE_SHORT)).toBeInTheDocument();
|
||||
expect(screen.getByText(RULE_FORM_PAGE_RULE_ACTIONS_TITLE)).toBeInTheDocument();
|
||||
expect(screen.getByText(RULE_FORM_PAGE_RULE_DETAILS_TITLE_SHORT)).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByTestId('ruleFlyoutFooterCancelButton')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('ruleFlyoutFooterNextStepButton')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should navigate back and forth through steps correctly', async () => {
|
||||
render(<RuleFlyout onCancel={onCancel} onSave={onSave} />);
|
||||
|
||||
fireEvent.click(screen.getByTestId('ruleFlyoutFooterNextStepButton'));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('ruleFlyoutFooterPreviousStepButton')).toBeInTheDocument()
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('ruleFlyoutFooterNextStepButton'));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('ruleFlyoutFooterSaveButton')).toBeInTheDocument()
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('ruleFlyoutFooterPreviousStepButton'));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('ruleFlyoutFooterNextStepButton')).toBeInTheDocument()
|
||||
);
|
||||
});
|
||||
|
||||
test('should call onSave when save button is pressed', async () => {
|
||||
render(<RuleFlyout onCancel={onCancel} onSave={onSave} />);
|
||||
|
||||
fireEvent.click(screen.getByTestId('ruleFlyoutFooterNextStepButton'));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('ruleFlyoutFooterPreviousStepButton')).toBeInTheDocument()
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('ruleFlyoutFooterNextStepButton'));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('ruleFlyoutFooterSaveButton')).toBeInTheDocument()
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('ruleFlyoutFooterSaveButton'));
|
||||
|
||||
expect(onSave).toHaveBeenCalledWith({
|
||||
...formDataMock,
|
||||
consumer: 'logs',
|
||||
});
|
||||
});
|
||||
|
||||
test('should call onCancel when the cancel button is clicked', () => {
|
||||
render(<RuleFlyout onCancel={onCancel} onSave={onSave} />);
|
||||
|
||||
fireEvent.click(screen.getByTestId('ruleFlyoutFooterCancelButton'));
|
||||
expect(onCancel).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { EuiFlyout, EuiPortal } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import type { RuleFormData } from '../types';
|
||||
import { RuleFlyoutBody } from './rule_flyout_body';
|
||||
|
||||
interface RuleFlyoutProps {
|
||||
isEdit?: boolean;
|
||||
isSaving?: boolean;
|
||||
onCancel?: () => void;
|
||||
onSave: (formData: RuleFormData) => void;
|
||||
}
|
||||
|
||||
// Wrapper component for the rule flyout. Currently only displays RuleFlyoutBody, but will be extended to conditionally
|
||||
// display the Show Request UI or the Action Connector UI. These UIs take over the entire flyout, so we need to swap out
|
||||
// their body elements entirely to avoid adding another EuiFlyout element to the DOM
|
||||
export const RuleFlyout = ({ onSave, isEdit, isSaving, onCancel = () => {} }: RuleFlyoutProps) => {
|
||||
return (
|
||||
<EuiPortal>
|
||||
<EuiFlyout
|
||||
ownFocus
|
||||
onClose={onCancel}
|
||||
aria-labelledby="flyoutTitle"
|
||||
size="m"
|
||||
maxWidth={500}
|
||||
className="ruleFormFlyout__container"
|
||||
>
|
||||
<RuleFlyoutBody onSave={onSave} onCancel={onCancel} isEdit={isEdit} isSaving={isSaving} />
|
||||
</EuiFlyout>
|
||||
</EuiPortal>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutHeader,
|
||||
EuiStepsHorizontal,
|
||||
EuiTitle,
|
||||
EuiSpacer,
|
||||
EuiCallOut,
|
||||
} from '@elastic/eui';
|
||||
import { checkActionFormActionTypeEnabled } from '@kbn/alerts-ui-shared';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useRuleFormHorizontalSteps, useRuleFormState } from '../hooks';
|
||||
import {
|
||||
RULE_FLYOUT_HEADER_CREATE_TITLE,
|
||||
RULE_FLYOUT_HEADER_EDIT_TITLE,
|
||||
DISABLED_ACTIONS_WARNING_TITLE,
|
||||
} from '../translations';
|
||||
import type { RuleFormData } from '../types';
|
||||
import { hasRuleErrors } from '../validation';
|
||||
import { RuleFlyoutCreateFooter } from './rule_flyout_create_footer';
|
||||
import { RuleFlyoutEditFooter } from './rule_flyout_edit_footer';
|
||||
import { RuleFlyoutEditTabs } from './rule_flyout_edit_tabs';
|
||||
|
||||
interface RuleFlyoutBodyProps {
|
||||
isEdit?: boolean;
|
||||
isSaving?: boolean;
|
||||
onCancel: () => void;
|
||||
onSave: (formData: RuleFormData) => void;
|
||||
}
|
||||
|
||||
export const RuleFlyoutBody = ({
|
||||
isEdit = false,
|
||||
isSaving = false,
|
||||
onCancel,
|
||||
onSave,
|
||||
}: RuleFlyoutBodyProps) => {
|
||||
const {
|
||||
formData,
|
||||
multiConsumerSelection,
|
||||
connectorTypes,
|
||||
connectors,
|
||||
baseErrors = {},
|
||||
paramsErrors = {},
|
||||
actionsErrors = {},
|
||||
actionsParamsErrors = {},
|
||||
} = useRuleFormState();
|
||||
|
||||
const hasErrors = useMemo(() => {
|
||||
const hasBrokenConnectors = formData.actions.some((action) => {
|
||||
return !connectors.find((connector) => connector.id === action.id);
|
||||
});
|
||||
|
||||
if (hasBrokenConnectors) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return hasRuleErrors({
|
||||
baseErrors,
|
||||
paramsErrors,
|
||||
actionsErrors,
|
||||
actionsParamsErrors,
|
||||
});
|
||||
}, [formData, connectors, baseErrors, paramsErrors, actionsErrors, actionsParamsErrors]);
|
||||
|
||||
const {
|
||||
steps,
|
||||
currentStepComponent,
|
||||
goToNextStep,
|
||||
goToPreviousStep,
|
||||
hasNextStep,
|
||||
hasPreviousStep,
|
||||
} = useRuleFormHorizontalSteps();
|
||||
|
||||
const { actions } = formData;
|
||||
|
||||
const onSaveInternal = useCallback(() => {
|
||||
onSave({
|
||||
...formData,
|
||||
...(multiConsumerSelection ? { consumer: multiConsumerSelection } : {}),
|
||||
});
|
||||
}, [onSave, formData, multiConsumerSelection]);
|
||||
|
||||
const hasActionsDisabled = useMemo(() => {
|
||||
const preconfiguredConnectors = connectors.filter((connector) => connector.isPreconfigured);
|
||||
return actions.some((action) => {
|
||||
const actionType = connectorTypes.find(({ id }) => id === action.actionTypeId);
|
||||
if (!actionType) {
|
||||
return false;
|
||||
}
|
||||
const checkEnabledResult = checkActionFormActionTypeEnabled(
|
||||
actionType,
|
||||
preconfiguredConnectors
|
||||
);
|
||||
return !actionType.enabled && !checkEnabledResult.isEnabled;
|
||||
});
|
||||
}, [actions, connectors, connectorTypes]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="s" data-test-subj={isEdit ? 'editRuleFlyoutTitle' : 'addRuleFlyoutTitle'}>
|
||||
<h3 id="flyoutTitle">
|
||||
{isEdit ? RULE_FLYOUT_HEADER_EDIT_TITLE : RULE_FLYOUT_HEADER_CREATE_TITLE}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
{isEdit && <RuleFlyoutEditTabs steps={steps} />}
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
{!isEdit && <EuiStepsHorizontal size="xs" steps={steps} />}
|
||||
{hasActionsDisabled && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
size="s"
|
||||
color="danger"
|
||||
iconType="error"
|
||||
data-test-subj="hasActionsDisabled"
|
||||
title={DISABLED_ACTIONS_WARNING_TITLE}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
{currentStepComponent}
|
||||
</EuiFlyoutBody>
|
||||
{isEdit ? (
|
||||
<RuleFlyoutEditFooter
|
||||
onCancel={onCancel}
|
||||
onSave={onSaveInternal}
|
||||
onShowRequest={() => {} /* TODO */}
|
||||
isSaving={isSaving}
|
||||
hasErrors={hasErrors}
|
||||
/>
|
||||
) : (
|
||||
<RuleFlyoutCreateFooter
|
||||
onCancel={onCancel}
|
||||
onSave={onSaveInternal}
|
||||
onShowRequest={() => {} /* TODO */}
|
||||
goToNextStep={goToNextStep}
|
||||
goToPreviousStep={goToPreviousStep}
|
||||
isSaving={isSaving}
|
||||
hasNextStep={hasNextStep}
|
||||
hasPreviousStep={hasPreviousStep}
|
||||
hasErrors={hasErrors}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyoutFooter,
|
||||
} from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import {
|
||||
RULE_FLYOUT_FOOTER_BACK_TEXT,
|
||||
RULE_FLYOUT_FOOTER_CANCEL_TEXT,
|
||||
RULE_FLYOUT_FOOTER_CREATE_TEXT,
|
||||
RULE_FLYOUT_FOOTER_NEXT_TEXT,
|
||||
RULE_PAGE_FOOTER_SHOW_REQUEST_TEXT,
|
||||
} from '../translations';
|
||||
|
||||
export interface RuleFlyoutCreateFooterProps {
|
||||
isSaving: boolean;
|
||||
hasErrors: boolean;
|
||||
onCancel: () => void;
|
||||
onSave: () => void;
|
||||
onShowRequest: () => void;
|
||||
hasNextStep: boolean;
|
||||
hasPreviousStep: boolean;
|
||||
goToNextStep: () => void;
|
||||
goToPreviousStep: () => void;
|
||||
}
|
||||
export const RuleFlyoutCreateFooter = ({
|
||||
onCancel,
|
||||
onSave,
|
||||
onShowRequest,
|
||||
hasErrors,
|
||||
isSaving,
|
||||
hasNextStep,
|
||||
hasPreviousStep,
|
||||
goToNextStep,
|
||||
goToPreviousStep,
|
||||
}: RuleFlyoutCreateFooterProps) => {
|
||||
return (
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
{hasPreviousStep ? (
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="ruleFlyoutFooterPreviousStepButton"
|
||||
onClick={goToPreviousStep}
|
||||
>
|
||||
{RULE_FLYOUT_FOOTER_BACK_TEXT}
|
||||
</EuiButtonEmpty>
|
||||
) : (
|
||||
<EuiButtonEmpty data-test-subj="ruleFlyoutFooterCancelButton" onClick={onCancel}>
|
||||
{RULE_FLYOUT_FOOTER_CANCEL_TEXT}
|
||||
</EuiButtonEmpty>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup justifyContent="flexEnd" gutterSize="m">
|
||||
{!hasNextStep && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="ruleFlyoutFooterShowRequestButton"
|
||||
isDisabled={isSaving || hasErrors}
|
||||
onClick={onShowRequest}
|
||||
>
|
||||
{RULE_PAGE_FOOTER_SHOW_REQUEST_TEXT}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem grow={false}>
|
||||
{hasNextStep ? (
|
||||
<EuiButton
|
||||
fill
|
||||
data-test-subj="ruleFlyoutFooterNextStepButton"
|
||||
onClick={goToNextStep}
|
||||
>
|
||||
{RULE_FLYOUT_FOOTER_NEXT_TEXT}
|
||||
</EuiButton>
|
||||
) : (
|
||||
<EuiButton
|
||||
fill
|
||||
data-test-subj="ruleFlyoutFooterSaveButton"
|
||||
type="submit"
|
||||
isDisabled={isSaving || hasErrors}
|
||||
isLoading={isSaving}
|
||||
onClick={onSave}
|
||||
>
|
||||
{RULE_FLYOUT_FOOTER_CREATE_TEXT}
|
||||
</EuiButton>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyoutFooter,
|
||||
} from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import {
|
||||
RULE_FLYOUT_FOOTER_CANCEL_TEXT,
|
||||
RULE_FLYOUT_FOOTER_SAVE_TEXT,
|
||||
RULE_PAGE_FOOTER_SHOW_REQUEST_TEXT,
|
||||
} from '../translations';
|
||||
|
||||
export interface RuleFlyoutEditFooterProps {
|
||||
isSaving: boolean;
|
||||
hasErrors: boolean;
|
||||
onCancel: () => void;
|
||||
onSave: () => void;
|
||||
onShowRequest: () => void;
|
||||
}
|
||||
export const RuleFlyoutEditFooter = ({
|
||||
onCancel,
|
||||
onSave,
|
||||
onShowRequest,
|
||||
hasErrors,
|
||||
isSaving,
|
||||
}: RuleFlyoutEditFooterProps) => {
|
||||
return (
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty data-test-subj="cancelSaveRuleButton" onClick={onCancel}>
|
||||
{RULE_FLYOUT_FOOTER_CANCEL_TEXT}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup justifyContent="flexEnd" gutterSize="m">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="ruleFlyoutFooterShowRequestButton"
|
||||
isDisabled={isSaving || hasErrors}
|
||||
onClick={onShowRequest}
|
||||
>
|
||||
{RULE_PAGE_FOOTER_SHOW_REQUEST_TEXT}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
data-test-subj="saveRuleButton"
|
||||
type="submit"
|
||||
isDisabled={isSaving || hasErrors}
|
||||
isLoading={isSaving}
|
||||
onClick={onSave}
|
||||
>
|
||||
{RULE_FLYOUT_FOOTER_SAVE_TEXT}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import { EuiTabs, EuiTab, useEuiPaddingSize } from '@elastic/eui';
|
||||
import { EuiStepHorizontalProps } from '@elastic/eui/src/components/steps/step_horizontal';
|
||||
|
||||
interface RuleFlyoutEditTabsProps {
|
||||
steps: Array<Omit<EuiStepHorizontalProps, 'step'>>;
|
||||
}
|
||||
|
||||
export const RuleFlyoutEditTabs = ({ steps }: RuleFlyoutEditTabsProps) => {
|
||||
const bottomMarginOffset = `-${useEuiPaddingSize('l')}`;
|
||||
|
||||
const tabs = useMemo(
|
||||
() =>
|
||||
steps.map((step, index) => {
|
||||
return (
|
||||
<EuiTab key={index} isSelected={step.status === 'current'} onClick={step.onClick}>
|
||||
{step.title}
|
||||
</EuiTab>
|
||||
);
|
||||
}),
|
||||
[steps]
|
||||
);
|
||||
return (
|
||||
<div style={{ marginBottom: bottomMarginOffset }}>
|
||||
<EuiTabs bottomBorder={false}>{tabs}</EuiTabs>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
.ruleForm__container {
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
.ruleFormFlyout__container {
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
@container (max-width: 768px) {
|
||||
.euiDescribedFormGroup {
|
||||
flex-direction: column;
|
||||
}
|
||||
.euiDescribedFormGroup > .euiFlexItem {
|
||||
width: 100%;
|
||||
}
|
||||
.ruleDefinitionHeader {
|
||||
flex-direction: column;
|
||||
gap: $euiSizeM;
|
||||
}
|
||||
.ruleDefinitionHeaderRuleTypeName {
|
||||
font-size: $euiFontSizeM;
|
||||
margin-bottom: $euiSizeXS;
|
||||
}
|
||||
.ruleDefinitionHeaderRuleTypeDescription, .ruleDefinitionHeaderDocsLink {
|
||||
font-size: $euiFontSizeS;
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ import {
|
|||
RULE_FORM_ROUTE_PARAMS_ERROR_TEXT,
|
||||
} from './translations';
|
||||
import { RuleFormPlugins } from './types';
|
||||
import './rule_form.scss';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
|
@ -114,5 +115,9 @@ export const RuleForm = (props: RuleFormProps) => {
|
|||
onSubmit,
|
||||
]);
|
||||
|
||||
return <QueryClientProvider client={queryClient}>{ruleFormComponent}</QueryClientProvider>;
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<div className="ruleForm__container">{ruleFormComponent}</div>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -233,6 +233,28 @@ export const ADD_ACTION_TEXT = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const ADD_ACTION_HEADER = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleActions.addActionHeader',
|
||||
{
|
||||
defaultMessage: 'Add an action',
|
||||
}
|
||||
);
|
||||
|
||||
export const ADD_ACTION_OPTIONAL_TEXT = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleActions.addActionOptionalText',
|
||||
{
|
||||
defaultMessage: 'Optional',
|
||||
}
|
||||
);
|
||||
|
||||
export const ADD_ACTION_DESCRIPTION_TEXT = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleActions.addActionDescriptionText',
|
||||
{
|
||||
defaultMessage:
|
||||
'Select a connector and configure the actions to be performed when an alert is triggered',
|
||||
}
|
||||
);
|
||||
|
||||
export const RULE_DETAILS_TITLE = i18n.translate('responseOpsRuleForm.ruleForm.ruleDetails.title', {
|
||||
defaultMessage: 'Rule name and tags',
|
||||
});
|
||||
|
@ -307,6 +329,55 @@ export const RULE_PAGE_FOOTER_SAVE_TEXT = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const RULE_FLYOUT_HEADER_CREATE_TITLE = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleFlyoutHeader.createTitle',
|
||||
{
|
||||
defaultMessage: 'Create rule',
|
||||
}
|
||||
);
|
||||
|
||||
export const RULE_FLYOUT_HEADER_EDIT_TITLE = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleFlyoutHeader.editTitle',
|
||||
{
|
||||
defaultMessage: 'Edit rule',
|
||||
}
|
||||
);
|
||||
|
||||
export const RULE_FLYOUT_FOOTER_CANCEL_TEXT = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleFlyoutFooter.cancelText',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
);
|
||||
|
||||
export const RULE_FLYOUT_FOOTER_BACK_TEXT = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleFlyoutFooter.backText',
|
||||
{
|
||||
defaultMessage: 'Back',
|
||||
}
|
||||
);
|
||||
|
||||
export const RULE_FLYOUT_FOOTER_NEXT_TEXT = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleFlyoutFooter.nextText',
|
||||
{
|
||||
defaultMessage: 'Next',
|
||||
}
|
||||
);
|
||||
|
||||
export const RULE_FLYOUT_FOOTER_CREATE_TEXT = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleFlyoutFooter.createText',
|
||||
{
|
||||
defaultMessage: 'Create rule',
|
||||
}
|
||||
);
|
||||
|
||||
export const RULE_FLYOUT_FOOTER_SAVE_TEXT = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleFlyoutFooter.saveText',
|
||||
{
|
||||
defaultMessage: 'Save changes',
|
||||
}
|
||||
);
|
||||
|
||||
export const HEALTH_CHECK_ALERTS_ERROR_TITLE = i18n.translate(
|
||||
'responseOpsRuleForm.healthCheck.alertsErrorTitle',
|
||||
{
|
||||
|
@ -490,6 +561,13 @@ export const RULE_FORM_PAGE_RULE_DEFINITION_TITLE = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const RULE_FORM_PAGE_RULE_DEFINITION_TITLE_SHORT = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleDefinitionTitleShort',
|
||||
{
|
||||
defaultMessage: 'Definition',
|
||||
}
|
||||
);
|
||||
|
||||
export const RULE_FORM_PAGE_RULE_ACTIONS_TITLE = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleActionsTitle',
|
||||
{
|
||||
|
@ -518,6 +596,13 @@ export const RULE_FORM_PAGE_RULE_DETAILS_TITLE = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const RULE_FORM_PAGE_RULE_DETAILS_TITLE_SHORT = i18n.translate(
|
||||
'responseOpsRuleForm.ruleForm.ruleDetailsTitleShort',
|
||||
{
|
||||
defaultMessage: 'Details',
|
||||
}
|
||||
);
|
||||
|
||||
export const RULE_FORM_RETURN_TITLE = i18n.translate('responseOpsRuleForm.ruleForm.returnTitle', {
|
||||
defaultMessage: 'Return',
|
||||
});
|
||||
|
|
|
@ -2,19 +2,10 @@
|
|||
"extends": "../../../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react"
|
||||
]
|
||||
"types": ["jest", "node", "react", "@kbn/ambient-ui-types"]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["target/**/*"],
|
||||
"kbn_references": [
|
||||
"@kbn/alerting-types",
|
||||
"@kbn/i18n",
|
||||
|
@ -39,6 +30,6 @@
|
|||
"@kbn/kibana-react-plugin",
|
||||
"@kbn/core-i18n-browser",
|
||||
"@kbn/core-theme-browser",
|
||||
"@kbn/core-user-profile-browser",
|
||||
"@kbn/core-user-profile-browser"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue