-
Notifications
You must be signed in to change notification settings - Fork 8.6k
[Alerting V2] [UI] Add activation configuration fields to alerting V2 rule form #255111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
yiannisnikolopoulos
merged 16 commits into
elastic:alerting_v2
from
yiannisnikolopoulos:feature/esql-rule-activation-configuration
Mar 6, 2026
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
add1604
[Alerting V2] [UI] Add activation configuration fields to alerting V2…
yiannisnikolopoulos 43f41d1
Merge branch 'alerting_v2' into feature/esql-rule-activation-configur…
yiannisnikolopoulos fa6bc78
Update condition handling in useCreateRule to use undefined instead o…
yiannisnikolopoulos 79483f0
Update tests for ActivationConfigurationFieldGroup to include new act…
yiannisnikolopoulos 8447ea7
Merge branch 'alerting_v2' into feature/esql-rule-activation-configur…
yiannisnikolopoulos a68c743
Merge branch 'alerting_v2' into feature/esql-rule-activation-configur…
yiannisnikolopoulos 9ddd90e
Refactor alerting rule form to replace Activation Configuration with …
yiannisnikolopoulos fd283ad
Cleanup and refactor
yiannisnikolopoulos 6d6d191
Update state transition components and form UI to match closer to the…
yiannisnikolopoulos 0650773
Merge branch 'alerting_v2' into feature/esql-rule-activation-configur…
yiannisnikolopoulos d505909
Enforce max value of 1000 for state transition count
yiannisnikolopoulos 7cb7adb
Enhance state transition form fields with default values and UI adjus…
yiannisnikolopoulos b4e5d3a
Merge branch 'alerting_v2' into feature/esql-rule-activation-configur…
yiannisnikolopoulos 949216a
Undo compressed prop addition in rule form fields
yiannisnikolopoulos ed889c3
Refactor state transition form fields to use default values and simpl…
yiannisnikolopoulos d5f9ac0
Merge branch 'alerting_v2' into feature/esql-rule-activation-configur…
yiannisnikolopoulos File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
...esponse-ops/alerting-v2-rule-form/form/field_groups/state_transition_field_group.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| /* | ||
| * 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; you may not use this file except in compliance with the Elastic License | ||
| * 2.0. | ||
| */ | ||
|
|
||
| import React from 'react'; | ||
| import { fireEvent, render, screen } from '@testing-library/react'; | ||
| import { StateTransitionFieldGroup } from './state_transition_field_group'; | ||
| import { createFormWrapper } from '../../test_utils'; | ||
|
|
||
| describe('StateTransitionFieldGroup', () => { | ||
| it('renders immediate mode by default when kind is "alert"', () => { | ||
| render(<StateTransitionFieldGroup />, { | ||
| wrapper: createFormWrapper({ kind: 'alert' }), | ||
| }); | ||
|
|
||
| expect(screen.getByText('Alert delay')).toBeInTheDocument(); | ||
| expect(screen.getByText('Immediate')).toBeInTheDocument(); | ||
| expect(screen.getByText('Breaches')).toBeInTheDocument(); | ||
| expect(screen.getByText('Duration')).toBeInTheDocument(); | ||
| expect(screen.getByTestId('stateTransitionImmediateDescription')).toBeInTheDocument(); | ||
| expect(screen.queryByTestId('stateTransitionCountInput')).not.toBeInTheDocument(); | ||
| expect(screen.queryByTestId('stateTransitionTimeframeNumberInput')).not.toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('shows breaches input when breaches is selected', () => { | ||
| render(<StateTransitionFieldGroup />, { | ||
| wrapper: createFormWrapper({ kind: 'alert' }), | ||
| }); | ||
|
|
||
| fireEvent.click(screen.getByRole('button', { name: 'Breaches' })); | ||
|
|
||
| expect(screen.getByTestId('stateTransitionCountInput')).toBeInTheDocument(); | ||
| expect(screen.getByTestId('stateTransitionCountInput')).toHaveValue(2); | ||
| expect(screen.queryByTestId('stateTransitionImmediateDescription')).not.toBeInTheDocument(); | ||
| expect(screen.queryByTestId('stateTransitionTimeframeNumberInput')).not.toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('does not render when kind is "signal"', () => { | ||
| render(<StateTransitionFieldGroup />, { | ||
| wrapper: createFormWrapper({ kind: 'signal' }), | ||
| }); | ||
|
|
||
| expect(screen.queryByText('Alert delay')).not.toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('shows immediate mode text when immediate is selected', () => { | ||
| render(<StateTransitionFieldGroup />, { | ||
| wrapper: createFormWrapper({ kind: 'alert' }), | ||
| }); | ||
|
|
||
| fireEvent.click(screen.getByRole('button', { name: 'Immediate' })); | ||
|
|
||
| expect(screen.getByTestId('stateTransitionImmediateDescription')).toBeInTheDocument(); | ||
| expect(screen.queryByTestId('stateTransitionCountInput')).not.toBeInTheDocument(); | ||
| expect(screen.queryByTestId('stateTransitionTimeframeNumberInput')).not.toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('shows duration inputs when duration is selected', () => { | ||
| render(<StateTransitionFieldGroup />, { | ||
| wrapper: createFormWrapper({ kind: 'alert' }), | ||
| }); | ||
|
|
||
| fireEvent.click(screen.getByRole('button', { name: 'Duration' })); | ||
|
|
||
| expect(screen.getByTestId('stateTransitionTimeframeNumberInput')).toBeInTheDocument(); | ||
| expect(screen.getByTestId('stateTransitionTimeframeUnitInput')).toBeInTheDocument(); | ||
| expect(screen.getByTestId('stateTransitionTimeframeNumberInput')).toHaveValue(2); | ||
| expect(screen.getByTestId('stateTransitionTimeframeUnitInput')).toHaveValue('m'); | ||
| expect(screen.queryByTestId('stateTransitionCountInput')).not.toBeInTheDocument(); | ||
| }); | ||
| }); |
133 changes: 133 additions & 0 deletions
133
...red/response-ops/alerting-v2-rule-form/form/field_groups/state_transition_field_group.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| /* | ||
| * 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; you may not use this file except in compliance with the Elastic License | ||
| * 2.0. | ||
| */ | ||
|
|
||
| import React, { useCallback, useState } from 'react'; | ||
| import { EuiButtonGroup, EuiSpacer, EuiText } from '@elastic/eui'; | ||
| import { i18n } from '@kbn/i18n'; | ||
| import { useFormContext, useWatch } from 'react-hook-form'; | ||
| import type { FormValues } from '../types'; | ||
| import { FieldGroup } from './field_group'; | ||
| import { StateTransitionCountField } from '../fields/state_transition_count_field'; | ||
| import { StateTransitionTimeframeField } from '../fields/state_transition_timeframe_field'; | ||
|
|
||
| type DelayMode = 'immediate' | 'breaches' | 'duration'; | ||
|
|
||
| const MODE_OPTIONS = [ | ||
| { | ||
| id: 'immediate' as const, | ||
| label: i18n.translate('xpack.alertingV2.ruleForm.stateTransition.delayModeImmediate', { | ||
| defaultMessage: 'Immediate', | ||
| }), | ||
| }, | ||
| { | ||
| id: 'breaches' as const, | ||
| label: i18n.translate('xpack.alertingV2.ruleForm.stateTransition.delayModeBreaches', { | ||
| defaultMessage: 'Breaches', | ||
| }), | ||
| }, | ||
| { | ||
| id: 'duration' as const, | ||
| label: i18n.translate('xpack.alertingV2.ruleForm.stateTransition.delayModeDuration', { | ||
| defaultMessage: 'Duration', | ||
| }), | ||
| }, | ||
| ]; | ||
|
|
||
| const DEFAULT_PENDING_COUNT = 2; | ||
| const DEFAULT_PENDING_TIMEFRAME = '2m'; | ||
|
|
||
| const deriveMode = (stateTransition?: { | ||
| pendingTimeframe?: string; | ||
| pendingCount?: number; | ||
| }): DelayMode => { | ||
| if (stateTransition?.pendingTimeframe != null) return 'duration'; | ||
| if (stateTransition?.pendingCount != null) return 'breaches'; | ||
| return 'immediate'; | ||
| }; | ||
|
|
||
| export const StateTransitionFieldGroup: React.FC = () => { | ||
| const { control, setValue } = useFormContext<FormValues>(); | ||
| const kind = useWatch({ control, name: 'kind' }); | ||
| const stateTransition = useWatch({ control, name: 'stateTransition' }); | ||
| const [selectedMode, setSelectedMode] = useState<DelayMode>(deriveMode(stateTransition)); | ||
|
|
||
| const onModeChange = useCallback( | ||
| (mode: string) => { | ||
| switch (mode as DelayMode) { | ||
| case 'immediate': | ||
| setSelectedMode('immediate'); | ||
| setValue('stateTransition', undefined); | ||
| break; | ||
| case 'breaches': | ||
| setSelectedMode('breaches'); | ||
| setValue( | ||
| 'stateTransition.pendingCount', | ||
| stateTransition?.pendingCount ?? DEFAULT_PENDING_COUNT | ||
| ); | ||
| setValue('stateTransition.pendingTimeframe', undefined); | ||
| break; | ||
| case 'duration': | ||
| setSelectedMode('duration'); | ||
| setValue('stateTransition.pendingCount', undefined); | ||
| setValue( | ||
| 'stateTransition.pendingTimeframe', | ||
| stateTransition?.pendingTimeframe ?? DEFAULT_PENDING_TIMEFRAME | ||
| ); | ||
| break; | ||
| } | ||
| }, | ||
| [setValue, stateTransition?.pendingCount, stateTransition?.pendingTimeframe] | ||
| ); | ||
|
|
||
| if (kind !== 'alert') { | ||
| return null; | ||
| } | ||
|
|
||
| return ( | ||
| <FieldGroup | ||
| title={i18n.translate('xpack.alertingV2.ruleForm.stateTransition.title', { | ||
| defaultMessage: 'Alert delay', | ||
| })} | ||
| > | ||
| <EuiButtonGroup | ||
| buttonSize="s" | ||
| legend={i18n.translate('xpack.alertingV2.ruleForm.stateTransition.delayModeLegend', { | ||
| defaultMessage: 'Alert delay mode', | ||
| })} | ||
| options={MODE_OPTIONS} | ||
| idSelected={selectedMode} | ||
| onChange={onModeChange} | ||
| isFullWidth | ||
| data-test-subj="stateTransitionDelayMode" | ||
| /> | ||
| <EuiSpacer size="s" /> | ||
| {selectedMode === 'immediate' && ( | ||
| <EuiText size="xs" color="subdued" data-test-subj="stateTransitionImmediateDescription"> | ||
| {i18n.translate('xpack.alertingV2.ruleForm.stateTransition.immediateDescription', { | ||
| defaultMessage: 'No delay - Alerts on first breach', | ||
| })} | ||
| </EuiText> | ||
| )} | ||
| {selectedMode === 'breaches' && ( | ||
| <StateTransitionCountField | ||
| prependLabel={i18n.translate( | ||
| 'xpack.alertingV2.ruleForm.stateTransition.inlineBreachesPrepend', | ||
| { defaultMessage: 'Consecutive breaches' } | ||
| )} | ||
| /> | ||
| )} | ||
| {selectedMode === 'duration' && ( | ||
| <StateTransitionTimeframeField | ||
| numberPrependLabel={i18n.translate( | ||
| 'xpack.alertingV2.ruleForm.stateTransition.inlineDurationPrepend', | ||
| { defaultMessage: 'Active for' } | ||
| )} | ||
| /> | ||
| )} | ||
| </FieldGroup> | ||
| ); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a fixed width because size
"m"was too wide and size"s"didn't fit the fields properly. I chose560pxbecause that was the width used in the figma flyoutThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I have some changes coming to this in my PR. We can use this for now.