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
10 changes: 10 additions & 0 deletions packages/@react-aria/calendar/src/useCalendarCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
onPressStart(e) {
if (state.isReadOnly) {
state.setFocusedDate(date);
state.setFocused(true);
return;
}

Expand All @@ -186,12 +187,14 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
if (isSameDay(date, state.highlightedRange.start)) {
state.setAnchorDate(state.highlightedRange.end);
state.setFocusedDate(date);
state.setFocused(true);
state.setDragging(true);
isRangeBoundaryPressed.current = true;
return;
} else if (isSameDay(date, state.highlightedRange.end)) {
state.setAnchorDate(state.highlightedRange.start);
state.setFocusedDate(date);
state.setFocused(true);
state.setDragging(true);
isRangeBoundaryPressed.current = true;
return;
Expand All @@ -204,6 +207,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta

state.selectDate(date);
state.setFocusedDate(date);
state.setFocused(true);
isAnchorPressed.current = true;
};

Expand All @@ -227,6 +231,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
if (!('anchorDate' in state) && !state.isReadOnly) {
state.selectDate(date);
state.setFocusedDate(date);
state.setFocused(true);
}
},
onPressUp(e) {
Expand All @@ -240,6 +245,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
if ('anchorDate' in state && touchDragTimerRef.current) {
state.selectDate(date);
state.setFocusedDate(date);
state.setFocused(true);
}

if ('anchorDate' in state) {
Expand All @@ -252,6 +258,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
// When releasing a drag or pressing the end date of a range, select it.
state.selectDate(date);
state.setFocusedDate(date);
state.setFocused(true);
} else if (e.pointerType === 'keyboard' && !state.anchorDate) {
// For range selection, auto-advance the focused date by one if using keyboard.
// This gives an indication that you're selecting a range rather than a single date.
Expand All @@ -264,11 +271,13 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
}
if (!state.isInvalid(nextDay)) {
state.setFocusedDate(nextDay);
state.setFocused(true);
}
} else if (e.pointerType === 'virtual') {
// For screen readers, just select the date on click.
state.selectDate(date);
state.setFocusedDate(date);
state.setFocused(true);
}
}
}
Expand Down Expand Up @@ -316,6 +325,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
onFocus() {
if (!isDisabled) {
state.setFocusedDate(date);
state.setFocused(true);
}
},
tabIndex,
Expand Down
7 changes: 3 additions & 4 deletions packages/@react-stately/calendar/src/useCalendarState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ export interface CalendarStateOptions<T extends DateValue = DateValue> extends C
* @default {months: 1}
*/
visibleDuration?: DateDuration,
/**
* Determines the alignment of the visible months on initial render based on the current selection or current date if there is no selection.
/**
* Determines the alignment of the visible months on initial render based on the current selection or current date if there is no selection.
* @default 'center'
*/
selectionAlignment?: 'start' | 'center' | 'end'
Expand Down Expand Up @@ -206,7 +206,6 @@ export function useCalendarState<T extends DateValue = DateValue>(props: Calenda
isValueInvalid,
setFocusedDate(date) {
focusCell(date);
setFocused(true);
},
focusNextDay() {
focusCell(focusedDate.add({days: 1}));
Expand Down Expand Up @@ -336,7 +335,7 @@ export function useCalendarState<T extends DateValue = DateValue>(props: Calenda
let dates: (CalendarDate | null)[] = [];

date = startOfWeek(date, locale, firstDayOfWeek);

// startOfWeek will clamp dates within the calendar system's valid range, which may
// start in the middle of a week. In this case, add null placeholders.
let dayOfWeek = getDayOfWeek(date, locale, firstDayOfWeek);
Expand Down
46 changes: 43 additions & 3 deletions packages/react-aria-components/test/Calendar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/

import {act, fireEvent, pointerMap, render, within} from '@react-spectrum/test-utils-internal';
import {Button, Calendar, CalendarCell, CalendarContext, CalendarGrid, CalendarGridBody, CalendarGridHeader, CalendarHeaderCell, CalendarStateContext, Heading} from 'react-aria-components';
import {Button, ButtonContext, Calendar, CalendarCell, CalendarContext, CalendarGrid, CalendarGridBody, CalendarGridHeader, CalendarHeaderCell, CalendarStateContext, Heading} from 'react-aria-components';
import {CalendarDate, getLocalTimeZone, startOfMonth, startOfWeek, today} from '@internationalized/date';
import React, {useContext} from 'react';
import userEvent from '@testing-library/user-event';
Expand Down Expand Up @@ -84,7 +84,7 @@ describe('Calendar', () => {
});

it('should support aria props on the Calendar', () => {
let {getByRole} = renderCalendar({
let {getByRole} = renderCalendar({
'aria-label': 'label',
'aria-labelledby': 'labelledby',
'aria-describedby': 'describedby',
Expand All @@ -95,7 +95,7 @@ describe('Calendar', () => {
expect(group).toHaveAttribute('aria-label', expect.stringContaining('label'));
expect(group).toHaveAttribute('aria-labelledby', expect.stringContaining('labelledby'));
expect(group).toHaveAttribute('aria-describedby', 'describedby');
expect(group).toHaveAttribute('aria-details', 'details');
expect(group).toHaveAttribute('aria-details', 'details');
});

it('should support custom CalendarGridHeader', () => {
Expand Down Expand Up @@ -400,4 +400,44 @@ describe('Calendar', () => {
await user.keyboard('[ArrowLeft][Enter]');
expect(calendar.getByLabelText(/selected/)).toBe(day16);
});

it('should not become focused just by setting the focused date', async () => {
let DatePicker = () => {
let state = useContext(CalendarStateContext);
return <Button onPress={() => state.setFocusedDate(new CalendarDate(2020, 3, 3))}>Set focused date</Button>;
};

let MyCalendar = () => {
return (
<Calendar aria-label="Appointment date">
<header>
<Button slot="previous">◀</Button>
<Heading />
<ButtonContext.Provider value={null}>
<DatePicker />
</ButtonContext.Provider>
<Button slot="next">▶</Button>
</header>
<CalendarGrid>
<CalendarGridHeader className="grid-header">
{(day) => (
<CalendarHeaderCell className="header-cell">
{day}
</CalendarHeaderCell>
)}
</CalendarGridHeader>
<CalendarGridBody className="grid-body">
{(date) => <CalendarCell date={date} className={({isSelected}) => isSelected ? 'selected' : ''} />}
</CalendarGridBody>
</CalendarGrid>
</Calendar>
);
};
let {getByRole} = render(
<MyCalendar />
);
let setFocusedDateButton = getByRole('button', {name: 'Set focused date'});
await user.click(setFocusedDateButton);
expect(setFocusedDateButton).toHaveFocus();
});
});