mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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
This commit is contained in:
@@ -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';
|
|||||||
|
|
||||||
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 = () => {
|
|||||||
{(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);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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';
|
|||||||
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> = ({
|
|||||||
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> = ({
|
|||||||
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> = ({
|
|||||||
<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) => {
|
|||||||
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;
|
||||||
|
`,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user