adobe/react-spectrum

Allow providing generic FocusableProps type in DateRangePicker

Ouverte

#5 766 ouverte le 29 janv. 2024

 (2 commentaires) (0 réaction) (0 personne assignée)TypeScript (1 500 forks)auto 404
help wantedtypescript

Métriques du dépôt

Stars
 (15 634 étoiles)
Métriques de merge PR
 (Merge moyen 8j 2h) (80 PRs mergées en 30 j)

Description

Provide a general summary of the feature here

Currently DateRangePicker has props of type DateRangePickerProps which extends AriaDateRangePickerProps -> AriaDatePickerBaseProps -> DatePickerBase -> DateFieldBase which finally extends FocusableProps

DateFieldBase is not allowing FocusableProps to set custom Target element type (it's always Element)

interface DateFieldBase<T extends DateValue> extends InputBase, Validation<MappedDateValue<T>>, FocusableProps, LabelableProps, HelpTextProps, OverlayTriggerProps {
// ommited for brewity
}

This prevents using use of DateRangePicker with libraries like react-final-form, which has onBlur, onFocus event target set as HTMLElement.

Result in type error during compilation:

Type 'FocusEvent<Element, Element>' is not assignable to type 'FocusEvent<HTMLElement, Element>'.

🤔 Expected Behavior?

DateRangePicker, down to DateFieldBase should allow override default Target for FocusableProps.

https://github.com/adobe/react-spectrum/blob/87dbb748cbfd9bd6f53183221b8e2ef96defbfc3/packages/react-aria-components/src/DatePicker.tsx#L156C10-L156C25

😯 Current Behavior

DateFieldBase doesn't allow to specify target element element type (it's always typeof Element)

💁 Possible Solution

interface DateFieldBase<T extends DateValue, Target extends Element> extends InputBase, Validation<MappedDateValue<T>>, FocusableProps<Target>, LabelableProps, HelpTextProps, OverlayTriggerProps {
// ommited for brewity
}

🔦 Context

Creating form wrapper for DateRangePicker using react-final-form is not possible because react-final-form expects onBlur, onFocus event's target to extend HTMLElement:

💻 Examples

import type { DateRange } from 'react-aria';
import { Field } from 'react-final-form';
import { useTranslation } from 'next-i18next';
import { useCountry } from '@colonnade/api';
import { DateRangePicker } from '@colonnade/react-components';
import { GregorianCalendar, parseDate, toCalendar } from '@internationalized/date';
import { OverlayTriggerProps } from '@react-types/overlays';
import { FocusableProps, HelpTextProps, InputBase, LabelableProps, Validation } from '@react-types/shared';

interface TripValues {
	departureDate: string;
	returnDate: string;
}

export const InternationalSingleTrip = () => {
	const { t } = useTranslation('travel');
	const country = useCountry();

	const parseDateRange = (value: DateRange | null): TripValues | null => {
		if (!value) return null;
		return { departureDate: value.start.toString(), returnDate: value.end.toString() };
	};

	const formatDateRange = (value: TripValues | null): DateRange | null => {
		if (!value) return null;

		const start = toCalendar(parseDate(value.departureDate), new GregorianCalendar());
		const end = toCalendar(parseDate(value.returnDate), new GregorianCalendar());

		return { start, end };
	};

	return (
		<div>
			<Field<TripValues | null, HTMLElement, DateRange | null>
				name="tripDate"
				startName="departureDate"
				endName="returnDate"
				locale={country}
				parse={parseDateRange}
				format={formatDateRange}
				label={t('pages.tripDetails.basicTripInformation.tripDateRange.label')}
			>
				{({ input, ...otherProps }) => <DateRangePicker {...otherProps} {...input} />}
			</Field>
		</div>
	);
};

🧢 Your Company/Team

No response

🕷 Tracking Issue

No response

Guide contributeur