adobe / react-spectrum

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.
https://react-spectrum.adobe.com
Apache License 2.0
12.17k stars 1.06k forks source link

Allow providing generic FocusableProps type in DateRangePicker #5766

Open mauron85 opened 5 months ago

mauron85 commented 5 months ago

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

snowystinger commented 5 months ago

I think this seems fine on the surface, we make use of it in ComboBox so that select is exposed https://github.com/adobe/react-spectrum/pull/4039

I am a bit curious what react-final-form will do with those, maybe they should change to Element?

We'll want to be careful about how far up the chain we expose it, and I imagine that most of our components would have this issue? is this the only one you're using?

mauron85 commented 4 months ago

so far this is the only component i have problems writing form wrapper for it. other use just strings so wrapper is much simpler (no need to format from datecalendar and back to string)