Skip to content

Commit

Permalink
[Security Solution][Detections] Manual rule run - Updated layout for …
Browse files Browse the repository at this point in the history
…time range selection modal (elastic#186526)

## Summary

Related to elastic/security-team#9327

These changes update the layout of the modal to select a time range for
"manual rule run":
1. Removed inlined calendars
2. Added modal title - "Manual rule run"
3. Increased made width to `600px`

<img width="1663" alt="Screenshot 2024-06-24 at 12 35 36"
src="https://github.com/elastic/kibana/assets/2700761/db6a6be7-5be5-4560-97f0-7f3f5eb1229b">

---------

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
e40pud and kibanamachine authored Jun 24, 2024
1 parent 745abf7 commit 4276d3f
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@
*/

import React from 'react';
import moment from 'moment';
import { fireEvent, render, screen } from '@testing-library/react';
import { ManualRuleRunModal } from '.';
import { ManualRuleRunModal, MAX_SCHEDULE_BACKFILL_LOOKBACK_WINDOW_DAYS } from '.';

const DATE_PICKER_PREVIOUS_BTN_CLASS = '.react-datepicker__navigation--previous';
const DATE_PICKER_NEXT_BTN_CLASS = '.react-datepicker__navigation--next';
const convertToDatePickerFormat = (date: moment.Moment) => {
return `${date.format('L')} ${date.format('LT')}`;
};

describe('ManualRuleRunModal', () => {
const onCancelMock = jest.fn();
const onConfirmMock = jest.fn();

let startDatePicker: HTMLElement;
let endDatePicker: HTMLElement;
let startDatePicker: Element;
let endDatePicker: Element;
let confirmModalConfirmButton: HTMLElement;
let cancelModalConfirmButton: HTMLElement;
let timeRangeForm: HTMLElement;
Expand All @@ -28,18 +30,13 @@ describe('ManualRuleRunModal', () => {
});

beforeEach(() => {
// This is an attempt to fix the "TypeError: scrollIntoView is not a function" error
// According to https://stackoverflow.com/a/53294906 the `scrollIntoView` is not implemented in jsdom,
// and proposed solution is coming from https://github.com/jsdom/jsdom/issues/1695
window.HTMLElement.prototype.scrollIntoView = () => {};

render(<ManualRuleRunModal onCancel={onCancelMock} onConfirm={onConfirmMock} />);

startDatePicker = screen.getByTestId('start-date-picker');
endDatePicker = screen.getByTestId('end-date-picker');
timeRangeForm = screen.getByTestId('manual-rule-run-time-range-form');
startDatePicker = timeRangeForm.getElementsByClassName('start-date-picker')[0];
endDatePicker = timeRangeForm.getElementsByClassName('end-date-picker')[0];
confirmModalConfirmButton = screen.getByTestId('confirmModalConfirmButton');
cancelModalConfirmButton = screen.getByTestId('confirmModalCancelButton');
timeRangeForm = screen.getByTestId('manual-rule-run-time-range-form');
});

it('should render modal', () => {
Expand All @@ -51,7 +48,16 @@ describe('ManualRuleRunModal', () => {
it('should render confirmation button disabled if invalid time range has been selected', () => {
expect(confirmModalConfirmButton).toBeEnabled();

fireEvent.click(endDatePicker.querySelector(DATE_PICKER_PREVIOUS_BTN_CLASS)!);
const now = moment();
const startDate = now.clone().subtract(1, 'd');
const endDate = now.clone().subtract(2, 'd');

fireEvent.change(startDatePicker, {
target: { value: convertToDatePickerFormat(startDate) },
});
fireEvent.change(endDatePicker, {
target: { value: convertToDatePickerFormat(endDate) },
});

expect(confirmModalConfirmButton).toBeDisabled();
expect(timeRangeForm).toHaveTextContent('Selected time range is invalid');
Expand All @@ -60,10 +66,14 @@ describe('ManualRuleRunModal', () => {
it('should render confirmation button disabled if selected start date is more than 90 days in the past', () => {
expect(confirmModalConfirmButton).toBeEnabled();

fireEvent.click(startDatePicker.querySelector(DATE_PICKER_PREVIOUS_BTN_CLASS)!);
fireEvent.click(startDatePicker.querySelector(DATE_PICKER_PREVIOUS_BTN_CLASS)!);
fireEvent.click(startDatePicker.querySelector(DATE_PICKER_PREVIOUS_BTN_CLASS)!);
fireEvent.click(startDatePicker.querySelector(DATE_PICKER_PREVIOUS_BTN_CLASS)!);
const now = moment();
const startDate = now.clone().subtract(MAX_SCHEDULE_BACKFILL_LOOKBACK_WINDOW_DAYS, 'd');

fireEvent.change(startDatePicker, {
target: {
value: convertToDatePickerFormat(startDate),
},
});

expect(confirmModalConfirmButton).toBeDisabled();
expect(timeRangeForm).toHaveTextContent(
Expand All @@ -74,7 +84,12 @@ describe('ManualRuleRunModal', () => {
it('should render confirmation button disabled if selected end date is in future', () => {
expect(confirmModalConfirmButton).toBeEnabled();

fireEvent.click(endDatePicker.querySelector(DATE_PICKER_NEXT_BTN_CLASS)!);
const now = moment();
const endDate = now.clone().add(2, 'd');

fireEvent.change(endDatePicker, {
target: { value: convertToDatePickerFormat(endDate) },
});

expect(confirmModalConfirmButton).toBeDisabled();
expect(timeRangeForm).toHaveTextContent('Manual rule run cannot be scheduled for the future');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import {
EuiFlexItem,
EuiForm,
EuiFormRow,
EuiHorizontalRule,
EuiSpacer,
useGeneratedHtmlId,
} from '@elastic/eui';
import moment from 'moment';
Expand All @@ -23,6 +21,8 @@ import { TECHNICAL_PREVIEW, TECHNICAL_PREVIEW_TOOLTIP } from '../../../../common

import * as i18n from './translations';

const MANUAL_RULE_RUN_MODAL_WIDTH = 600;

export const MAX_SCHEDULE_BACKFILL_LOOKBACK_WINDOW_DAYS = 90;

interface ManualRuleRunModalProps {
Expand Down Expand Up @@ -68,36 +68,34 @@ const ManualRuleRunModalComponent = ({ onCancel, onConfirm }: ManualRuleRunModal
return (
<EuiConfirmModal
aria-labelledby={modalTitleId}
titleProps={{ id: modalTitleId }}
title={
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem>{i18n.MANUAL_RULE_RUN_MODAL_TITLE}</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiBetaBadge label={TECHNICAL_PREVIEW} tooltipContent={TECHNICAL_PREVIEW_TOOLTIP} />
</EuiFlexItem>
</EuiFlexGroup>
}
titleProps={{ id: modalTitleId, style: { width: '100%' } }}
onCancel={onCancel}
onConfirm={handleConfirm}
confirmButtonText={i18n.MANUAL_RULE_RUN_CONFIRM_BUTTON}
cancelButtonText={i18n.MANUAL_RULE_RUN_CANCEL_BUTTON}
confirmButtonDisabled={isInvalid}
style={{ width: MANUAL_RULE_RUN_MODAL_WIDTH }}
>
<EuiForm data-test-subj="manual-rule-run-modal-form">
<EuiSpacer size="m" />
<EuiForm data-test-subj="manual-rule-run-modal-form" fullWidth>
<EuiFormRow
data-test-subj="manual-rule-run-time-range-form"
label={
<EuiFlexGroup>
<EuiFlexItem>{i18n.MANUAL_RULE_RUN_TIME_RANGE_TITLE}</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiBetaBadge
label={TECHNICAL_PREVIEW}
tooltipContent={TECHNICAL_PREVIEW_TOOLTIP}
/>
</EuiFlexItem>
</EuiFlexGroup>
}
label={i18n.MANUAL_RULE_RUN_TIME_RANGE_TITLE}
isInvalid={isInvalid}
error={errorMessage}
>
<EuiDatePickerRange
data-test-subj="manual-rule-run-time-range"
readOnly={true}
startDateControl={
<EuiDatePicker
className="start-date-picker"
aria-label="Start date range"
selected={startDate}
onChange={(date) => date && setStartDate(date)}
Expand All @@ -108,6 +106,7 @@ const ManualRuleRunModalComponent = ({ onCancel, onConfirm }: ManualRuleRunModal
}
endDateControl={
<EuiDatePicker
className="end-date-picker"
aria-label="End date range"
selected={endDate}
onChange={(date) => date && setEndDate(date)}
Expand All @@ -118,30 +117,6 @@ const ManualRuleRunModalComponent = ({ onCancel, onConfirm }: ManualRuleRunModal
}
/>
</EuiFormRow>
<EuiHorizontalRule />
<EuiFormRow data-test-subj="start-date-picker" label={i18n.MANUAL_RULE_RUN_START_AT_TITLE}>
<EuiDatePicker
aria-label="Start date picker"
inline
selected={startDate}
onChange={(date) => date && setStartDate(date)}
startDate={startDate}
endDate={endDate}
showTimeSelect={true}
/>
</EuiFormRow>
<EuiHorizontalRule />
<EuiFormRow data-test-subj="end-date-picker" label={i18n.MANUAL_RULE_RUN_END_AT_TITLE}>
<EuiDatePicker
aria-label="End date picker"
inline
selected={endDate}
onChange={(date) => date && setEndDate(date)}
startDate={startDate}
endDate={endDate}
showTimeSelect={true}
/>
</EuiFormRow>
</EuiForm>
</EuiConfirmModal>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,64 @@
*/
import { i18n } from '@kbn/i18n';

export const MANUAL_RULE_RUN_MODAL_TITLE = i18n.translate(
'xpack.securitySolution.manualRuleRun.modalTitle',
{
defaultMessage: 'Manual rule run',
}
);

export const MANUAL_RULE_RUN_TIME_RANGE_TITLE = i18n.translate(
'xpack.securitySolution.manuelRuleRun.timeRangeTitle',
'xpack.securitySolution.manualRuleRun.timeRangeTitle',
{
defaultMessage: 'Select timerange for manual rule run',
}
);

export const MANUAL_RULE_RUN_START_AT_TITLE = i18n.translate(
'xpack.securitySolution.manuelRuleRun.startAtTitle',
'xpack.securitySolution.manualRuleRun.startAtTitle',
{
defaultMessage: 'Start at',
}
);

export const MANUAL_RULE_RUN_END_AT_TITLE = i18n.translate(
'xpack.securitySolution.manuelRuleRun.endAtTitle',
'xpack.securitySolution.manualRuleRun.endAtTitle',
{
defaultMessage: 'Finish at',
}
);

export const MANUAL_RULE_RUN_CONFIRM_BUTTON = i18n.translate(
'xpack.securitySolution.manuelRuleRun.confirmButton',
'xpack.securitySolution.manualRuleRun.confirmButton',
{
defaultMessage: 'Run',
}
);

export const MANUAL_RULE_RUN_CANCEL_BUTTON = i18n.translate(
'xpack.securitySolution.manuelRuleRun.cancelButton',
'xpack.securitySolution.manualRuleRun.cancelButton',
{
defaultMessage: 'Cancel',
}
);

export const MANUAL_RULE_RUN_INVALID_TIME_RANGE_ERROR = i18n.translate(
'xpack.securitySolution.manuelRuleRun.invalidTimeRangeError',
'xpack.securitySolution.manualRuleRun.invalidTimeRangeError',
{
defaultMessage: 'Selected time range is invalid',
}
);

export const MANUAL_RULE_RUN_FUTURE_TIME_RANGE_ERROR = i18n.translate(
'xpack.securitySolution.manuelRuleRun.futureTimeRangeError',
'xpack.securitySolution.manualRuleRun.futureTimeRangeError',
{
defaultMessage: 'Manual rule run cannot be scheduled for the future',
}
);

export const MANUAL_RULE_RUN_START_DATE_OUT_OF_RANGE_ERROR = (maxDaysLookback: number) =>
i18n.translate('xpack.securitySolution.manuelRuleRun.startDateIsOutOfRangeError', {
i18n.translate('xpack.securitySolution.manuelRulaRun.startDateIsOutOfRangeError', {
values: { maxDaysLookback },
defaultMessage:
'Manual rule run cannot be scheduled earlier than {maxDaysLookback, plural, =1 {# day} other {# days}} ago',
Expand Down

0 comments on commit 4276d3f

Please sign in to comment.