Commit 7b183971 by Alex Khomenko Committed by GitHub

Grafana-UI: Enable empty time range (#26320)

* Grafana-UI: Enable empty time range

* Grafana-UI: Add clearable prop

* Grafana-UI: Update types

* Grafana-UI: Use InputTimeRange type

* Grafana-UI: Remove InputTimeRange type

* Grafana-UI: Fix clear icon hover color
parent f93c289f
...@@ -5,7 +5,7 @@ export interface DateTimeBuiltinFormat { ...@@ -5,7 +5,7 @@ export interface DateTimeBuiltinFormat {
__momentBuiltinFormatBrand: any; __momentBuiltinFormatBrand: any;
} }
export const ISO_8601: DateTimeBuiltinFormat = moment.ISO_8601; export const ISO_8601: DateTimeBuiltinFormat = moment.ISO_8601;
export type DateTimeInput = Date | string | number | Array<string | number> | DateTime; // null | undefined export type DateTimeInput = Date | string | number | Array<string | number> | DateTime | null; // | undefined;
export type FormatInput = string | DateTimeBuiltinFormat | undefined; export type FormatInput = string | DateTimeBuiltinFormat | undefined;
export type DurationInput = string | number | DateTimeDuration; export type DurationInput = string | number | DateTimeDuration;
export type DurationUnit = export type DurationUnit =
......
...@@ -5,6 +5,22 @@ import { TimeRangeInput } from './TimeRangeInput'; ...@@ -5,6 +5,22 @@ import { TimeRangeInput } from './TimeRangeInput';
A variant of `TimeRangePicker` for use in forms. A variant of `TimeRangePicker` for use in forms.
`dateTime(null)` can be used to provide empty time range value. The shape of the return value on input clear is:
```javascript
{
from: dateTime(null),
to: dateTime(null),
raw: {
from: dateTime(null),
to: dateTime(null),
},
};
```
`dateMath.isValid()` from `@grafana/data` can be used to check for a valid time range value.
### Usage ### Usage
```jsx ```jsx
......
...@@ -29,8 +29,31 @@ export const basic = () => { ...@@ -29,8 +29,31 @@ export const basic = () => {
{(value, updateValue) => { {(value, updateValue) => {
return ( return (
<TimeRangeInput <TimeRangeInput
onChangeTimeZone={tz => action('onTimeZoneChange fired')(tz)} value={value}
timeZone="browser" onChange={timeRange => {
action('onChange fired')(timeRange);
updateValue(timeRange);
}}
/>
);
}}
</UseState>
);
};
export const clearable = () => {
return (
<UseState
initialState={{
from: dateTime(),
to: dateTime(),
raw: { from: 'now-6h' as TimeFragment, to: 'now' as TimeFragment },
}}
>
{(value, updateValue) => {
return (
<TimeRangeInput
clearable
value={value} value={value}
onChange={timeRange => { onChange={timeRange => {
action('onChange fired')(timeRange); action('onChange fired')(timeRange);
......
import React, { FC, FormEvent, useState } from 'react'; import React, { FC, FormEvent, MouseEvent, useState } from 'react';
import { css, cx } from 'emotion'; import { css, cx } from 'emotion';
import { GrafanaTheme, TimeRange, TimeZone } from '@grafana/data'; import { dateTime, GrafanaTheme, TimeRange, TimeZone, dateMath } from '@grafana/data';
import { useStyles } from '../../themes/ThemeContext'; import { useStyles } from '../../themes/ThemeContext';
import { ClickOutsideWrapper } from '../ClickOutsideWrapper/ClickOutsideWrapper'; import { ClickOutsideWrapper } from '../ClickOutsideWrapper/ClickOutsideWrapper';
import { Icon } from '../Icon/Icon'; import { Icon } from '../Icon/Icon';
...@@ -10,12 +10,24 @@ import { TimePickerButtonLabel } from './TimeRangePicker'; ...@@ -10,12 +10,24 @@ import { TimePickerButtonLabel } from './TimeRangePicker';
import { TimePickerContent } from './TimeRangePicker/TimePickerContent'; import { TimePickerContent } from './TimeRangePicker/TimePickerContent';
import { otherOptions, quickOptions } from './rangeOptions'; import { otherOptions, quickOptions } from './rangeOptions';
export const defaultTimeRange: TimeRange = {
from: dateTime().subtract(6, 'hour'),
to: dateTime(),
raw: { from: 'now-6h', to: 'now' },
};
const isValidTimeRange = (range: any) => {
return dateMath.isValid(range.from) && dateMath.isValid(range.to);
};
export interface Props { export interface Props {
value: TimeRange; value: TimeRange;
timeZone?: TimeZone; timeZone?: TimeZone;
onChange: (timeRange: TimeRange) => void; onChange: (timeRange: TimeRange) => void;
onChangeTimeZone?: (timeZone: TimeZone) => void; onChangeTimeZone?: (timeZone: TimeZone) => void;
hideTimeZone?: boolean; hideTimeZone?: boolean;
placeholder?: string;
clearable?: boolean;
} }
const noop = () => {}; const noop = () => {};
...@@ -24,8 +36,10 @@ export const TimeRangeInput: FC<Props> = ({ ...@@ -24,8 +36,10 @@ export const TimeRangeInput: FC<Props> = ({
value, value,
onChange, onChange,
onChangeTimeZone, onChangeTimeZone,
clearable,
hideTimeZone = true, hideTimeZone = true,
timeZone = 'browser', timeZone = 'browser',
placeholder = 'Select time range',
}) => { }) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const styles = useStyles(getStyles); const styles = useStyles(getStyles);
...@@ -45,11 +59,26 @@ export const TimeRangeInput: FC<Props> = ({ ...@@ -45,11 +59,26 @@ export const TimeRangeInput: FC<Props> = ({
onChange(timeRange); onChange(timeRange);
}; };
const onRangeClear = (event: MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
const from = dateTime(null);
const to = dateTime(null);
onChange({ from, to, raw: { from, to } });
};
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div tabIndex={0} className={styles.pickerInput} aria-label="TimePicker Open Button" onClick={onOpen}> <div tabIndex={0} className={styles.pickerInput} aria-label="TimePicker Open Button" onClick={onOpen}>
<TimePickerButtonLabel value={value} /> {isValidTimeRange(value) ? (
<TimePickerButtonLabel value={value as TimeRange} />
) : (
<span className={styles.placeholder}>{placeholder}</span>
)}
<span className={styles.caretIcon}> <span className={styles.caretIcon}>
{isValidTimeRange(value) && clearable && (
<Icon className={styles.clearIcon} name="times" size="lg" onClick={onRangeClear} />
)}
<Icon name={isOpen ? 'angle-up' : 'angle-down'} size="lg" /> <Icon name={isOpen ? 'angle-up' : 'angle-down'} size="lg" />
</span> </span>
</div> </div>
...@@ -57,7 +86,7 @@ export const TimeRangeInput: FC<Props> = ({ ...@@ -57,7 +86,7 @@ export const TimeRangeInput: FC<Props> = ({
<ClickOutsideWrapper includeButtonPress={false} onClick={onClose}> <ClickOutsideWrapper includeButtonPress={false} onClick={onClose}>
<TimePickerContent <TimePickerContent
timeZone={timeZone} timeZone={timeZone}
value={value} value={isValidTimeRange(value) ? (value as TimeRange) : defaultTimeRange}
onChange={onRangeChange} onChange={onRangeChange}
otherOptions={otherOptions} otherOptions={otherOptions}
quickOptions={quickOptions} quickOptions={quickOptions}
...@@ -100,5 +129,15 @@ const getStyles = (theme: GrafanaTheme) => { ...@@ -100,5 +129,15 @@ const getStyles = (theme: GrafanaTheme) => {
margin-left: ${theme.spacing.xs}; margin-left: ${theme.spacing.xs};
` `
), ),
clearIcon: css`
margin-right: ${theme.spacing.xs};
&:hover {
color: ${theme.colors.linkHover};
}
`,
placeholder: css`
color: ${theme.colors.formInputPlaceholderText};
opacity: 1;
`,
}; };
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment