[ResponseOps][Maintenance Windows] Show dates in the selected timezone on the edit form (#158020)

Resolves https://github.com/elastic/kibana/issues/158013

## Summary

When a specific timezone has been set for a schedule, we will convert
the time to local time when showing it in a list, table or overview.
Updated the edit/create form so that when someone chooses to edit that
schedule, we will then default to and show the specific timezone that
was selected when it was created.


### 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


### To verify
Scenario 1: 

1. Make sure your kibana timezone setting is set to `'Browser'`
2. Create a maintenance window and set the timezone to
`America/Los_Angeles` or any timezone that is not your own
3. Verify that the times match the timezone set and save <img
width="1002" alt="Screen Shot 2023-05-17 at 2 47 12 PM"
src="4ea15e09-3482-42a5-aa06-2dee7dd8c189">
4. Verify on the table that the times match your current timezone <img
width="829" alt="Screen Shot 2023-05-17 at 2 51 31 PM"
src="29e6f52b-8c40-4133-92fe-0674400a7368">
5. Edit and verify that the times match the timezone you set when the mw
was created <img width="991" alt="Screen Shot 2023-05-17 at 2 52 29 PM"
src="9920d569-0e7d-484a-8f08-d5f38a94616d">

Scenario 2: 

1. Make sure your kibana timezone setting is set to timezone. I used
`America/Denver`
2. Create a maintenance window and verify that the times reflect the
timezone set in adv settings <img width="1000" alt="Screen Shot
2023-05-17 at 2 55 26 PM"
src="31f3164e-bb48-4ebe-98db-9bb24254fcd5">
3. Verify on the table that the times match adv settings
4. Edit and verify that the times and the timezone match adv settings
<img width="1001" alt="Screen Shot 2023-05-17 at 2 56 00 PM"
src="350ad4bb-9dbb-4265-bd7a-cf6a0cace7f7">
This commit is contained in:
Alexi Doak 2023-05-18 08:10:38 -04:00 committed by GitHub
parent 8cf6d034e3
commit 212e4df02d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 75 additions and 17 deletions

View file

@ -94,8 +94,8 @@ describe('CreateMaintenanceWindowForm', () => {
const timezoneInput = within(result.getByTestId('timezone-field')).getByTestId('input');
expect(titleInput).toHaveValue('test');
expect(dateInputs[0]).toHaveValue('03/24/2023 12:00 AM');
expect(dateInputs[1]).toHaveValue('03/26/2023 12:00 AM');
expect(dateInputs[0]).toHaveValue('03/23/2023 09:00 PM');
expect(dateInputs[1]).toHaveValue('03/25/2023 09:00 PM');
expect(recurringInput).toBeChecked();
expect(timezoneInput).toHaveTextContent('America/Los_Angeles');
});

View file

@ -108,9 +108,9 @@ export const CreateMaintenanceWindowForm = React.memo<CreateMaintenanceWindowFor
onSubmit: submitMaintenanceWindow,
});
const [{ recurring }] = useFormData<FormProps>({
const [{ recurring, timezone }] = useFormData<FormProps>({
form,
watch: ['recurring'],
watch: ['recurring', 'timezone'],
});
const isRecurring = recurring || false;
const showTimezone = isBrowser || initialValue?.timezone !== undefined;
@ -181,7 +181,13 @@ export const CreateMaintenanceWindowForm = React.memo<CreateMaintenanceWindowFor
},
}}
>
{(fields) => <DatePickerRangeField fields={fields} data-test-subj="date-field" />}
{(fields) => (
<DatePickerRangeField
fields={fields}
timezone={timezone ?? [defaultTimezone]}
data-test-subj="date-field"
/>
)}
</UseMultiFields>
</EuiFlexItem>
{showTimezone ? (

View file

@ -28,7 +28,7 @@ export const DatePickerField: React.FC<DatePickerFieldProps> = React.memo(
const { setFieldValue } = useFormContext();
const [form] = useFormData({ watch: [field.path] });
const selected = getSelected(form, field.path);
const { selected, utcOffset } = getSelected(form, field.path);
const onChange = useCallback(
(currentDate: Moment | null) => {
@ -45,6 +45,7 @@ export const DatePickerField: React.FC<DatePickerFieldProps> = React.memo(
selected={selected}
onChange={onChange}
minDate={today}
utcOffset={utcOffset}
fullWidth
/>
</EuiFormRow>

View file

@ -18,19 +18,28 @@ import { getSelectedForDatePicker as getSelected } from '../../helpers/get_selec
interface DatePickerRangeFieldProps {
fields: { startDate: FieldHook<string, string>; endDate: FieldHook<string, string> };
timezone?: string[];
showTimeSelect?: boolean;
'data-test-subj'?: string;
}
export const DatePickerRangeField: React.FC<DatePickerRangeFieldProps> = React.memo(
({ fields, showTimeSelect = true, ...rest }) => {
({ fields, timezone, showTimeSelect = true, ...rest }) => {
const [today] = useState<Moment>(moment());
const { setFieldValue } = useFormContext();
const [form] = useFormData({ watch: [fields.startDate.path, fields.endDate.path] });
const startDate = getSelected(form, fields.startDate.path);
const endDate = getSelected(form, fields.endDate.path);
const { selected: startDate, utcOffset: startOffset } = getSelected(
form,
fields.startDate.path,
timezone
);
const { selected: endDate, utcOffset: endOffset } = getSelected(
form,
fields.endDate.path,
timezone
);
const onStartDateChange = useCallback(
(currentDate: Moment | null) => {
@ -69,6 +78,7 @@ export const DatePickerRangeField: React.FC<DatePickerRangeFieldProps> = React.m
aria-label="Start date"
showTimeSelect={showTimeSelect}
minDate={today}
utcOffset={startOffset}
/>
}
endDateControl={
@ -80,6 +90,7 @@ export const DatePickerRangeField: React.FC<DatePickerRangeFieldProps> = React.m
aria-label="End date"
showTimeSelect={showTimeSelect}
minDate={today}
utcOffset={endOffset}
/>
}
fullWidth

View file

@ -15,22 +15,43 @@ describe('getSelectedForDatePicker', () => {
test('should return the current date if the form is not initialized', () => {
jest.useFakeTimers().setSystemTime(new Date('2023-03-30T00:00:00.000Z'));
expect(getSelectedForDatePicker({}, 'date').toISOString()).toEqual('2023-03-30T00:00:00.000Z');
const { selected } = getSelectedForDatePicker({}, 'date');
expect(selected.toISOString()).toEqual('2023-03-30T00:00:00.000Z');
});
test('should return the form date if it is valid', () => {
jest.useFakeTimers().setSystemTime(new Date('2023-03-30T00:00:00.000Z'));
expect(
getSelectedForDatePicker({ date: '2023-01-30T00:00:00.000Z' }, 'date').toISOString()
).toEqual('2023-01-30T00:00:00.000Z');
const { selected } = getSelectedForDatePicker({ date: '2023-01-30T00:00:00.000Z' }, 'date');
expect(selected.toISOString()).toEqual('2023-01-30T00:00:00.000Z');
});
test('should return the current date if the form date is not valid', () => {
jest.useFakeTimers().setSystemTime(new Date('2023-03-30T00:00:00.000Z'));
expect(getSelectedForDatePicker({ date: 'test' }, 'date').toISOString()).toEqual(
'2023-03-30T00:00:00.000Z'
const { selected } = getSelectedForDatePicker({ date: 'test' }, 'date');
expect(selected.toISOString()).toEqual('2023-03-30T00:00:00.000Z');
});
test('should return the current date if the form is not initialized and an offset that reflects the timezone', () => {
jest.useFakeTimers().setSystemTime(new Date('2023-03-30T00:00:00.000Z'));
const { selected, utcOffset } = getSelectedForDatePicker({}, 'date', ['America/Denver']);
expect(selected.toISOString()).toEqual('2023-03-30T00:00:00.000Z');
expect(selected.toString()).toEqual('Wed Mar 29 2023 18:00:00 GMT-0600');
expect(utcOffset).toEqual(-360);
});
test('should return the form date if it is valid and an offset that reflects the timezone', () => {
jest.useFakeTimers().setSystemTime(new Date('2023-03-30T00:00:00.000Z'));
const { selected, utcOffset } = getSelectedForDatePicker(
{ date: '2023-05-01T00:00:00.000Z' },
'date',
['America/Denver']
);
expect(selected.toISOString()).toEqual('2023-05-01T00:00:00.000Z');
expect(selected.toString()).toEqual('Sun Apr 30 2023 18:00:00 GMT-0600');
expect(utcOffset).toEqual(-360);
});
});

View file

@ -7,9 +7,15 @@
import { get } from 'lodash';
import moment, { Moment } from 'moment';
import 'moment-timezone';
import { FormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
export function getSelectedForDatePicker(form: FormData, path: string): Moment {
export function getSelectedForDatePicker(
form: FormData,
path: string,
timezone?: string[]
): { selected: Moment; utcOffset: number } {
// parse from a string date to moment() if there is an intitial value
// otherwise just get the current date
const initialValue = get(form, path);
@ -17,5 +23,18 @@ export function getSelectedForDatePicker(form: FormData, path: string): Moment {
if (initialValue && moment(initialValue).isValid()) {
selected = moment(initialValue);
}
return selected;
const utcOffset =
timezone && timezone.length > 0
? moment()
.tz(timezone[0])
.year(selected.year())
.month(selected.month())
.date(selected.date())
.hour(selected.hour())
.minute(selected.minute())
.second(selected.second())
.millisecond(selected.millisecond())
.utcOffset()
: selected.utcOffset();
return { selected: selected.clone().utcOffset(utcOffset), utcOffset };
}