Grafana UI: Add ref to DatePickerWithInput (#104346)

Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
This commit is contained in:
Juan Cabanas
2025-04-25 09:48:32 -03:00
committed by GitHub
co-authored by Ashley Harrison
parent 06343fcda9
commit 52b120cb67
@@ -1,6 +1,6 @@
import { css } from '@emotion/css';
import { autoUpdate, flip, shift, useClick, useDismiss, useFloating, useInteractions } from '@floating-ui/react';
import { ChangeEvent, useState } from 'react';
import { ChangeEvent, forwardRef, useImperativeHandle, useState } from 'react';
import { GrafanaTheme2, dateTime } from '@grafana/data';
@@ -11,7 +11,7 @@ import { DatePicker } from '../DatePicker/DatePicker';
export const formatDate = (date: Date | string) => dateTime(date).format('L');
/** @public */
export interface DatePickerWithInputProps extends Omit<InputProps, 'ref' | 'value' | 'onChange'> {
export interface DatePickerWithInputProps extends Omit<InputProps, 'value' | 'onChange'> {
/** Value selected by the DatePicker */
value?: Date | string;
/** The minimum date the value can be set to */
@@ -27,78 +27,78 @@ export interface DatePickerWithInputProps extends Omit<InputProps, 'ref' | 'valu
}
/** @public */
export const DatePickerWithInput = ({
value,
minDate,
maxDate,
onChange,
closeOnSelect,
placeholder = 'Date',
...rest
}: DatePickerWithInputProps) => {
const [open, setOpen] = useState(false);
const styles = useStyles2(getStyles);
export const DatePickerWithInput = forwardRef<HTMLInputElement, DatePickerWithInputProps>(
({ value, minDate, maxDate, onChange, closeOnSelect, placeholder = 'Date', ...rest }, ref) => {
const [open, setOpen] = useState(false);
const styles = useStyles2(getStyles);
// the order of middleware is important!
// see https://floating-ui.com/docs/arrow#order
const middleware = [
flip({
// see https://floating-ui.com/docs/flip#combining-with-shift
crossAxis: false,
boundary: document.body,
}),
shift(),
];
// the order of middleware is important!
// see https://floating-ui.com/docs/arrow#order
const middleware = [
flip({
// see https://floating-ui.com/docs/flip#combining-with-shift
crossAxis: false,
boundary: document.body,
}),
shift(),
];
const { context, refs, floatingStyles } = useFloating({
open,
placement: 'bottom-start',
onOpenChange: setOpen,
middleware,
whileElementsMounted: autoUpdate,
strategy: 'fixed',
});
const { context, refs, floatingStyles } = useFloating<HTMLInputElement>({
open,
placement: 'bottom-start',
onOpenChange: setOpen,
middleware,
whileElementsMounted: autoUpdate,
strategy: 'fixed',
});
const click = useClick(context);
const dismiss = useDismiss(context);
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, click]);
const click = useClick(context);
const dismiss = useDismiss(context);
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, click]);
return (
<div className={styles.container}>
<Input
ref={refs.setReference}
type="text"
autoComplete={'off'}
placeholder={placeholder}
value={value ? formatDate(value) : value}
onChange={(ev: ChangeEvent<HTMLInputElement>) => {
// Allow resetting the date
if (ev.target.value === '') {
onChange('');
}
}}
className={styles.input}
{...rest}
{...getReferenceProps()}
/>
<div className={styles.popover} ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>
<DatePicker
isOpen={open}
value={value && typeof value !== 'string' ? value : dateTime().toDate()}
minDate={minDate}
maxDate={maxDate}
onChange={(ev) => {
onChange(ev);
if (closeOnSelect) {
setOpen(false);
useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(ref, () => refs.domReference.current, [
refs.domReference,
]);
return (
<div className={styles.container}>
<Input
ref={refs.setReference}
type="text"
autoComplete={'off'}
placeholder={placeholder}
value={value ? formatDate(value) : value}
onChange={(ev: ChangeEvent<HTMLInputElement>) => {
// Allow resetting the date
if (ev.target.value === '') {
onChange('');
}
}}
onClose={() => setOpen(false)}
className={styles.input}
{...rest}
{...getReferenceProps()}
/>
<div className={styles.popover} ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>
<DatePicker
isOpen={open}
value={value && typeof value !== 'string' ? value : dateTime().toDate()}
minDate={minDate}
maxDate={maxDate}
onChange={(ev) => {
onChange(ev);
if (closeOnSelect) {
setOpen(false);
}
}}
onClose={() => setOpen(false)}
/>
</div>
</div>
</div>
);
};
);
}
);
DatePickerWithInput.displayName = 'DatePickerWithInput';
const getStyles = (theme: GrafanaTheme2) => {
return {