mirror of
https://github.com/grafana/grafana.git
synced 2025-01-19 13:03:32 -06:00
DateTimePicker: Add clearable prop (#88215)
* DateTimePicker: Add clearable prop * Docs/story * Separate func * Cleanup * Remove disabled style
This commit is contained in:
parent
2aa67c7a47
commit
69108d3cac
@ -16,7 +16,7 @@ const [date, setDate] = useState<DateTime>(dateTime('2021-05-05 12:00:00'));
|
||||
return <DateTimePicker label="Date" date={date} onChange={setDate} />;
|
||||
```
|
||||
|
||||
### With disbled hours, minutes or seconds
|
||||
### With disabled hours, minutes or seconds
|
||||
|
||||
```tsx
|
||||
import React, { useState } from 'react';
|
||||
@ -36,6 +36,10 @@ return (
|
||||
);
|
||||
```
|
||||
|
||||
### Clearable
|
||||
|
||||
You can set the `clearable` prop to `true` to enable the clearing of selected values and allow the component to have an empty value instead of the current date/time.
|
||||
|
||||
### Props
|
||||
|
||||
<ArgTypes of={DateTimePicker} />
|
||||
|
@ -27,6 +27,7 @@ const meta: Meta<typeof DateTimePicker> = {
|
||||
minDate: { control: 'date' },
|
||||
maxDate: { control: 'date' },
|
||||
showSeconds: { control: 'boolean' },
|
||||
clearable: { control: 'boolean' },
|
||||
},
|
||||
args: {
|
||||
minDate: minimumDate,
|
||||
@ -63,7 +64,7 @@ export const OnlyWorkingHoursEnabled: StoryFn<typeof DateTimePicker> = ({ label,
|
||||
);
|
||||
};
|
||||
|
||||
export const Basic: StoryFn<typeof DateTimePicker> = ({ label, minDate, maxDate, showSeconds }) => {
|
||||
export const Basic: StoryFn<typeof DateTimePicker> = ({ label, minDate, maxDate, showSeconds, clearable }) => {
|
||||
const [date, setDate] = useState<DateTime>(dateTime(today));
|
||||
// the minDate arg can change from Date object to number, we need to handle this
|
||||
// scenario to avoid a crash in the component's story.
|
||||
@ -77,6 +78,7 @@ export const Basic: StoryFn<typeof DateTimePicker> = ({ label, minDate, maxDate,
|
||||
maxDate={maxDateVal}
|
||||
date={date}
|
||||
showSeconds={showSeconds}
|
||||
clearable={clearable}
|
||||
onChange={(newValue) => {
|
||||
action('on change')(newValue);
|
||||
setDate(newValue);
|
||||
@ -89,4 +91,24 @@ Basic.args = {
|
||||
label: 'Date',
|
||||
};
|
||||
|
||||
export const Clearable: StoryFn<typeof DateTimePicker> = ({ label, showSeconds, clearable }) => {
|
||||
const [date, setDate] = useState<DateTime>(dateTime(today));
|
||||
return (
|
||||
<DateTimePicker
|
||||
label={label}
|
||||
date={date}
|
||||
showSeconds={showSeconds}
|
||||
clearable={clearable}
|
||||
onChange={(newValue) => {
|
||||
action('on change')(newValue);
|
||||
setDate(newValue);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Clearable.args = {
|
||||
clearable: true,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
@ -50,6 +50,8 @@ export interface Props {
|
||||
disabledMinutes?: () => number[];
|
||||
/** Set the seconds that can't be selected */
|
||||
disabledSeconds?: () => number[];
|
||||
/** Can input be cleared/have empty values */
|
||||
clearable?: boolean;
|
||||
}
|
||||
|
||||
export const DateTimePicker = ({
|
||||
@ -62,6 +64,7 @@ export const DateTimePicker = ({
|
||||
disabledMinutes,
|
||||
disabledSeconds,
|
||||
showSeconds = true,
|
||||
clearable = false,
|
||||
}: Props) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
|
||||
@ -131,6 +134,7 @@ export const DateTimePicker = ({
|
||||
label={label}
|
||||
ref={refs.setReference}
|
||||
showSeconds={showSeconds}
|
||||
clearable={clearable}
|
||||
/>
|
||||
{isOpen ? (
|
||||
isFullscreen ? (
|
||||
@ -203,6 +207,7 @@ interface InputProps {
|
||||
onChange: (date: DateTime) => void;
|
||||
onOpen: (event: FormEvent<HTMLElement>) => void;
|
||||
showSeconds?: boolean;
|
||||
clearable?: boolean;
|
||||
}
|
||||
|
||||
type InputState = {
|
||||
@ -211,10 +216,11 @@ type InputState = {
|
||||
};
|
||||
|
||||
const DateTimeInput = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
({ date, label, onChange, onOpen, showSeconds = true }, ref) => {
|
||||
({ date, label, onChange, onOpen, showSeconds = true, clearable = false }, ref) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
const format = showSeconds ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm';
|
||||
const [internalDate, setInternalDate] = useState<InputState>(() => {
|
||||
return { value: date ? dateTimeFormat(date) : dateTimeFormat(dateTime()), invalid: false };
|
||||
return { value: date ? dateTimeFormat(date) : !clearable ? dateTimeFormat(dateTime()) : '', invalid: false };
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@ -235,21 +241,19 @@ const DateTimeInput = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
}, []);
|
||||
|
||||
const onBlur = useCallback(() => {
|
||||
if (!internalDate.invalid) {
|
||||
if (!internalDate.invalid && internalDate.value) {
|
||||
const date = dateTimeForTimeZone(getTimeZone(), internalDate.value);
|
||||
onChange(date);
|
||||
}
|
||||
}, [internalDate, onChange]);
|
||||
|
||||
const clearInternalDate = useCallback(() => {
|
||||
setInternalDate({ value: '', invalid: false });
|
||||
}, []);
|
||||
|
||||
const icon = <Button aria-label="Time picker" icon="calendar-alt" variant="secondary" onClick={onOpen} />;
|
||||
return (
|
||||
<InlineField
|
||||
label={label}
|
||||
invalid={!!(internalDate.value && internalDate.invalid)}
|
||||
className={css({
|
||||
marginBottom: 0,
|
||||
})}
|
||||
>
|
||||
<InlineField label={label} invalid={!!(internalDate.value && internalDate.invalid)} className={styles.field}>
|
||||
<Input
|
||||
onChange={onChangeDate}
|
||||
addonAfter={icon}
|
||||
@ -258,6 +262,10 @@ const DateTimeInput = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
data-testid={Components.DateTimePicker.input}
|
||||
placeholder="Select date/time"
|
||||
ref={ref}
|
||||
suffix={
|
||||
clearable &&
|
||||
internalDate.value && <Icon name="times" className={styles.clearIcon} onClick={clearInternalDate} />
|
||||
}
|
||||
/>
|
||||
</InlineField>
|
||||
);
|
||||
@ -389,4 +397,11 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
zIndex: theme.zIndex.modal,
|
||||
maxWidth: '280px',
|
||||
}),
|
||||
clearIcon: css({
|
||||
cursor: 'pointer',
|
||||
}),
|
||||
field: css({
|
||||
marginBottom: 0,
|
||||
width: '100%',
|
||||
}),
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user