[RAM] Reset rule settings modal on cancel (#169720)

## Summary

Fixes #169296 

- Resets the rule settings modal when the user clicks Cancel, but caches
the initial pull from the server so that a second request isn't
necessary on reopen
- Updates this cache on save so that the reset on modal close remains
accurate


### Checklist

- [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:
Zacqary Adam Xeper 2023-10-25 12:12:06 -05:00 committed by GitHub
parent 8500600393
commit 3a5d6cc92b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 17 deletions

View file

@ -200,6 +200,36 @@ describe('rules_settings_modal', () => {
expect(modalProps.onSave).toHaveBeenCalledTimes(1);
});
test('reset flapping settings to initial state on cancel without triggering another server reload', async () => {
const result = render(<RulesSettingsModalWithProviders {...modalProps} />);
expect(getFlappingSettingsMock).toHaveBeenCalledTimes(1);
expect(getQueryDelaySettingsMock).toHaveBeenCalledTimes(1);
await waitForModalLoad();
const lookBackWindowInput = result.getByTestId('lookBackWindowRangeInput');
const statusChangeThresholdInput = result.getByTestId('statusChangeThresholdRangeInput');
fireEvent.change(lookBackWindowInput, { target: { value: 15 } });
fireEvent.change(statusChangeThresholdInput, { target: { value: 3 } });
expect(lookBackWindowInput.getAttribute('value')).toBe('15');
expect(statusChangeThresholdInput.getAttribute('value')).toBe('3');
// Try cancelling
userEvent.click(result.getByTestId('rulesSettingsModalCancelButton'));
expect(modalProps.onClose).toHaveBeenCalledTimes(1);
expect(updateFlappingSettingsMock).not.toHaveBeenCalled();
expect(modalProps.onSave).not.toHaveBeenCalled();
expect(screen.queryByTestId('centerJustifiedSpinner')).toBe(null);
expect(lookBackWindowInput.getAttribute('value')).toBe('10');
expect(statusChangeThresholdInput.getAttribute('value')).toBe('10');
expect(getFlappingSettingsMock).toHaveBeenCalledTimes(1);
expect(getQueryDelaySettingsMock).toHaveBeenCalledTimes(1);
});
test('should prevent statusChangeThreshold from being greater than lookBackWindow', async () => {
const result = render(<RulesSettingsModalWithProviders {...modalProps} />);
await waitForModalLoad();

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { memo, useState } from 'react';
import React, { memo, useCallback, useState, useRef } from 'react';
import {
RulesSettingsFlappingProperties,
RulesSettingsProperties,
@ -60,6 +60,26 @@ export const RulesSettingsErrorPrompt = memo(() => {
);
});
const useResettableState: <T>(
initialValue?: T
) => [T | undefined, boolean, (next: T, shouldUpdateInitialValue?: boolean) => void, () => void] = (
initalValue
) => {
const initialValueRef = useRef(initalValue);
const [value, setValue] = useState(initalValue);
const [hasChanged, setHasChanged] = useState(false);
const reset = () => {
setValue(initialValueRef.current);
setHasChanged(false);
};
const updateValue = (next: typeof value, shouldUpdateInitialValue = false) => {
setValue(next);
setHasChanged(true);
if (shouldUpdateInitialValue) initialValueRef.current = next;
};
return [value, hasChanged, updateValue, reset];
};
export interface RulesSettingsModalProps {
isVisible: boolean;
setUpdatingRulesSettings?: (isUpdating: boolean) => void;
@ -84,21 +104,24 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
},
} = capabilities;
const [flappingSettings, setFlappingSettings] = useState<RulesSettingsFlappingProperties>();
const [hasFlappingChanged, setHasFlappingChanged] = useState<boolean>(false);
const [flappingSettings, hasFlappingChanged, setFlappingSettings, resetFlappingSettings] =
useResettableState<RulesSettingsFlappingProperties>();
const [queryDelaySettings, setQueryDelaySettings] = useState<RulesSettingsQueryDelayProperties>();
const [hasQueryDelayChanged, setHasQueryDelayChanged] = useState<boolean>(false);
const [queryDelaySettings, hasQueryDelayChanged, setQueryDelaySettings, resetQueryDelaySettings] =
useResettableState<RulesSettingsQueryDelayProperties>();
const { isLoading: isFlappingLoading, isError: hasFlappingError } = useGetFlappingSettings({
enabled: isVisible,
onSuccess: (fetchedSettings) => {
if (!flappingSettings) {
setFlappingSettings({
enabled: fetchedSettings.enabled,
lookBackWindow: fetchedSettings.lookBackWindow,
statusChangeThreshold: fetchedSettings.statusChangeThreshold,
});
setFlappingSettings(
{
enabled: fetchedSettings.enabled,
lookBackWindow: fetchedSettings.lookBackWindow,
statusChangeThreshold: fetchedSettings.statusChangeThreshold,
},
true // Update the initial value so we don't need to fetch it from the server again
);
}
},
});
@ -107,13 +130,22 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
enabled: isVisible,
onSuccess: (fetchedSettings) => {
if (!queryDelaySettings) {
setQueryDelaySettings({
delay: fetchedSettings.delay,
});
setQueryDelaySettings(
{
delay: fetchedSettings.delay,
},
true
);
}
},
});
const onCloseModal = useCallback(() => {
resetFlappingSettings();
resetQueryDelaySettings();
onClose();
}, [onClose, resetFlappingSettings, resetQueryDelaySettings]);
const { mutate } = useUpdateRuleSettings({
onSave,
onClose,
@ -148,7 +180,6 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
newSettings.statusChangeThreshold
),
});
setHasFlappingChanged(true);
}
if (setting === 'queryDelay') {
@ -160,7 +191,6 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
[key]: value,
};
setQueryDelaySettings(newSettings);
setHasQueryDelayChanged(true);
}
};
@ -168,9 +198,11 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
const updatedSettings: RulesSettingsProperties = {};
if (canWriteFlappingSettings && hasFlappingChanged) {
updatedSettings.flapping = flappingSettings;
setFlappingSettings(flappingSettings!, true);
}
if (canWriteQueryDelaySettings && hasQueryDelayChanged) {
updatedSettings.queryDelay = queryDelaySettings;
setQueryDelaySettings(queryDelaySettings!, true);
}
mutate(updatedSettings);
};
@ -214,7 +246,7 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
};
return (
<EuiModal data-test-subj="rulesSettingsModal" onClose={onClose} maxWidth={880}>
<EuiModal data-test-subj="rulesSettingsModal" onClose={onCloseModal} maxWidth={880}>
<EuiModalHeader>
<EuiModalHeaderTitle component="h3">
<FormattedMessage
@ -236,7 +268,7 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
<EuiHorizontalRule margin="none" />
</EuiModalBody>
<EuiModalFooter>
<EuiButtonEmpty data-test-subj="rulesSettingsModalCancelButton" onClick={onClose}>
<EuiButtonEmpty data-test-subj="rulesSettingsModalCancelButton" onClick={onCloseModal}>
<FormattedMessage
id="xpack.triggersActionsUI.rulesSettings.modal.cancelButton"
defaultMessage="Cancel"