diff --git a/packages/grafana-data/src/datetime/datemath.test.ts b/packages/grafana-data/src/datetime/datemath.test.ts index fe07179832b..6033fc3530a 100644 --- a/packages/grafana-data/src/datetime/datemath.test.ts +++ b/packages/grafana-data/src/datetime/datemath.test.ts @@ -135,4 +135,71 @@ describe('DateMath', () => { expect(date!.valueOf()).toEqual(dateTime([2014, 1, 3]).valueOf()); }); }); + + describe('Round to fiscal start/end', () => { + it('Should round to start of fiscal year when datetime is the same year as the start of the fiscal year', () => { + let date = dateMath.roundToFiscal(1, dateTime([2021, 3, 5]), 'y', false); + let expected = dateTime([2021, 1, 1]); + expect(date!.valueOf()).toEqual(expected.valueOf()); + }); + + it('Should round to start of fiscal year when datetime is the next year from the start of the fiscal year', () => { + let date = dateMath.roundToFiscal(1, dateTime([2022, 0, 2]), 'y', false); + let expected = dateTime([2021, 1, 1]); + expect(date!.valueOf()).toEqual(expected.valueOf()); + }); + + it('Should round to start of fiscal year when datetime is on a leap day', () => { + let date = dateMath.roundToFiscal(1, dateTime([2020, 1, 29]), 'y', false); + let expected = dateTime([2020, 1, 1]); + expect(date!.valueOf()).toEqual(expected.valueOf()); + }); + + it('Should round to end of fiscal year when datetime is the same year as the start of the fiscal year', () => { + let date = dateMath.roundToFiscal(1, dateTime([2021, 5, 2]), 'y', true); + let expected = dateTime([2022, 0, 1]).endOf('M'); + expect(date!.valueOf()).toEqual(expected.valueOf()); + }); + + it('Should round to end of fiscal year when datetime is the next year from the start of the fiscal year', () => { + let date = dateMath.roundToFiscal(1, dateTime([2022, 0, 1]), 'y', true); + let expected = dateTime([2022, 0, 1]).endOf('M'); + expect(date!.valueOf()).toEqual(expected.valueOf()); + }); + + it('Should round to end of fiscal year when datetime is on a leap day', () => { + let date = dateMath.roundToFiscal(1, dateTime([2020, 1, 29]), 'y', true); + let expected = dateTime([2021, 0, 1]).endOf('M'); + expect(date!.valueOf()).toEqual(expected.valueOf()); + }); + + //fq1 = 2021-02-01 - 2021-04-30 + //fq2 = 2021-05-01 - 2021-07-31 + //fq4 = 2021-08-01 - 2021-10-31 + //fq5 = 2021-11-01 - 2022-01-31 + + it('Should round to start of q2 when one month into q2', () => { + let date = dateMath.roundToFiscal(1, dateTime([2021, 6, 1]), 'Q', false); + let expected = dateTime([2021, 4, 1]); + expect(date!.valueOf()).toEqual(expected.valueOf()); + }); + + it('Should round to start of q4 when datetime is in next year from fiscal year start', () => { + let date = dateMath.roundToFiscal(1, dateTime([2022, 0, 1]), 'Q', false); + let expected = dateTime([2021, 10, 1]); + expect(date!.valueOf()).toEqual(expected.valueOf()); + }); + + it('Should round to end of q2 when one month into q2', () => { + let date = dateMath.roundToFiscal(1, dateTime([2021, 6, 1]), 'Q', true); + let expected = dateTime([2021, 6, 1]).endOf('M'); + expect(date!.valueOf()).toEqual(expected.valueOf()); + }); + + it('Should round to end of q4 when datetime is in next year from fiscal year start', () => { + let date = dateMath.roundToFiscal(1, dateTime([2022, 0, 1]), 'Q', true); + let expected = dateTime([2022, 0, 31]).endOf('M'); + expect(date!.valueOf()).toEqual(expected.valueOf()); + }); + }); }); diff --git a/packages/grafana-data/src/datetime/datemath.ts b/packages/grafana-data/src/datetime/datemath.ts index d35e1392576..99dceeb4044 100644 --- a/packages/grafana-data/src/datetime/datemath.ts +++ b/packages/grafana-data/src/datetime/datemath.ts @@ -2,7 +2,7 @@ import { includes, isDate } from 'lodash'; import { DateTime, dateTime, dateTimeForTimeZone, ISO_8601, isDateTime, DurationUnit } from './moment_wrapper'; import { TimeZone } from '../types/index'; -const units: DurationUnit[] = ['y', 'M', 'w', 'd', 'h', 'm', 's']; +const units: DurationUnit[] = ['y', 'M', 'w', 'd', 'h', 'm', 's', 'Q']; export function isMathString(text: string | DateTime | Date): boolean { if (!text) { @@ -26,7 +26,8 @@ export function isMathString(text: string | DateTime | Date): boolean { export function parse( text?: string | DateTime | Date | null, roundUp?: boolean, - timezone?: TimeZone + timezone?: TimeZone, + fiscalYearStartMonth?: number ): DateTime | undefined { if (!text) { return undefined; @@ -67,7 +68,7 @@ export function parse( return time; } - return parseDateMath(mathString, time, roundUp); + return parseDateMath(mathString, time, roundUp, fiscalYearStartMonth); } } @@ -96,7 +97,12 @@ export function isValid(text: string | DateTime): boolean { * @param roundUp If true it will round the time to endOf time unit, otherwise to startOf time unit. */ // TODO: Had to revert Andrejs `time: moment.Moment` to `time: any` -export function parseDateMath(mathString: string, time: any, roundUp?: boolean): DateTime | undefined { +export function parseDateMath( + mathString: string, + time: any, + roundUp?: boolean, + fiscalYearStartMonth = 0 +): DateTime | undefined { const strippedMathString = mathString.replace(/\s/g, ''); const dateTime = time; let i = 0; @@ -107,6 +113,7 @@ export function parseDateMath(mathString: string, time: any, roundUp?: boolean): let type; let num; let unit; + let isFiscal = false; if (c === '/') { type = 0; @@ -121,7 +128,7 @@ export function parseDateMath(mathString: string, time: any, roundUp?: boolean): if (isNaN(parseInt(strippedMathString.charAt(i), 10))) { num = 1; } else if (strippedMathString.length === 2) { - num = strippedMathString.charAt(i); + num = parseInt(strippedMathString.charAt(i), 10); } else { const numFrom = i; while (!isNaN(parseInt(strippedMathString.charAt(i), 10))) { @@ -141,14 +148,27 @@ export function parseDateMath(mathString: string, time: any, roundUp?: boolean): } unit = strippedMathString.charAt(i++); + if (unit === 'f') { + unit = strippedMathString.charAt(i++); + isFiscal = true; + } + if (!includes(units, unit)) { return undefined; } else { if (type === 0) { if (roundUp) { - dateTime.endOf(unit); + if (isFiscal) { + roundToFiscal(fiscalYearStartMonth, dateTime, unit, roundUp); + } else { + dateTime.endOf(unit); + } } else { - dateTime.startOf(unit); + if (isFiscal) { + roundToFiscal(fiscalYearStartMonth, dateTime, unit, roundUp); + } else { + dateTime.startOf(unit); + } } } else if (type === 1) { dateTime.add(num, unit); @@ -159,3 +179,24 @@ export function parseDateMath(mathString: string, time: any, roundUp?: boolean): } return dateTime; } + +export function roundToFiscal(fyStartMonth: number, dateTime: any, unit: string, roundUp: boolean | undefined) { + switch (unit) { + case 'y': + if (roundUp) { + roundToFiscal(fyStartMonth, dateTime, unit, false).add(11, 'M').endOf('M'); + } else { + dateTime.subtract((dateTime.month() - fyStartMonth + 12) % 12, 'M').startOf('M'); + } + return dateTime; + case 'Q': + if (roundUp) { + roundToFiscal(fyStartMonth, dateTime, unit, false).add(2, 'M').endOf('M'); + } else { + dateTime.subtract((dateTime.month() - fyStartMonth + 3) % 3, 'M').startOf('M'); + } + return dateTime; + default: + return undefined; + } +} diff --git a/packages/grafana-data/src/datetime/parser.ts b/packages/grafana-data/src/datetime/parser.ts index 8efabe579bb..26901deaaee 100644 --- a/packages/grafana-data/src/datetime/parser.ts +++ b/packages/grafana-data/src/datetime/parser.ts @@ -19,6 +19,7 @@ export interface DateTimeOptionsWhenParsing extends DateTimeOptions { * the returned DateTime value will be 06:00:00. */ roundUp?: boolean; + fiscalYearStartMonth?: number; } type DateTimeParser = (value: DateTimeInput, options?: T) => DateTime; @@ -56,7 +57,7 @@ const parseString = (value: string, options?: DateTimeOptionsWhenParsing): DateT return moment() as DateTime; } - const parsed = parse(value, options?.roundUp, options?.timeZone); + const parsed = parse(value, options?.roundUp, options?.timeZone, options?.fiscalYearStartMonth); return parsed || (moment() as DateTime); } diff --git a/packages/grafana-data/src/datetime/rangeutil.ts b/packages/grafana-data/src/datetime/rangeutil.ts index 15b52d443cb..16e34be35ea 100644 --- a/packages/grafana-data/src/datetime/rangeutil.ts +++ b/packages/grafana-data/src/datetime/rangeutil.ts @@ -40,7 +40,9 @@ const rangeOptions: TimeOption[] = [ }, { from: 'now-1w/w', to: 'now-1w/w', display: 'Previous week' }, { from: 'now-1M/M', to: 'now-1M/M', display: 'Previous month' }, + { from: 'now-1Q/fQ', to: 'now-1Q/fQ', display: 'Previous fiscal quarter' }, { from: 'now-1y/y', to: 'now-1y/y', display: 'Previous year' }, + { from: 'now-1y/fy', to: 'now-1y/fy', display: 'Previous fiscal year' }, { from: 'now-5m', to: 'now', display: 'Last 5 minutes' }, { from: 'now-15m', to: 'now', display: 'Last 15 minutes' }, @@ -58,6 +60,10 @@ const rangeOptions: TimeOption[] = [ { from: 'now-1y', to: 'now', display: 'Last 1 year' }, { from: 'now-2y', to: 'now', display: 'Last 2 years' }, { from: 'now-5y', to: 'now', display: 'Last 5 years' }, + { from: 'now/fQ', to: 'now', display: 'This fiscal quarter so far' }, + { from: 'now/fQ', to: 'now/fQ', display: 'This fiscal quarter' }, + { from: 'now/fy', to: 'now', display: 'This fiscal year so far' }, + { from: 'now/fy', to: 'now/fy', display: 'This fiscal year' }, ]; const hiddenRangeOptions: TimeOption[] = [ @@ -192,9 +198,9 @@ export const describeTimeRangeAbbreviation = (range: TimeRange, timeZone?: TimeZ return parsed ? timeZoneAbbrevation(parsed, { timeZone }) : ''; }; -export const convertRawToRange = (raw: RawTimeRange, timeZone?: TimeZone): TimeRange => { - const from = dateTimeParse(raw.from, { roundUp: false, timeZone }); - const to = dateTimeParse(raw.to, { roundUp: true, timeZone }); +export const convertRawToRange = (raw: RawTimeRange, timeZone?: TimeZone, fiscalYearStartMonth?: number): TimeRange => { + const from = dateTimeParse(raw.from, { roundUp: false, timeZone, fiscalYearStartMonth }); + const to = dateTimeParse(raw.to, { roundUp: true, timeZone, fiscalYearStartMonth }); if (dateMath.isMathString(raw.from) || dateMath.isMathString(raw.to)) { return { from, to, raw }; @@ -210,6 +216,15 @@ function isRelativeTime(v: DateTime | string) { return false; } +export function isFiscal(timeRange: TimeRange) { + if (typeof timeRange.raw.from === 'string' && timeRange.raw.from.indexOf('f') > 0) { + return true; + } else if (typeof timeRange.raw.to === 'string' && timeRange.raw.to.indexOf('f') > 0) { + return true; + } + return false; +} + export function isRelativeTimeRange(raw: RawTimeRange): boolean { return isRelativeTime(raw.from) || isRelativeTime(raw.to); } diff --git a/packages/grafana-e2e/src/flows/setTimeRange.ts b/packages/grafana-e2e/src/flows/setTimeRange.ts index 33c20c73844..8f79bc1d6c4 100644 --- a/packages/grafana-e2e/src/flows/setTimeRange.ts +++ b/packages/grafana-e2e/src/flows/setTimeRange.ts @@ -11,10 +11,10 @@ export const setTimeRange = ({ from, to, zone }: TimeRangeConfig) => { e2e.components.TimePicker.openButton().click(); if (zone) { - e2e().contains('button', 'Change time zone').click(); + e2e().contains('button', 'Change time settings').click(); selectOption({ - clickToOpen: false, + clickToOpen: true, container: e2e.components.TimeZonePicker.container(), optionText: zone, }); diff --git a/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.tsx b/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.tsx index 0357d4e0bbb..939ac33c704 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.tsx @@ -5,7 +5,7 @@ import { useStyles2 } from '../../../themes'; import { Button } from '../../Button'; import { ClickOutsideWrapper } from '../../ClickOutsideWrapper/ClickOutsideWrapper'; import { TimeRangeList } from '../TimeRangePicker/TimeRangeList'; -import { quickOptions } from '../rangeOptions'; +import { quickOptions } from '../options'; import CustomScrollbar from '../../CustomScrollbar/CustomScrollbar'; import { TimePickerTitle } from '../TimeRangePicker/TimePickerTitle'; import { diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.tsx index f9a84698b66..f946c588b25 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangeInput.tsx @@ -7,7 +7,7 @@ import { Icon } from '../Icon/Icon'; import { getInputStyles } from '../Input/Input'; import { TimePickerButtonLabel } from './TimeRangePicker'; import { TimePickerContent } from './TimeRangePicker/TimePickerContent'; -import { otherOptions, quickOptions } from './rangeOptions'; +import { quickOptions } from './options'; import { selectors } from '@grafana/e2e-selectors'; import { stylesFactory } from '../../themes'; @@ -100,7 +100,6 @@ export const TimeRangeInput: FC = ({ timeZone={timeZone} value={isValidTimeRange(value) ? (value as TimeRange) : getDefaultTimeRange()} onChange={onRangeChange} - otherOptions={otherOptions} quickOptions={quickOptions} onChangeTimeZone={onChangeTimeZone} className={styles.content} diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx index 0137df6d558..16e97daec96 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker.tsx @@ -23,7 +23,7 @@ import { dateMath, } from '@grafana/data'; import { Themeable } from '../../types'; -import { otherOptions, quickOptions } from './rangeOptions'; +import { quickOptions } from './options'; import { ButtonGroup, ToolbarButton } from '../Button'; import { selectors } from '@grafana/e2e-selectors'; @@ -32,10 +32,12 @@ export interface TimeRangePickerProps extends Themeable { hideText?: boolean; value: TimeRange; timeZone?: TimeZone; + fiscalYearStartMonth?: number; timeSyncButton?: JSX.Element; isSynced?: boolean; onChange: (timeRange: TimeRange) => void; onChangeTimeZone: (timeZone: TimeZone) => void; + onChangeFiscalYearStartMonth?: (month: number) => void; onMoveBackward: () => void; onMoveForward: () => void; onZoom: () => void; @@ -75,11 +77,13 @@ export class UnthemedTimeRangePicker extends PureComponent diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerContent.test.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerContent.test.tsx index 329f24e5a5b..f77402fef87 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerContent.test.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerContent.test.tsx @@ -38,14 +38,12 @@ describe('TimePickerContent', () => { it('renders with relative picker', () => { renderComponent({ value: absoluteValue }); - expect(screen.queryByText(/relative time ranges/i)).toBeInTheDocument(); - expect(screen.queryByText(/other quick ranges/i)).toBeInTheDocument(); + expect(screen.queryByText(/Last 5 minutes/i)).toBeInTheDocument(); }); it('renders without relative picker', () => { renderComponent({ value: absoluteValue, hideQuickRanges: true }); - expect(screen.queryByText(/relative time ranges/i)).not.toBeInTheDocument(); - expect(screen.queryByText(/other quick ranges/i)).not.toBeInTheDocument(); + expect(screen.queryByText(/Last 5 minutes/i)).not.toBeInTheDocument(); }); it('renders with timezone picker', () => { @@ -86,14 +84,12 @@ describe('TimePickerContent', () => { it('renders with relative picker', () => { renderComponent({ value: absoluteValue, isFullscreen: false }); - expect(screen.queryByText(/relative time ranges/i)).toBeInTheDocument(); - expect(screen.queryByText(/other quick ranges/i)).toBeInTheDocument(); + expect(screen.queryByText(/Last 5 minutes/i)).toBeInTheDocument(); }); it('renders without relative picker', () => { renderComponent({ value: absoluteValue, isFullscreen: false, hideQuickRanges: true }); - expect(screen.queryByText(/relative time ranges/i)).not.toBeInTheDocument(); - expect(screen.queryByText(/other quick ranges/i)).not.toBeInTheDocument(); + expect(screen.queryByText(/Last 5 minutes/i)).not.toBeInTheDocument(); }); it('renders with absolute picker when absolute value and quick ranges are visible', () => { @@ -139,6 +135,10 @@ function renderComponent({ { return { @@ -46,11 +47,15 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, isReversed, hideQuickRang rightSide: css` width: 40% !important; border-right: ${isReversed ? `1px solid ${theme.colors.border.weak}` : 'none'}; - + display: flex; + flex-direction: column; @media only screen and (max-width: ${theme.breakpoints.values.lg}px) { width: 100% !important; } `, + timeRangeFilter: css` + padding: ${theme.spacing(1)}; + `, spacing: css` margin-top: 16px; `, @@ -127,9 +132,10 @@ interface Props { value: TimeRange; onChange: (timeRange: TimeRange) => void; onChangeTimeZone: (timeZone: TimeZone) => void; + onChangeFiscalYearStartMonth?: (month: number) => void; timeZone?: TimeZone; + fiscalYearStartMonth?: number; quickOptions?: TimeOption[]; - otherOptions?: TimeOption[]; history?: TimeRange[]; showHistory?: boolean; className?: string; @@ -150,11 +156,11 @@ interface FormProps extends Omit { export const TimePickerContentWithScreenSize: React.FC = (props) => { const { quickOptions = [], - otherOptions = [], isReversed, isFullscreen, hideQuickRanges, timeZone, + fiscalYearStartMonth, value, onChange, history, @@ -162,6 +168,7 @@ export const TimePickerContentWithScreenSize: React.FC = (p className, hideTimeZone, onChangeTimeZone, + onChangeFiscalYearStartMonth, } = props; const isHistoryEmpty = !history?.length; const isContainerTall = @@ -169,7 +176,10 @@ export const TimePickerContentWithScreenSize: React.FC = (p const theme = useTheme2(); const styles = getStyles(theme, isReversed, hideQuickRanges, isContainerTall); const historyOptions = mapToHistoryOptions(history, timeZone); - const timeOption = useTimeOption(value.raw, otherOptions, quickOptions); + const timeOption = useTimeOption(value.raw, quickOptions); + const [searchTerm, setSearchQuery] = useState(''); + + const filteredQuickOptions = quickOptions.filter((o) => o.display.toLowerCase().includes(searchTerm.toLowerCase())); const onChangeTimeOption = (timeOption: TimeOption) => { return onChange(mapOptionToTimeRange(timeOption)); @@ -179,26 +189,23 @@ export const TimePickerContentWithScreenSize: React.FC = (p
{(!isFullscreen || !hideQuickRanges) && ( - - {!isFullscreen && } - {!hideQuickRanges && ( - <> - -
- - - )} - +
+
+ +
+ + {!isFullscreen && } + {!hideQuickRanges && ( + + )} + +
)} {isFullscreen && (
@@ -206,7 +213,14 @@ export const TimePickerContentWithScreenSize: React.FC = (p
)}
- {!hideTimeZone && isFullscreen && } + {!hideTimeZone && isFullscreen && ( + + )}
); }; @@ -268,7 +282,7 @@ const NarrowScreenForm: React.FC = (props) => { }; const FullScreenForm: React.FC = (props) => { - const { onChange } = props; + const { onChange, value, timeZone, fiscalYearStartMonth, isReversed, historyOptions } = props; const theme = useTheme2(); const styles = getFullScreenStyles(theme, props.hideQuickRanges); const onChangeTimeOption = (timeOption: TimeOption) => { @@ -282,18 +296,19 @@ const FullScreenForm: React.FC = (props) => { Absolute time range
{props.showHistory && (
} /> @@ -338,23 +353,13 @@ function mapToHistoryOptions(ranges?: TimeRange[], timeZone?: TimeZone): TimeOpt EmptyRecentList.displayName = 'EmptyRecentList'; -const useTimeOption = ( - raw: RawTimeRange, - quickOptions: TimeOption[], - otherOptions: TimeOption[] -): TimeOption | undefined => { +const useTimeOption = (raw: RawTimeRange, quickOptions: TimeOption[]): TimeOption | undefined => { return useMemo(() => { if (!rangeUtil.isRelativeTimeRange(raw)) { return; } - const quickOption = quickOptions.find((option) => { + return quickOptions.find((option) => { return option.from === raw.from && option.to === raw.to; }); - if (quickOption) { - return quickOption; - } - return otherOptions.find((option) => { - return option.from === raw.from && option.to === raw.to; - }); - }, [raw, otherOptions, quickOptions]); + }, [raw, quickOptions]); }; diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerFooter.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerFooter.tsx index 1dcceee3f20..242c92bd054 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerFooter.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerFooter.tsx @@ -9,18 +9,29 @@ import { Button } from '../../Button'; import { TimeZonePicker } from '../TimeZonePicker'; import { isString } from 'lodash'; import { selectors } from '@grafana/e2e-selectors'; +import { Field, RadioButtonGroup, Select } from '../..'; +import { monthOptions } from '../options'; interface Props { timeZone?: TimeZone; + fiscalYearStartMonth?: number; timestamp?: number; onChangeTimeZone: (timeZone: TimeZone) => void; + onChangeFiscalYearStartMonth?: (month: number) => void; } export const TimePickerFooter: FC = (props) => { - const { timeZone, timestamp = Date.now(), onChangeTimeZone } = props; + const { + timeZone, + fiscalYearStartMonth, + timestamp = Date.now(), + onChangeTimeZone, + onChangeFiscalYearStartMonth, + } = props; const [isEditing, setEditing] = useState(false); + const [editMode, setEditMode] = useState('tz'); - const onToggleChangeTz = useCallback( + const onToggleChangeTimeSettings = useCallback( (event?: React.MouseEvent) => { if (event) { event.stopPropagation(); @@ -43,42 +54,72 @@ export const TimePickerFooter: FC = (props) => { return null; } - if (isEditing) { - return ( -
-
- { - onToggleChangeTz(); - - if (isString(timeZone)) { - onChangeTimeZone(timeZone); - } - }} - autoFocus={true} - onBlur={onToggleChangeTz} - /> -
-
- ); - } - return ( -
-
-
- -
- +
+
+
+
+ +
+ +
+
- -
-
- -
+
+ +
+ {isEditing ? ( +
+
+ +
+ {editMode === 'tz' ? ( +
+ { + onToggleChangeTimeSettings(); + + if (isString(timeZone)) { + onChangeTimeZone(timeZone); + } + }} + onBlur={onToggleChangeTimeSettings} + /> +
+ ) : ( +
+ + event.stopPropagation()} - onFocus={onFocus} - onChange={(event) => onChange(event.currentTarget.value, to.value)} - addonAfter={icon} - aria-label={selectors.components.TimePicker.fromField} - value={from.value} - /> +
+ event.stopPropagation()} + onFocus={onFocus} + onChange={(event) => onChange(event.currentTarget.value, to.value)} + addonAfter={icon} + aria-label={selectors.components.TimePicker.fromField} + value={from.value} + /> + {fyTooltip} +
- event.stopPropagation()} - onFocus={onFocus} - onChange={(event) => onChange(from.value, event.currentTarget.value)} - addonAfter={icon} - aria-label={selectors.components.TimePicker.toField} - value={to.value} - /> +
+ event.stopPropagation()} + onFocus={onFocus} + onChange={(event) => onChange(from.value, event.currentTarget.value)} + addonAfter={icon} + aria-label={selectors.components.TimePicker.toField} + value={to.value} + /> + {fyTooltip} +