Skip to content
14 changes: 7 additions & 7 deletions packages/react-aria-components/src/DateField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@ export const DateField = /*#__PURE__*/ (forwardRef as forwardRefType)(function D
slot={props.slot || undefined}
data-invalid={state.isInvalid || undefined}
data-disabled={state.isDisabled || undefined} />
<HiddenDateInput
<HiddenDateInput
autoComplete={props.autoComplete}
name={props.name}
isDisabled={props.isDisabled}
state={state} />
state={state} />
</Provider>
);
});
Expand Down Expand Up @@ -180,7 +180,7 @@ export const TimeField = /*#__PURE__*/ (forwardRef as forwardRefType)(function T
{...renderProps}
ref={ref}
slot={props.slot || undefined}
data-invalid={state.isInvalid || undefined}
data-invalid={state.isInvalid || undefined}
data-disabled={state.isDisabled || undefined} />
</Provider>
);
Expand Down Expand Up @@ -269,6 +269,7 @@ const DateInputInner = forwardRef((props: DateInputProps, ref: ForwardedRef<HTML
ref={ref}
slot={props.slot || undefined}
className={className ?? 'react-aria-DateInput'}
isReadOnly={state.isReadOnly}
isInvalid={state.isInvalid}
isDisabled={state.isDisabled}>
{state.segments.map((segment, i) => cloneElement(children(segment), {key: i}))}
Expand Down Expand Up @@ -337,12 +338,11 @@ export const DateSegment = /*#__PURE__*/ (forwardRef as forwardRefType)(function
let {segmentProps} = useDateSegment(segment, state, domRef);
let {focusProps, isFocused, isFocusVisible} = useFocusRing();
let {hoverProps, isHovered} = useHover({...otherProps, isDisabled: state.isDisabled || segment.type === 'literal'});
let {isEditable, ...segmentRest} = segment;
let renderProps = useRenderProps({
...otherProps,
values: {
...segmentRest,
isReadOnly: !isEditable,
...segment,
isReadOnly: state.isReadOnly,
isInvalid: state.isInvalid,
isDisabled: state.isDisabled,
isHovered,
Expand All @@ -361,7 +361,7 @@ export const DateSegment = /*#__PURE__*/ (forwardRef as forwardRefType)(function
ref={domRef}
data-placeholder={segment.isPlaceholder || undefined}
data-invalid={state.isInvalid || undefined}
data-readonly={!isEditable || undefined}
data-readonly={state.isReadOnly || undefined}
data-disabled={state.isDisabled || undefined}
data-type={segment.type}
data-hovered={isHovered || undefined}
Expand Down
7 changes: 5 additions & 2 deletions packages/react-aria-components/src/Group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export interface GroupProps extends AriaLabelingProps, Omit<HTMLAttributes<HTMLE
isDisabled?: boolean,
/** Whether the group is invalid. */
isInvalid?: boolean,
/** Whether the group is read only. */
isReadOnly?: boolean,
/**
* An accessibility role for the group. By default, this is set to `'group'`.
* Use `'region'` when the contents of the group is important enough to be
Expand All @@ -65,7 +67,7 @@ export const GroupContext = createContext<ContextValue<GroupProps, HTMLDivElemen
*/
export const Group = /*#__PURE__*/ (forwardRef as forwardRefType)(function Group(props: GroupProps, ref: ForwardedRef<HTMLDivElement>) {
[props, ref] = useContextProps(props, ref, GroupContext);
let {isDisabled, isInvalid, onHoverStart, onHoverChange, onHoverEnd, ...otherProps} = props;
let {isDisabled, isInvalid, isReadOnly, onHoverStart, onHoverChange, onHoverEnd, ...otherProps} = props;

let {hoverProps, isHovered} = useHover({onHoverStart, onHoverChange, onHoverEnd, isDisabled});
let {isFocused, isFocusVisible, focusProps} = useFocusRing({
Expand All @@ -92,7 +94,8 @@ export const Group = /*#__PURE__*/ (forwardRef as forwardRefType)(function Group
data-hovered={isHovered || undefined}
data-focus-visible={isFocusVisible || undefined}
data-disabled={isDisabled || undefined}
data-invalid={isInvalid || undefined}>
data-invalid={isInvalid || undefined}
data-readonly={isReadOnly || undefined}>
{renderProps.children}
</div>
);
Expand Down
6 changes: 6 additions & 0 deletions packages/react-aria-components/stories/DateField.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ export default {
isInvalid: {
control: 'boolean'
},
isDisabled: {
control: 'boolean'
},
isReadOnly: {
control: 'boolean'
},
validationBehavior: {
control: 'select',
options: ['native', 'aria']
Expand Down
65 changes: 64 additions & 1 deletion packages/react-aria-components/test/DateField.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ describe('DateField', () => {
expect(segment).toHaveAttribute('data-placeholder', 'true');
expect(segment).toHaveAttribute('data-type');
expect(segment).toHaveAttribute('data-test', 'test');
expect(segment).not.toHaveAttribute('data-readonly');
}

for (let literal of [...input.children].filter(child => child.getAttribute('data-type') === 'literal')) {
expect(literal).not.toHaveAttribute('data-readonly');
}
});

Expand Down Expand Up @@ -164,7 +169,7 @@ describe('DateField', () => {
});

it('should support disabled state', () => {
let {getByRole} = render(
let {getByRole, getAllByRole} = render(
<DateField isDisabled>
<Label>Birth date</Label>
<DateInput className={({isDisabled}) => isDisabled ? 'disabled' : ''}>
Expand All @@ -175,6 +180,64 @@ describe('DateField', () => {
let group = getByRole('group');
expect(group).toHaveAttribute('data-disabled');
expect(group).toHaveClass('disabled');

for (let segment of getAllByRole('spinbutton')) {
expect(segment).not.toHaveAttribute('data-readonly');
expect(segment).toHaveAttribute('data-disabled');
}
for (let literal of [...group.children].filter(child => child.getAttribute('data-type') === 'literal')) {
expect(literal).not.toHaveAttribute('data-readonly');
expect(literal).toHaveAttribute('data-disabled');
}
});

it('should support readonly with disabled state', () => {
let {getByRole, getAllByRole} = render(
<DateField isReadOnly isDisabled>
<Label>Birth date</Label>
<DateInput>
{segment => <DateSegment segment={segment} />}
</DateInput>
</DateField>
);

let group = getByRole('group');
expect(group).toHaveAttribute('data-readonly');
expect(group).toHaveAttribute('data-disabled');

for (let segment of getAllByRole('spinbutton')) {
expect(segment).toHaveAttribute('data-readonly');
expect(segment).toHaveAttribute('data-disabled');
}
for (let literal of [...group.children].filter(child => child.getAttribute('data-type') === 'literal')) {
expect(literal).toHaveAttribute('data-readonly');
expect(literal).toHaveAttribute('data-disabled');
}
});

it('should support readonly state', () => {
let {getByRole, getAllByRole} = render(
<DateField isReadOnly>
<Label>Birth date</Label>
<DateInput>
{segment => <DateSegment segment={segment} />}
</DateInput>
</DateField>
);

let group = getByRole('group');
expect(group).toHaveAttribute('data-readonly');
expect(group).not.toHaveAttribute('data-disabled');
expect(group).not.toHaveClass('disabled');

for (let segment of getAllByRole('spinbutton')) {
expect(segment).toHaveAttribute('data-readonly');
expect(segment).not.toHaveAttribute('data-disabled');
}
for (let literal of [...group.children].filter(child => child.getAttribute('data-type') === 'literal')) {
expect(literal).toHaveAttribute('data-readonly');
expect(literal).not.toHaveAttribute('data-disabled');
}
});

it('should support render props', () => {
Expand Down