diff --git a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateInput.tsx b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateInput.tsx index 0856c80dd9d8b..b109943b0a3ce 100644 --- a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateInput.tsx +++ b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateInput.tsx @@ -37,6 +37,7 @@ type DateInputProps = { readonly onClear: () => void; readonly onDateBlur?: () => void; readonly onFocus?: () => void; + readonly onKeyDown?: (event: React.KeyboardEvent) => void; readonly placeholder: string; }; @@ -51,6 +52,7 @@ export const DateInput = ({ onClear, onDateBlur, onFocus, + onKeyDown, placeholder, }: DateInputProps) => { const fieldName = inputType === "date" ? field : (`${field}Time` as const); @@ -69,6 +71,7 @@ export const DateInput = ({ onBlur={onDateBlur} onChange={handleInputChange(field, inputType)} onFocus={onFocus} + onKeyDown={onKeyDown} placeholder={placeholder} value={inputValue} w="full" diff --git a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeFilter.tsx b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeFilter.tsx index a8992f668979f..38aaddfdeb110 100644 --- a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeFilter.tsx +++ b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/DateRangeFilter.tsx @@ -39,6 +39,7 @@ export const DateRangeFilter = ({ filter, onChange, onRemove }: FilterPluginProp const hasValue = isValidFilterValue(filter.config.type, filter.value); const { + applyDateRange, editingState, endDateValue, formatDisplayValue, @@ -64,6 +65,12 @@ export const DateRangeFilter = ({ filter, onChange, onRemove }: FilterPluginProp defaultOpen={!hasValue} key={filter.id} lazyMount + onOpenChange={(details) => { + if (!details.open) { + // Submit the current editing state when the popover closes + applyDateRange(); + } + }} positioning={{ placement: "bottom-start" }} unmountOnExit > diff --git a/airflow-core/src/airflow/ui/src/hooks/useDateRangeFilter.ts b/airflow-core/src/airflow/ui/src/hooks/useDateRangeFilter.ts index a184204b4767b..068eb18361e70 100644 --- a/airflow-core/src/airflow/ui/src/hooks/useDateRangeFilter.ts +++ b/airflow-core/src/airflow/ui/src/hooks/useDateRangeFilter.ts @@ -260,20 +260,6 @@ export const useDateRangeFilter = ({ onChange, translate, value }: UseDateRangeF const newInputs = { ...prev.inputs, [inputKey]: inputValue }; const validationErrors = validateInputs(newInputs); - const dateStr = field === "start" ? newInputs.start : newInputs.end; - const timeStr = field === "start" ? newInputs.startTime : newInputs.endTime; - - if (dayjs(dateStr, DATE_INPUT_FORMAT, true).isValid()) { - const combinedDateTime = combineDateAndTime(dateStr, timeStr, selectedTimezone); - - if (Boolean(combinedDateTime)) { - onChange({ - ...value, - [field === "start" ? "startDate" : "endDate"]: combinedDateTime, - }); - } - } - return { ...prev, inputs: newInputs, @@ -318,7 +304,31 @@ export const useDateRangeFilter = ({ onChange, translate, value }: UseDateRangeF const hasValidationErrors = editingState.validationErrors.length > 0; + const applyDateRange = () => { + const { inputs } = editingState; + const errors = validateInputs(inputs); + + // Don't apply if there are validation errors + if (errors.length > 0) { + return; + } + + const startDateTime = inputs.start + ? combineDateAndTime(inputs.start, inputs.startTime, selectedTimezone) + : undefined; + const endDateTime = inputs.end + ? combineDateAndTime(inputs.end, inputs.endTime, selectedTimezone) + : undefined; + + onChange({ + ...value, + endDate: endDateTime, + startDate: startDateTime, + }); + }; + return { + applyDateRange, editingState, endDateValue, formatDisplayValue,