Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions packages/react-aria-components/src/DateField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ export interface DateFieldRenderProps {
* Whether the date field is read only.
* @selector [data-readonly]
*/
isReadOnly: boolean
isReadOnly: boolean,
/**
* Whether the date field is required.
* @selector [data-required]
*/
isRequired: boolean
}
export interface DateFieldProps<T extends DateValue> extends Omit<AriaDateFieldProps<T>, 'label' | 'description' | 'errorMessage' | 'validationState' | 'validationBehavior'>, RACValidation, RenderProps<DateFieldRenderProps>, SlotProps, GlobalDOMAttributes<HTMLDivElement> {
/**
Expand Down Expand Up @@ -113,7 +118,8 @@ export const DateField = /*#__PURE__*/ (forwardRef as forwardRefType)(function D
state,
isInvalid: state.isInvalid,
isDisabled: state.isDisabled,
isReadOnly: state.isReadOnly
isReadOnly: state.isReadOnly,
isRequired: props.isRequired || false
},
defaultClassName: 'react-aria-DateField'
});
Expand Down Expand Up @@ -143,7 +149,8 @@ export const DateField = /*#__PURE__*/ (forwardRef as forwardRefType)(function D
slot={props.slot || undefined}
data-invalid={state.isInvalid || undefined}
data-disabled={state.isDisabled || undefined}
data-readonly={state.isReadOnly || undefined} />
data-readonly={state.isReadOnly || undefined}
data-required={props.isRequired || undefined} />
<HiddenDateInput
autoComplete={props.autoComplete}
name={props.name}
Expand Down Expand Up @@ -186,7 +193,8 @@ export const TimeField = /*#__PURE__*/ (forwardRef as forwardRefType)(function T
state,
isInvalid: state.isInvalid,
isDisabled: state.isDisabled,
isReadOnly: state.isReadOnly
isReadOnly: state.isReadOnly,
isRequired: props.isRequired || false
},
defaultClassName: 'react-aria-TimeField'
});
Expand Down Expand Up @@ -216,7 +224,8 @@ export const TimeField = /*#__PURE__*/ (forwardRef as forwardRefType)(function T
slot={props.slot || undefined}
data-invalid={state.isInvalid || undefined}
data-disabled={state.isDisabled || undefined}
data-readonly={state.isReadOnly || undefined} />
data-readonly={state.isReadOnly || undefined}
data-required={props.isRequired || undefined} />
</Provider>
);
});
Expand Down
13 changes: 11 additions & 2 deletions packages/react-aria-components/src/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ export interface DatePickerRenderProps {
* @selector [data-invalid]
*/
isInvalid: boolean,
/**
* Whether the date picker is required.
* @selector [data-required]
*/
isRequired: boolean,
/**
* Whether the date picker's popover is currently open.
* @selector [data-open]
Expand Down Expand Up @@ -161,7 +166,8 @@ export const DatePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(function
isDisabled: props.isDisabled || false,
isInvalid: state.isInvalid,
isOpen: state.isOpen,
isReadOnly: props.isReadOnly || false
isReadOnly: props.isReadOnly || false,
isRequired: props.isRequired || false
},
defaultClassName: 'react-aria-DatePicker'
});
Expand Down Expand Up @@ -204,6 +210,7 @@ export const DatePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(function
data-focus-visible={isFocusVisible || undefined}
data-disabled={props.isDisabled || undefined}
data-readonly={props.isReadOnly || undefined}
data-required={props.isRequired || undefined}
data-open={state.isOpen || undefined} />
<HiddenDateInput
autoComplete={props.autoComplete}
Expand Down Expand Up @@ -271,7 +278,8 @@ export const DateRangePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(func
isDisabled: props.isDisabled || false,
isInvalid: state.isInvalid,
isOpen: state.isOpen,
isReadOnly: props.isReadOnly || false
isReadOnly: props.isReadOnly || false,
isRequired: props.isRequired || false
},
defaultClassName: 'react-aria-DateRangePicker'
});
Expand Down Expand Up @@ -319,6 +327,7 @@ export const DateRangePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(func
data-focus-visible={isFocusVisible || undefined}
data-disabled={props.isDisabled || undefined}
data-readonly={props.isReadOnly || undefined}
data-required={props.isRequired || undefined}
data-open={state.isOpen || undefined} />
</Provider>
);
Expand Down
40 changes: 40 additions & 0 deletions packages/react-aria-components/test/DateField.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,46 @@ describe('DateField', () => {
expect(group).toHaveAttribute('data-disabled-state', 'disabled');
});

it('should support required render prop', () => {
let {getByRole} = render(
<DateField isRequired>
{({isRequired}) => (
<>
<Label>Birth date</Label>
<DateInput
data-required-state={isRequired ? 'required' : null}>
{segment => <DateSegment segment={segment} />}
</DateInput>
</>
)}
</DateField>
);
let group = getByRole('group');
expect(group).toHaveAttribute('data-required-state', 'required');
});

it('should support required state', () => {
let {getByRole, rerender} = render(
<DateField>
<Label>Birth date</Label>
<DateInput>
{segment => <DateSegment segment={segment} />}
</DateInput>
</DateField>
);
let group = getByRole('group');
expect(group.closest('.react-aria-DateField')).not.toHaveAttribute('data-required');
rerender(
<DateField isRequired>
<Label>Birth date</Label>
<DateInput>
{segment => <DateSegment segment={segment} />}
</DateInput>
</DateField>
);
expect(group.closest('.react-aria-DateField')).toHaveAttribute('data-required');
});

it('should support form value', () => {
render(
<DateField name="birthday" form="test" value={new CalendarDate(2020, 2, 3)}>
Expand Down
43 changes: 43 additions & 0 deletions packages/react-aria-components/test/DatePicker.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,49 @@ describe('DatePicker', () => {
expect(group).toHaveAttribute('data-validation-state', 'invalid');
});

it('should support required render prop', () => {
let {getByRole} = render(
<DatePicker isRequired>
{({isRequired}) => (
<>
<Label>Birth date</Label>
<Group data-required-state={isRequired ? 'required' : null}>
<DateInput>
{(segment) => <DateSegment segment={segment} />}
</DateInput>
<Button>▼</Button>
</Group>
<Popover>
<Dialog>
<Calendar>
<header>
<Button slot="previous">◀</Button>
<Heading />
<Button slot="next">▶</Button>
</header>
<CalendarGrid>
{(date) => <CalendarCell date={date} />}
</CalendarGrid>
</Calendar>
</Dialog>
</Popover>
</>
)}
</DatePicker>
);

let group = getByRole('group');
expect(group).toHaveAttribute('data-required-state', 'required');
});

it('should support required state', () => {
let {getByRole, rerender} = render(<TestDatePicker />);
let group = getByRole('group');
expect(group.closest('.react-aria-DatePicker')).not.toHaveAttribute('data-required');
rerender(<TestDatePicker isRequired />);
expect(group.closest('.react-aria-DatePicker')).toHaveAttribute('data-required');
});

it('should support form value', () => {
render(<TestDatePicker name="birthday" form="test" value={new CalendarDate(2020, 2, 3)} />);
let input = document.querySelector('input[name=birthday]');
Expand Down
47 changes: 47 additions & 0 deletions packages/react-aria-components/test/DateRangePicker.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,53 @@ describe('DateRangePicker', () => {
expect(group).toHaveAttribute('data-validation-state', 'invalid');
});

it('should support required render prop', () => {
let {getByRole} = render(
<DateRangePicker isRequired>
{({isRequired}) => (
<>
<Label>Trip dates</Label>
<Group data-required-state={isRequired ? 'required' : null}>
<DateInput slot="start">
{(segment) => <DateSegment segment={segment} />}
</DateInput>
<span aria-hidden="true">–</span>
<DateInput slot="end">
{(segment) => <DateSegment segment={segment} />}
</DateInput>
<Button>▼</Button>
</Group>
<Popover>
<Dialog>
<RangeCalendar>
<header>
<Button slot="previous">◀</Button>
<Heading />
<Button slot="next">▶</Button>
</header>
<CalendarGrid>
{(date) => <CalendarCell date={date} />}
</CalendarGrid>
</RangeCalendar>
</Dialog>
</Popover>
</>
)}
</DateRangePicker>
);

let group = getByRole('group');
expect(group).toHaveAttribute('data-required-state', 'required');
});

it('should support required state', () => {
let {getByRole, rerender} = render(<TestDateRangePicker />);
let group = getByRole('group');
expect(group.closest('.react-aria-DateRangePicker')).not.toHaveAttribute('data-required');
rerender(<TestDateRangePicker isRequired />);
expect(group.closest('.react-aria-DateRangePicker')).toHaveAttribute('data-required');
});

it('should support form value', () => {
render(<TestDateRangePicker startName="start" endName="end" form="test" value={{start: new CalendarDate(2023, 1, 10), end: new CalendarDate(2023, 1, 20)}} />);
let start = document.querySelector('input[name=start]');
Expand Down
40 changes: 40 additions & 0 deletions packages/react-aria-components/test/TimeField.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,46 @@ describe('TimeField', () => {
expect(group).toHaveAttribute('data-disabled-state', 'disabled');
});

it('should support required render prop', () => {
let {getByRole} = render(
<TimeField isRequired>
{({isRequired}) => (
<>
<Label>Time</Label>
<DateInput
data-required-state={isRequired ? 'required' : null}>
{segment => <DateSegment segment={segment} />}
</DateInput>
</>
)}
</TimeField>
);
let group = getByRole('group');
expect(group).toHaveAttribute('data-required-state', 'required');
});

it('should support required state', () => {
let {getByRole, rerender} = render(
<TimeField>
<Label>Time</Label>
<DateInput>
{segment => <DateSegment segment={segment} />}
</DateInput>
</TimeField>
);
let group = getByRole('group');
expect(group.closest('.react-aria-TimeField')).not.toHaveAttribute('data-required');
rerender(
<TimeField isRequired>
<Label>Time</Label>
<DateInput>
{segment => <DateSegment segment={segment} />}
</DateInput>
</TimeField>
);
expect(group.closest('.react-aria-TimeField')).toHaveAttribute('data-required');
});

it('should support form value', () => {
render(
<TimeField name="time" form="test" value={new Time(8, 30)}>
Expand Down