Add placeholder, empty-value, and onConfirm support to DateField (#10993)

Co-authored-by: DS Boyce <260543580+ds-boyce@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Eric Bailey
2026-06-25 18:04:54 -05:00
committed by GitHub
parent fcf17a482f
commit 308693762c
7 changed files with 162 additions and 18 deletions
+33 -4
View File
@@ -1,4 +1,4 @@
import {useCallback, useImperativeHandle} from 'react'
import {useCallback, useImperativeHandle, useState} from 'react'
import {Keyboard, View} from 'react-native'
import DatePicker from 'react-native-date-picker'
import {msg} from '@lingui/core/macro'
@@ -28,6 +28,8 @@ export function DateField({
value,
inputRef,
onChangeDate,
onConfirm,
placeholder,
testID,
label,
isInvalid,
@@ -38,10 +40,23 @@ export function DateField({
const t = useTheme()
const control = Dialog.useDialogControl()
/*
* The picker requires a valid date, so when value is empty we fall back to
* maximumDate (if set) or today. Draft state lets the picker scroll even when
* the parent does not echo value back (e.g. a clearable field).
*/
const fallbackDate = maximumDate
? toSimpleDateString(maximumDate)
: toSimpleDateString(new Date())
const [draft, setDraft] = useState(() =>
value === '' ? fallbackDate : toSimpleDateString(value),
)
const onChangeInternal = useCallback(
(date: Date | undefined) => {
if (date) {
const formatted = toSimpleDateString(date)
setDraft(formatted)
onChangeDate(formatted)
}
},
@@ -53,13 +68,14 @@ export function DateField({
() => ({
focus: () => {
Keyboard.dismiss()
setDraft(value === '' ? fallbackDate : toSimpleDateString(value))
control.open()
},
blur: () => {
control.close()
},
}),
[control],
[control, value, fallbackDate],
)
return (
@@ -67,8 +83,10 @@ export function DateField({
<DateFieldButton
label={label}
value={value}
placeholder={placeholder}
onPress={() => {
Keyboard.dismiss()
setDraft(value === '' ? fallbackDate : toSimpleDateString(value))
control.open()
}}
isInvalid={isInvalid}
@@ -85,7 +103,7 @@ export function DateField({
<DatePicker
timeZoneOffsetInMinutes={0}
theme={t.scheme}
date={new Date(toSimpleDateString(value))}
date={new Date(draft)}
onDateChange={onChangeInternal}
mode="date"
locale={i18n.locale}
@@ -102,7 +120,18 @@ export function DateField({
</View>
<Button
label={_(msg`Done`)}
onPress={() => control.close()}
onPress={() => {
/*
* Commit the currently shown date even if the user never
* scrolled (onDateChange only fires on scroll). This keeps
* onChangeDate firing alongside onConfirm, matching Android and
* web, so an empty field confirmed without scrolling does not
* report a date via onConfirm while onChangeDate stays silent.
*/
onChangeDate(draft)
onConfirm?.(draft)
control.close()
}}
size="large"
color="primary"
variant="solid">