Fix/6885 crash when rule has empty date field (#6905)

* Fix crash when rule date field loses focus while empty

Fixes #6885

* Remove ts-strict-ignore and fix types in DateSelect

* Generate release note 6905
This commit is contained in:
Gabriel J.
2026-02-11 00:05:37 +01:00
committed by GitHub
parent edcf893a27
commit 078da08ad5
4 changed files with 55 additions and 23 deletions

View File

@@ -39,6 +39,7 @@
"@testing-library/react": "16.3.0",
"@testing-library/user-event": "14.6.1",
"@types/lodash": "^4",
"@types/pikaday": "^1.7.10",
"@types/promise-retry": "^1.1.6",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",

View File

@@ -1,5 +1,4 @@
// @ts-strict-ignore
import React, {
import {
forwardRef,
useEffect,
useEffectEvent,
@@ -9,7 +8,13 @@ import React, {
useRef,
useState,
} from 'react';
import type { ComponentProps, KeyboardEvent, Ref } from 'react';
import type {
ChangeEvent,
ComponentProps,
JSX,
KeyboardEvent,
Ref,
} from 'react';
import { useResponsive } from '@actual-app/components/hooks/useResponsive';
import { Input } from '@actual-app/components/input';
@@ -122,8 +127,8 @@ type DatePickerProps = {
value: string;
firstDayOfWeekIdx: string;
dateFormat: string;
onUpdate?: (selectedDate: Date) => void;
onSelect: (selectedDate: Date | null) => void;
onUpdate: (selectedDate: Date) => void;
onSelect: (selectedDate: Date) => void;
};
type DatePickerForwardedRef = {
@@ -132,8 +137,8 @@ type DatePickerForwardedRef = {
const DatePicker = forwardRef<DatePickerForwardedRef, DatePickerProps>(
({ value, firstDayOfWeekIdx, dateFormat, onUpdate, onSelect }, ref) => {
const locale = useLocale();
const picker = useRef(null);
const mountPoint = useRef(null);
const picker = useRef<Pikaday | null>(null);
const mountPoint = useRef<HTMLDivElement | null>(null);
const onUpdateEffect = useEffectEvent(onUpdate);
@@ -141,29 +146,32 @@ const DatePicker = forwardRef<DatePickerForwardedRef, DatePickerProps>(
ref,
() => ({
handleInputKeyDown(e) {
const currentDate = picker.current?.getDate();
if (!currentDate) return;
let newDate = null;
switch (e.key) {
case 'ArrowLeft':
e.preventDefault();
newDate = subDays(picker.current.getDate(), 1);
newDate = subDays(currentDate, 1);
break;
case 'ArrowUp':
e.preventDefault();
newDate = subDays(picker.current.getDate(), 7);
newDate = subDays(currentDate, 7);
break;
case 'ArrowRight':
e.preventDefault();
newDate = addDays(picker.current.getDate(), 1);
newDate = addDays(currentDate, 1);
break;
case 'ArrowDown':
e.preventDefault();
newDate = addDays(picker.current.getDate(), 7);
newDate = addDays(currentDate, 7);
break;
default:
}
if (newDate) {
picker.current.setDate(newDate, true);
picker.current?.setDate(newDate, true);
onUpdateEffect?.(newDate);
}
},
@@ -194,16 +202,16 @@ const DatePicker = forwardRef<DatePickerForwardedRef, DatePickerProps>(
useLayoutEffect(() => {
picker.current = initPikaday();
mountPoint.current.appendChild(picker.current.el);
mountPoint.current?.appendChild(picker.current.el);
return () => {
picker.current.destroy();
picker.current?.destroy();
};
}, []);
useEffect(() => {
if (value && picker.current.getDate() !== value) {
picker.current.setDate(parse(value, dateFormat, new Date()), true);
if (value) {
picker.current?.setDate(parse(value, dateFormat, new Date()), true);
}
}, [value, dateFormat]);
@@ -218,7 +226,7 @@ const DatePicker = forwardRef<DatePickerForwardedRef, DatePickerProps>(
DatePicker.displayName = 'DatePicker';
function defaultShouldSaveFromKey(e) {
function defaultShouldSaveFromKey(e: KeyboardEvent<HTMLInputElement>) {
return e.key === 'Enter';
}
@@ -263,7 +271,7 @@ function DateSelectDesktop({
return '';
}, [defaultValue, dateFormat]);
const picker = useRef(null);
const picker = useRef<DatePickerForwardedRef | null>(null);
const [value, setValue] = useState(parsedDefaultValue);
const [open, setOpen] = useState(embedded || isOpen || false);
const innerRef = useRef<HTMLInputElement | null>(null);
@@ -315,7 +323,7 @@ function DateSelectDesktop({
!e.altKey &&
open
) {
picker.current.handleInputKeyDown(e);
picker.current?.handleInputKeyDown(e);
} else if (e.key === 'Escape') {
setValue(parsedDefaultValue);
setSelectedValue(parsedDefaultValue);
@@ -357,11 +365,11 @@ function DateSelectDesktop({
}
}
function onChange(e) {
function onChange(e: ChangeEvent<HTMLInputElement>) {
setValue(e.target.value);
}
const maybeWrapTooltip = content => {
const maybeWrapTooltip = (content: JSX.Element) => {
if (embedded) {
return open ? content : null;
}
@@ -413,8 +421,8 @@ function DateSelectDesktop({
// Otherwise the input is reset to whatever is already
// selected
if (value === '') {
setSelectedValue(null);
onSelect(null);
setSelectedValue('');
onSelect('');
} else {
setValue(selectedValue || '');

View File

@@ -0,0 +1,6 @@
---
category: Bugfixes
authors: [rznn7]
---
Fix crash when setting a rule date field and leaving it empty

View File

@@ -161,6 +161,7 @@ __metadata:
"@testing-library/react": "npm:16.3.0"
"@testing-library/user-event": "npm:14.6.1"
"@types/lodash": "npm:^4"
"@types/pikaday": "npm:^1.7.10"
"@types/promise-retry": "npm:^1.1.6"
"@types/react": "npm:^19.2.5"
"@types/react-dom": "npm:^19.2.3"
@@ -9431,6 +9432,15 @@ __metadata:
languageName: node
linkType: hard
"@types/pikaday@npm:^1.7.10":
version: 1.7.10
resolution: "@types/pikaday@npm:1.7.10"
dependencies:
moment: "npm:>=2.29.2"
checksum: 10/638bc31725f4663c6266bb527d34d08740d8dc98c1c71efe53ef5bc63e177718e960f96f2719ee97f18ec7caf15e9f70d225e07f4d9ae1235384bb6da31a34a7
languageName: node
linkType: hard
"@types/plist@npm:^3.0.1":
version: 3.0.5
resolution: "@types/plist@npm:3.0.5"
@@ -20768,6 +20778,13 @@ __metadata:
languageName: node
linkType: hard
"moment@npm:>=2.29.2":
version: 2.30.1
resolution: "moment@npm:2.30.1"
checksum: 10/ae42d876d4ec831ef66110bdc302c0657c664991e45cf2afffc4b0f6cd6d251dde11375c982a5c0564ccc0fa593fc564576ddceb8c8845e87c15f58aa6baca69
languageName: node
linkType: hard
"mrmime@npm:^2.0.0":
version: 2.0.1
resolution: "mrmime@npm:2.0.1"