Skip to content

Commit

Permalink
[8.x] [Response Ops] [Rule Form] Add Show Request and Add Action scre…
Browse files Browse the repository at this point in the history
…ens to flyout (#206154) (#207903)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Response Ops] [Rule Form] Add Show Request and Add Action screens to
flyout (#206154)](#206154)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Zacqary Adam
Xeper","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-01-22T18:53:08Z","message":"[Response
Ops] [Rule Form] Add Show Request and Add Action screens to flyout
(#206154)\n\n## Summary\r\n\r\nPart of #195211\r\n\r\n- Adds Show
Request screen to the new rule form
flyout\r\n\r\n<details>\r\n<summary>Screenshot</summary>\r\n<img
width=\"585\" alt=\"Screenshot 2025-01-10 at 1 30
15 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/72500b0d-d959-4d17-944e-a7dc0894fb98\"\r\n/>\r\n</details>\r\n\r\n-
Renders the action connectors UI within the flyout instead of
opening\r\na modal\r\n
\r\n<details>\r\n<summary>Screenshot</summary>\r\n<img width=\"505\"
alt=\"Screenshot 2025-01-10 at 1 28
38 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/b5b464c0-7359-43ab-bea1-93d2981a5794\"\r\n/>\r\n</details>\r\n\r\n-
Duplicates the dropdown filter design from the flyout UI within
the\r\naction connectors modal when displayed on a smaller
screen\r\n\r\n<details>\r\n<summary>Screenshot</summary>\r\n<img
width=\"809\" alt=\"Screenshot 2025-01-10 at 1 30
28 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/5ef28458-1b6d-4a29-961d-fbcc1640e706\"\r\n/>\r\n</details>\r\n\r\n###
Implementation notes\r\n\r\nIn order to get the action connectors UI to
render the same way in both\r\na modal and the flyout, without
duplicating a large amount of code, I\r\nhad to introduce a little bit
of complexity. Within the Rule Page, it's\r\nas simple as opening the UI
inside a modal, but the flyout cannot open a\r\nsecond flyout; it has to
know when and how to completely replace its own\r\ncontents.\r\n\r\n-
The bulk of the action connectors UI is now moved
to\r\n`<RuleActionsConnectorsBody>`. `<RuleActionsConnectorsModal>`
and\r\n`<RuleFlyoutSelectConnector>` act as wrappers for this
component.\r\n- The `<RuleActions>` step no longer handles rendering the
connector UI,\r\nbecause it's not at a high enough level to know if it's
in the\r\n`<RulePage>` or the `<RuleFlyout>`. Instead, it simply sends a
signal up\r\nthe context hierarchy to
`setIsConnectorsScreenVisible`.\r\n- A new context called
`RuleFormScreenContext` keeps track of\r\n`isConnectorsScreenVisible`, a
state for whether or not the action\r\nconnectors \"screen\" is open,
regardless of whether that screen is\r\ndisplayed in a modal or a
flyout.\r\n- The Rule Page uses `isConnectorsScreenVisible` to determine
whether to\r\nrender the modal. This works the same way as it used to,
but handled by\r\nthe `<RulePage>` instead of the `<RuleActions>`
component.\r\n- The Rule Flyout uses `isConnectorsScreenVisible` to
determine whether\r\nto continue to render `<RuleFlyoutBody>` or to
completely replace its\r\ncontents with
`<RuleFlyoutSelectConnector>`\r\n\r\nFor consistency, this PR also moves
the Show Request modal/flyout screen\r\ninto the same system.\r\n\r\n###
Testing\r\n\r\nTo test the new flyout,
edit\r\n`packages/response-ops/rule_form/src/create_rule_form.tsx`
and\r\n`packages/response-ops/rule_form/src/edit_rule_form.tsx` so that
they\r\nrender `<RuleFlyout>` instead of
`<RulePage>`.\r\n\r\n<details>\r\n<summary><strong>Use this diff
block</strong></summary>\r\n\r\n```diff\r\ndiff --git
a/packages/response-ops/rule_form/src/create_rule_form.tsx
b/packages/response-ops/rule_form/src/create_rule_form.tsx\r\nindex
2f5e0472dcd..564744b96ec 100644\r\n---
a/packages/response-ops/rule_form/src/create_rule_form.tsx\r\n+++
b/packages/response-ops/rule_form/src/create_rule_form.tsx\r\n@@ -31,6
+31,7 @@ import {\r\n parseRuleCircuitBreakerErrorMessage,\r\n } from
'./utils';\r\n import { RULE_CREATE_SUCCESS_TEXT, RULE_CREATE_ERROR_TEXT
} from './translations';\r\n+import { RuleFlyout } from
'./rule_flyout';\r\n \r\n export interface CreateRuleFormProps {\r\n
ruleTypeId: string;\r\n@@ -199,7 +200,7 @@ export const CreateRuleForm =
(props: CreateRuleFormProps) => {\r\n }),\r\n }}\r\n >\r\n- <RulePage
isEdit={false} isSaving={isSaving} onCancel={onCancel} onSave={onSave}
/>\r\n+ <RuleFlyout isEdit={false} isSaving={isSaving}
onCancel={onCancel} onSave={onSave} />\r\n </RuleFormStateProvider>\r\n
</div>\r\n );\r\ndiff --git
a/packages/response-ops/rule_form/src/edit_rule_form.tsx
b/packages/response-ops/rule_form/src/edit_rule_form.tsx\r\nindex
392447114ed..41aecd7245a 100644\r\n---
a/packages/response-ops/rule_form/src/edit_rule_form.tsx\r\n+++
b/packages/response-ops/rule_form/src/edit_rule_form.tsx\r\n@@ -26,6
+26,7 @@ import {\r\n import { RULE_EDIT_ERROR_TEXT,
RULE_EDIT_SUCCESS_TEXT } from './translations';\r\n import {
getAvailableRuleTypes, parseRuleCircuitBreakerErrorMessage } from
'./utils';\r\n import { DEFAULT_VALID_CONSUMERS, getDefaultFormData }
from './constants';\r\n+import { RuleFlyout } from './rule_flyout';\r\n
\r\n export interface EditRuleFormProps {\r\n id: string;\r\n@@ -193,7
+194,7 @@ export const EditRuleForm = (props: EditRuleFormProps) =>
{\r\n showMustacheAutocompleteSwitch,\r\n }}\r\n >\r\n- <RulePage
isEdit={true} isSaving={isSaving} onSave={onSave} onCancel={onCancel}
/>\r\n+ <RuleFlyout isEdit={true} isSaving={isSaving} onSave={onSave}
onCancel={onCancel} />\r\n </RuleFormStateProvider>\r\n </div>\r\n
);\r\n```\r\n\r\n</details>\r\n\r\n### Still Todo\r\n\r\n1. Replace all
instances of the v1 rule flyout with this new one (it's\r\nused heavily
in solutions, not in Stack Management)\r\n\r\n### Checklist\r\n\r\n- [x]
Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<[email protected]>","sha":"8004e3e70ad63b938d724eafd561533eeb225cd9","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:ResponseOps","v9.0.0","Feature:Alerting/RulesManagement","backport:version","v8.18.0"],"title":"[Response
Ops] [Rule Form] Add Show Request and Add Action screens to
flyout","number":206154,"url":"https://github.com/elastic/kibana/pull/206154","mergeCommit":{"message":"[Response
Ops] [Rule Form] Add Show Request and Add Action screens to flyout
(#206154)\n\n## Summary\r\n\r\nPart of #195211\r\n\r\n- Adds Show
Request screen to the new rule form
flyout\r\n\r\n<details>\r\n<summary>Screenshot</summary>\r\n<img
width=\"585\" alt=\"Screenshot 2025-01-10 at 1 30
15 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/72500b0d-d959-4d17-944e-a7dc0894fb98\"\r\n/>\r\n</details>\r\n\r\n-
Renders the action connectors UI within the flyout instead of
opening\r\na modal\r\n
\r\n<details>\r\n<summary>Screenshot</summary>\r\n<img width=\"505\"
alt=\"Screenshot 2025-01-10 at 1 28
38 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/b5b464c0-7359-43ab-bea1-93d2981a5794\"\r\n/>\r\n</details>\r\n\r\n-
Duplicates the dropdown filter design from the flyout UI within
the\r\naction connectors modal when displayed on a smaller
screen\r\n\r\n<details>\r\n<summary>Screenshot</summary>\r\n<img
width=\"809\" alt=\"Screenshot 2025-01-10 at 1 30
28 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/5ef28458-1b6d-4a29-961d-fbcc1640e706\"\r\n/>\r\n</details>\r\n\r\n###
Implementation notes\r\n\r\nIn order to get the action connectors UI to
render the same way in both\r\na modal and the flyout, without
duplicating a large amount of code, I\r\nhad to introduce a little bit
of complexity. Within the Rule Page, it's\r\nas simple as opening the UI
inside a modal, but the flyout cannot open a\r\nsecond flyout; it has to
know when and how to completely replace its own\r\ncontents.\r\n\r\n-
The bulk of the action connectors UI is now moved
to\r\n`<RuleActionsConnectorsBody>`. `<RuleActionsConnectorsModal>`
and\r\n`<RuleFlyoutSelectConnector>` act as wrappers for this
component.\r\n- The `<RuleActions>` step no longer handles rendering the
connector UI,\r\nbecause it's not at a high enough level to know if it's
in the\r\n`<RulePage>` or the `<RuleFlyout>`. Instead, it simply sends a
signal up\r\nthe context hierarchy to
`setIsConnectorsScreenVisible`.\r\n- A new context called
`RuleFormScreenContext` keeps track of\r\n`isConnectorsScreenVisible`, a
state for whether or not the action\r\nconnectors \"screen\" is open,
regardless of whether that screen is\r\ndisplayed in a modal or a
flyout.\r\n- The Rule Page uses `isConnectorsScreenVisible` to determine
whether to\r\nrender the modal. This works the same way as it used to,
but handled by\r\nthe `<RulePage>` instead of the `<RuleActions>`
component.\r\n- The Rule Flyout uses `isConnectorsScreenVisible` to
determine whether\r\nto continue to render `<RuleFlyoutBody>` or to
completely replace its\r\ncontents with
`<RuleFlyoutSelectConnector>`\r\n\r\nFor consistency, this PR also moves
the Show Request modal/flyout screen\r\ninto the same system.\r\n\r\n###
Testing\r\n\r\nTo test the new flyout,
edit\r\n`packages/response-ops/rule_form/src/create_rule_form.tsx`
and\r\n`packages/response-ops/rule_form/src/edit_rule_form.tsx` so that
they\r\nrender `<RuleFlyout>` instead of
`<RulePage>`.\r\n\r\n<details>\r\n<summary><strong>Use this diff
block</strong></summary>\r\n\r\n```diff\r\ndiff --git
a/packages/response-ops/rule_form/src/create_rule_form.tsx
b/packages/response-ops/rule_form/src/create_rule_form.tsx\r\nindex
2f5e0472dcd..564744b96ec 100644\r\n---
a/packages/response-ops/rule_form/src/create_rule_form.tsx\r\n+++
b/packages/response-ops/rule_form/src/create_rule_form.tsx\r\n@@ -31,6
+31,7 @@ import {\r\n parseRuleCircuitBreakerErrorMessage,\r\n } from
'./utils';\r\n import { RULE_CREATE_SUCCESS_TEXT, RULE_CREATE_ERROR_TEXT
} from './translations';\r\n+import { RuleFlyout } from
'./rule_flyout';\r\n \r\n export interface CreateRuleFormProps {\r\n
ruleTypeId: string;\r\n@@ -199,7 +200,7 @@ export const CreateRuleForm =
(props: CreateRuleFormProps) => {\r\n }),\r\n }}\r\n >\r\n- <RulePage
isEdit={false} isSaving={isSaving} onCancel={onCancel} onSave={onSave}
/>\r\n+ <RuleFlyout isEdit={false} isSaving={isSaving}
onCancel={onCancel} onSave={onSave} />\r\n </RuleFormStateProvider>\r\n
</div>\r\n );\r\ndiff --git
a/packages/response-ops/rule_form/src/edit_rule_form.tsx
b/packages/response-ops/rule_form/src/edit_rule_form.tsx\r\nindex
392447114ed..41aecd7245a 100644\r\n---
a/packages/response-ops/rule_form/src/edit_rule_form.tsx\r\n+++
b/packages/response-ops/rule_form/src/edit_rule_form.tsx\r\n@@ -26,6
+26,7 @@ import {\r\n import { RULE_EDIT_ERROR_TEXT,
RULE_EDIT_SUCCESS_TEXT } from './translations';\r\n import {
getAvailableRuleTypes, parseRuleCircuitBreakerErrorMessage } from
'./utils';\r\n import { DEFAULT_VALID_CONSUMERS, getDefaultFormData }
from './constants';\r\n+import { RuleFlyout } from './rule_flyout';\r\n
\r\n export interface EditRuleFormProps {\r\n id: string;\r\n@@ -193,7
+194,7 @@ export const EditRuleForm = (props: EditRuleFormProps) =>
{\r\n showMustacheAutocompleteSwitch,\r\n }}\r\n >\r\n- <RulePage
isEdit={true} isSaving={isSaving} onSave={onSave} onCancel={onCancel}
/>\r\n+ <RuleFlyout isEdit={true} isSaving={isSaving} onSave={onSave}
onCancel={onCancel} />\r\n </RuleFormStateProvider>\r\n </div>\r\n
);\r\n```\r\n\r\n</details>\r\n\r\n### Still Todo\r\n\r\n1. Replace all
instances of the v1 rule flyout with this new one (it's\r\nused heavily
in solutions, not in Stack Management)\r\n\r\n### Checklist\r\n\r\n- [x]
Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<[email protected]>","sha":"8004e3e70ad63b938d724eafd561533eeb225cd9"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/206154","number":206154,"mergeCommit":{"message":"[Response
Ops] [Rule Form] Add Show Request and Add Action screens to flyout
(#206154)\n\n## Summary\r\n\r\nPart of #195211\r\n\r\n- Adds Show
Request screen to the new rule form
flyout\r\n\r\n<details>\r\n<summary>Screenshot</summary>\r\n<img
width=\"585\" alt=\"Screenshot 2025-01-10 at 1 30
15 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/72500b0d-d959-4d17-944e-a7dc0894fb98\"\r\n/>\r\n</details>\r\n\r\n-
Renders the action connectors UI within the flyout instead of
opening\r\na modal\r\n
\r\n<details>\r\n<summary>Screenshot</summary>\r\n<img width=\"505\"
alt=\"Screenshot 2025-01-10 at 1 28
38 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/b5b464c0-7359-43ab-bea1-93d2981a5794\"\r\n/>\r\n</details>\r\n\r\n-
Duplicates the dropdown filter design from the flyout UI within
the\r\naction connectors modal when displayed on a smaller
screen\r\n\r\n<details>\r\n<summary>Screenshot</summary>\r\n<img
width=\"809\" alt=\"Screenshot 2025-01-10 at 1 30
28 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/5ef28458-1b6d-4a29-961d-fbcc1640e706\"\r\n/>\r\n</details>\r\n\r\n###
Implementation notes\r\n\r\nIn order to get the action connectors UI to
render the same way in both\r\na modal and the flyout, without
duplicating a large amount of code, I\r\nhad to introduce a little bit
of complexity. Within the Rule Page, it's\r\nas simple as opening the UI
inside a modal, but the flyout cannot open a\r\nsecond flyout; it has to
know when and how to completely replace its own\r\ncontents.\r\n\r\n-
The bulk of the action connectors UI is now moved
to\r\n`<RuleActionsConnectorsBody>`. `<RuleActionsConnectorsModal>`
and\r\n`<RuleFlyoutSelectConnector>` act as wrappers for this
component.\r\n- The `<RuleActions>` step no longer handles rendering the
connector UI,\r\nbecause it's not at a high enough level to know if it's
in the\r\n`<RulePage>` or the `<RuleFlyout>`. Instead, it simply sends a
signal up\r\nthe context hierarchy to
`setIsConnectorsScreenVisible`.\r\n- A new context called
`RuleFormScreenContext` keeps track of\r\n`isConnectorsScreenVisible`, a
state for whether or not the action\r\nconnectors \"screen\" is open,
regardless of whether that screen is\r\ndisplayed in a modal or a
flyout.\r\n- The Rule Page uses `isConnectorsScreenVisible` to determine
whether to\r\nrender the modal. This works the same way as it used to,
but handled by\r\nthe `<RulePage>` instead of the `<RuleActions>`
component.\r\n- The Rule Flyout uses `isConnectorsScreenVisible` to
determine whether\r\nto continue to render `<RuleFlyoutBody>` or to
completely replace its\r\ncontents with
`<RuleFlyoutSelectConnector>`\r\n\r\nFor consistency, this PR also moves
the Show Request modal/flyout screen\r\ninto the same system.\r\n\r\n###
Testing\r\n\r\nTo test the new flyout,
edit\r\n`packages/response-ops/rule_form/src/create_rule_form.tsx`
and\r\n`packages/response-ops/rule_form/src/edit_rule_form.tsx` so that
they\r\nrender `<RuleFlyout>` instead of
`<RulePage>`.\r\n\r\n<details>\r\n<summary><strong>Use this diff
block</strong></summary>\r\n\r\n```diff\r\ndiff --git
a/packages/response-ops/rule_form/src/create_rule_form.tsx
b/packages/response-ops/rule_form/src/create_rule_form.tsx\r\nindex
2f5e0472dcd..564744b96ec 100644\r\n---
a/packages/response-ops/rule_form/src/create_rule_form.tsx\r\n+++
b/packages/response-ops/rule_form/src/create_rule_form.tsx\r\n@@ -31,6
+31,7 @@ import {\r\n parseRuleCircuitBreakerErrorMessage,\r\n } from
'./utils';\r\n import { RULE_CREATE_SUCCESS_TEXT, RULE_CREATE_ERROR_TEXT
} from './translations';\r\n+import { RuleFlyout } from
'./rule_flyout';\r\n \r\n export interface CreateRuleFormProps {\r\n
ruleTypeId: string;\r\n@@ -199,7 +200,7 @@ export const CreateRuleForm =
(props: CreateRuleFormProps) => {\r\n }),\r\n }}\r\n >\r\n- <RulePage
isEdit={false} isSaving={isSaving} onCancel={onCancel} onSave={onSave}
/>\r\n+ <RuleFlyout isEdit={false} isSaving={isSaving}
onCancel={onCancel} onSave={onSave} />\r\n </RuleFormStateProvider>\r\n
</div>\r\n );\r\ndiff --git
a/packages/response-ops/rule_form/src/edit_rule_form.tsx
b/packages/response-ops/rule_form/src/edit_rule_form.tsx\r\nindex
392447114ed..41aecd7245a 100644\r\n---
a/packages/response-ops/rule_form/src/edit_rule_form.tsx\r\n+++
b/packages/response-ops/rule_form/src/edit_rule_form.tsx\r\n@@ -26,6
+26,7 @@ import {\r\n import { RULE_EDIT_ERROR_TEXT,
RULE_EDIT_SUCCESS_TEXT } from './translations';\r\n import {
getAvailableRuleTypes, parseRuleCircuitBreakerErrorMessage } from
'./utils';\r\n import { DEFAULT_VALID_CONSUMERS, getDefaultFormData }
from './constants';\r\n+import { RuleFlyout } from './rule_flyout';\r\n
\r\n export interface EditRuleFormProps {\r\n id: string;\r\n@@ -193,7
+194,7 @@ export const EditRuleForm = (props: EditRuleFormProps) =>
{\r\n showMustacheAutocompleteSwitch,\r\n }}\r\n >\r\n- <RulePage
isEdit={true} isSaving={isSaving} onSave={onSave} onCancel={onCancel}
/>\r\n+ <RuleFlyout isEdit={true} isSaving={isSaving} onSave={onSave}
onCancel={onCancel} />\r\n </RuleFormStateProvider>\r\n </div>\r\n
);\r\n```\r\n\r\n</details>\r\n\r\n### Still Todo\r\n\r\n1. Replace all
instances of the v1 rule flyout with this new one (it's\r\nused heavily
in solutions, not in Stack Management)\r\n\r\n### Checklist\r\n\r\n- [x]
Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<[email protected]>","sha":"8004e3e70ad63b938d724eafd561533eeb225cd9"}},{"branch":"8.x","label":"v8.18.0","branchLabelMappingKey":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Zacqary Adam Xeper <[email protected]>
  • Loading branch information
kibanamachine and Zacqary authored Jan 22, 2025
1 parent 14b5722 commit ae8be0d
Show file tree
Hide file tree
Showing 26 changed files with 1,124 additions and 647 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
export * from './use_rule_form_dispatch';
export * from './use_rule_form_state';
export * from './use_rule_form_steps';
export * from './use_rule_form_screen_context';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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 { useContext } from 'react';
import { RuleFormScreenContext } from '../rule_form_screen_context';

export const useRuleFormScreenContext = () => {
return useContext(RuleFormScreenContext);
};
Original file line number Diff line number Diff line change
Expand Up @@ -149,27 +149,15 @@ const useCommonRuleFormSteps = ({
? {
title: RULE_FORM_PAGE_RULE_ACTIONS_TITLE,
status: actionsStatus,
children: (
<>
<RuleActions />
<EuiSpacer />
<EuiHorizontalRule margin="none" />
</>
),
children: <RuleActions />,
}
: null,
[RuleFormStepId.DETAILS]: {
title: shortTitles
? RULE_FORM_PAGE_RULE_DETAILS_TITLE_SHORT
: RULE_FORM_PAGE_RULE_DETAILS_TITLE,
status: ruleDetailsStatus,
children: (
<>
<RuleDetails />
<EuiSpacer />
<EuiHorizontalRule margin="none" />
</>
),
children: <RuleDetails />,
},
}),
[ruleDefinitionStatus, canReadConnectors, actionsStatus, ruleDetailsStatus, shortTitles]
Expand Down Expand Up @@ -210,7 +198,7 @@ export const useRuleFormSteps: () => RuleFormVerticalSteps = () => {

const mappedSteps = useMemo(() => {
return stepOrder
.map((stepId) => {
.map((stepId, index) => {
const step = steps[stepId];
return step
? {
Expand All @@ -227,6 +215,12 @@ export const useRuleFormSteps: () => RuleFormVerticalSteps = () => {
stepId={stepId}
>
{step.children}
{index > 0 && (
<>
<EuiSpacer />
<EuiHorizontalRule margin="none" />
</>
)}
</ReportOnBlur>
),
}
Expand All @@ -246,8 +240,10 @@ interface RuleFormHorizontalSteps {
hasNextStep: boolean;
hasPreviousStep: boolean;
}
export const useRuleFormHorizontalSteps: () => RuleFormHorizontalSteps = () => {
const [currentStep, setCurrentStep] = useState<RuleFormStepId>(STEP_ORDER[0]);
export const useRuleFormHorizontalSteps: (
initialStep?: RuleFormStepId
) => RuleFormHorizontalSteps = (initialStep = STEP_ORDER[0]) => {
const [currentStep, setCurrentStep] = useState<RuleFormStepId>(initialStep);
const [touchedSteps, setTouchedSteps] = useState<Record<RuleFormStepId, boolean>>(
STEP_ORDER.reduce(
(result, stepId) => ({ ...result, [stepId]: false }),
Expand Down
Original file line number Diff line number Diff line change
@@ -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 './request_code_block';
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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 { omit, pick } from 'lodash';
import React, { useMemo } from 'react';
import { EuiCodeBlock } from '@elastic/eui';
import {
CreateRuleBody,
UPDATE_FIELDS_WITH_ACTIONS,
UpdateRuleBody,
transformCreateRuleBody,
transformUpdateRuleBody,
} from '../common/apis';
import { BASE_ALERTING_API_PATH } from '../constants';
import { useRuleFormState } from '../hooks';
import { SHOW_REQUEST_MODAL_ERROR } from '../translations';
import { RuleFormData } from '../types';

const stringifyBodyRequest = ({
formData,
isEdit,
}: {
formData: RuleFormData;
isEdit: boolean;
}): string => {
try {
const request = isEdit
? transformUpdateRuleBody(pick(formData, UPDATE_FIELDS_WITH_ACTIONS) as UpdateRuleBody)
: transformCreateRuleBody(omit(formData, 'id') as CreateRuleBody);
return JSON.stringify(request, null, 2);
} catch {
return SHOW_REQUEST_MODAL_ERROR;
}
};

interface RequestCodeBlockProps {
isEdit: boolean;
'data-test-subj'?: string;
}
export const RequestCodeBlock = (props: RequestCodeBlockProps) => {
const { isEdit, 'data-test-subj': dataTestSubj } = props;
const { formData, id, multiConsumerSelection } = useRuleFormState();

const formattedRequest = useMemo(() => {
return stringifyBodyRequest({
formData: {
...formData,
...(multiConsumerSelection ? { consumer: multiConsumerSelection } : {}),
},
isEdit,
});
}, [formData, isEdit, multiConsumerSelection]);

return (
<EuiCodeBlock language="json" isCopyable data-test-subj={dataTestSubj}>
{`${isEdit ? 'PUT' : 'POST'} kbn:${BASE_ALERTING_API_PATH}/rule${
isEdit ? `/${id}` : ''
}\n${formattedRequest}`}
</EuiCodeBlock>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const http = httpServiceMock.createStartContract();
jest.mock('../hooks', () => ({
useRuleFormState: jest.fn(),
useRuleFormDispatch: jest.fn(),
useRuleFormScreenContext: jest.fn(),
}));

jest.mock('./rule_actions_system_actions_item', () => ({
Expand Down Expand Up @@ -94,7 +95,8 @@ const mockValidate = jest.fn().mockResolvedValue({
errors: {},
});

const { useRuleFormState, useRuleFormDispatch } = jest.requireMock('../hooks');
const { useRuleFormState, useRuleFormDispatch, useRuleFormScreenContext } =
jest.requireMock('../hooks');
const { useLoadConnectors, useLoadConnectorTypes, useLoadRuleTypeAadTemplateField } =
jest.requireMock('../common/hooks');

Expand All @@ -109,6 +111,7 @@ const mockActions = [getAction('1'), getAction('2')];
const mockSystemActions = [getSystemAction('3')];

const mockOnChange = jest.fn();
const mockSetIsConnectorsScreenVisible = jest.fn();

describe('ruleActions', () => {
beforeEach(() => {
Expand Down Expand Up @@ -167,6 +170,9 @@ describe('ruleActions', () => {
aadTemplateFields: [],
});
useRuleFormDispatch.mockReturnValue(mockOnChange);
useRuleFormScreenContext.mockReturnValue({
setIsConnectorsScreenVisible: mockSetIsConnectorsScreenVisible,
});
});

afterEach(() => {
Expand Down Expand Up @@ -216,29 +222,7 @@ describe('ruleActions', () => {
render(<RuleActions />);

await userEvent.click(screen.getByTestId('ruleActionsAddActionButton'));
expect(screen.getByText('RuleActionsConnectorsModal')).toBeInTheDocument();
});

test('should call onSelectConnector with the correct parameters', async () => {
render(<RuleActions />);

await userEvent.click(screen.getByTestId('ruleActionsAddActionButton'));
expect(screen.getByText('RuleActionsConnectorsModal')).toBeInTheDocument();

await userEvent.click(screen.getByText('select connector'));
expect(mockOnChange).toHaveBeenCalledWith({
payload: {
actionTypeId: 'actionType-1',
frequency: { notifyWhen: 'onActionGroupChange', summary: false, throttle: null },
group: 'test',
id: 'connector-1',
params: { key: 'value' },
uuid: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
},
type: 'addAction',
});

expect(screen.queryByText('RuleActionsConnectorsModal')).not.toBeInTheDocument();
expect(mockSetIsConnectorsScreenVisible).toHaveBeenCalledWith(true);
});

test('should use the rule producer ID if it is not a multi-consumer rule', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,17 @@

import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiImage, EuiSpacer, EuiText } from '@elastic/eui';
import { RuleSystemAction } from '@kbn/alerting-types';
import { ActionConnector } from '@kbn/alerts-ui-shared';
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 { RuleAction } from '../common/types';
import { MULTI_CONSUMER_RULE_TYPE_IDS } from '../constants';
import { useRuleFormState, useRuleFormScreenContext } 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';

Expand All @@ -40,69 +36,19 @@ const useRuleActionsIllustration = () => {
};

export const RuleActions = () => {
const [isConnectorModalOpen, setIsConnectorModalOpen] = useState<boolean>(false);
const ruleActionsIllustration = useRuleActionsIllustration();
const { setIsConnectorsScreenVisible } = useRuleFormScreenContext();

const {
formData: { actions, consumer },
plugins: { actionTypeRegistry },
multiConsumerSelection,
selectedRuleType,
connectorTypes,
} = useRuleFormState();

const dispatch = useRuleFormDispatch();

const onModalOpen = useCallback(() => {
setIsConnectorModalOpen(true);
}, []);

const onModalClose = useCallback(() => {
setIsConnectorModalOpen(false);
}, []);

const onSelectConnector = useCallback(
async (connector: ActionConnector) => {
const { id, actionTypeId } = connector;
const uuid = uuidv4();
const group = selectedRuleType.defaultActionGroupId;
const actionTypeModel = actionTypeRegistry.get(actionTypeId);

const params =
getDefaultParams({
group,
ruleType: selectedRuleType,
actionTypeModel,
}) || {};

dispatch({
type: 'addAction',
payload: {
id,
actionTypeId,
uuid,
params,
group,
frequency: DEFAULT_FREQUENCY,
},
});

const res: { errors: RuleFormParamsErrors } = await actionTypeRegistry
.get(actionTypeId)
?.validateParams(params);

dispatch({
type: 'setActionParamsError',
payload: {
uuid,
errors: res.errors,
},
});

onModalClose();
},
[dispatch, onModalClose, selectedRuleType, actionTypeRegistry]
);
setIsConnectorsScreenVisible(true);
}, [setIsConnectorsScreenVisible]);

const producerId = useMemo(() => {
if (MULTI_CONSUMER_RULE_TYPE_IDS.includes(selectedRuleType.id)) {
Expand Down Expand Up @@ -184,9 +130,6 @@ export const RuleActions = () => {
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
{isConnectorModalOpen && (
<RuleActionsConnectorsModal onClose={onModalClose} onSelectConnector={onSelectConnector} />
)}
</>
);
};
Loading

0 comments on commit ae8be0d

Please sign in to comment.