mirror of https://github.com/grafana/grafana.git
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:
parent
f93c289fed
commit
7b183971fd
|
|
@ -5,7 +5,7 @@ export interface DateTimeBuiltinFormat {
|
|||
__momentBuiltinFormatBrand: any;
|
||||
}
|
||||
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 DurationInput = string | number | DateTimeDuration;
|
||||
export type DurationUnit =
|
||||
|
|
|
|||
|
|
@ -5,6 +5,22 @@ import { TimeRangeInput } from './TimeRangeInput';
|
|||
|
||||
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
|
||||
|
||||
```jsx
|
||||
|
|
|
|||
|
|
@ -29,8 +29,31 @@ export const basic = () => {
|
|||
{(value, updateValue) => {
|
||||
return (
|
||||
<TimeRangeInput
|
||||
onChangeTimeZone={tz => action('onTimeZoneChange fired')(tz)}
|
||||
timeZone="browser"
|
||||
value={value}
|
||||
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}
|
||||
onChange={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 { GrafanaTheme, TimeRange, TimeZone } from '@grafana/data';
|
||||
import { dateTime, GrafanaTheme, TimeRange, TimeZone, dateMath } from '@grafana/data';
|
||||
import { useStyles } from '../../themes/ThemeContext';
|
||||
import { ClickOutsideWrapper } from '../ClickOutsideWrapper/ClickOutsideWrapper';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
|
|
@ -10,12 +10,24 @@ import { TimePickerButtonLabel } from './TimeRangePicker';
|
|||
import { TimePickerContent } from './TimeRangePicker/TimePickerContent';
|
||||
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 {
|
||||
value: TimeRange;
|
||||
timeZone?: TimeZone;
|
||||
onChange: (timeRange: TimeRange) => void;
|
||||
onChangeTimeZone?: (timeZone: TimeZone) => void;
|
||||
hideTimeZone?: boolean;
|
||||
placeholder?: string;
|
||||
clearable?: boolean;
|
||||
}
|
||||
|
||||
const noop = () => {};
|
||||
|
|
@ -24,8 +36,10 @@ export const TimeRangeInput: FC<Props> = ({
|
|||
value,
|
||||
onChange,
|
||||
onChangeTimeZone,
|
||||
clearable,
|
||||
hideTimeZone = true,
|
||||
timeZone = 'browser',
|
||||
placeholder = 'Select time range',
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const styles = useStyles(getStyles);
|
||||
|
|
@ -45,11 +59,26 @@ export const TimeRangeInput: FC<Props> = ({
|
|||
onChange(timeRange);
|
||||
};
|
||||
|
||||
const onRangeClear = (event: MouseEvent<HTMLDivElement>) => {
|
||||
event.stopPropagation();
|
||||
const from = dateTime(null);
|
||||
const to = dateTime(null);
|
||||
onChange({ from, to, raw: { from, to } });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<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}>
|
||||
{isValidTimeRange(value) && clearable && (
|
||||
<Icon className={styles.clearIcon} name="times" size="lg" onClick={onRangeClear} />
|
||||
)}
|
||||
<Icon name={isOpen ? 'angle-up' : 'angle-down'} size="lg" />
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -57,7 +86,7 @@ export const TimeRangeInput: FC<Props> = ({
|
|||
<ClickOutsideWrapper includeButtonPress={false} onClick={onClose}>
|
||||
<TimePickerContent
|
||||
timeZone={timeZone}
|
||||
value={value}
|
||||
value={isValidTimeRange(value) ? (value as TimeRange) : defaultTimeRange}
|
||||
onChange={onRangeChange}
|
||||
otherOptions={otherOptions}
|
||||
quickOptions={quickOptions}
|
||||
|
|
@ -100,5 +129,15 @@ const getStyles = (theme: GrafanaTheme) => {
|
|||
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;
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue