Skip to content

Commit dcfa1b0

Browse files
authored
Fix type regression in calendar (#6790)
* Fix type regression in RangeCalendar
1 parent 1af59d1 commit dcfa1b0

File tree

4 files changed

+88
-6
lines changed

4 files changed

+88
-6
lines changed

packages/@react-spectrum/provider/test/Provider.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212

1313
// needs to be imported first
14+
// eslint-disable-next-line
1415
import MatchMediaMock from 'jest-matchmedia-mock';
1516
// eslint-disable-next-line rsp-rules/sort-imports
1617
import {act, fireEvent, pointerMap, render} from '@react-spectrum/test-utils-internal';

packages/@react-types/calendar/src/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export interface CalendarPropsBase {
6767

6868
export type DateRange = RangeValue<DateValue> | null;
6969
export interface CalendarProps<T extends DateValue | null> extends CalendarPropsBase, ValueBase<T | null, MappedDateValue<T>> {}
70-
export interface RangeCalendarProps<T extends DateValue | null> extends CalendarPropsBase, ValueBase<RangeValue<T>> {
70+
export interface RangeCalendarProps<T extends DateValue | null> extends CalendarPropsBase, ValueBase<RangeValue<T> | null> {
7171
/**
7272
* When combined with `isDateUnavailable`, determines whether non-contiguous ranges,
7373
* i.e. ranges containing unavailable dates, may be selected.

packages/react-aria-components/src/Calendar.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,20 @@
99
* OF ANY KIND, either express or implied. See the License for the specific language
1010
* governing permissions and limitations under the License.
1111
*/
12-
import {CalendarProps as BaseCalendarProps, RangeCalendarProps as BaseRangeCalendarProps, DateValue, mergeProps, useCalendar, useCalendarCell, useCalendarGrid, useFocusRing, useHover, useLocale, useRangeCalendar, VisuallyHidden} from 'react-aria';
12+
import {
13+
AriaCalendarProps,
14+
AriaRangeCalendarProps,
15+
DateValue,
16+
mergeProps,
17+
useCalendar,
18+
useCalendarCell,
19+
useCalendarGrid,
20+
useFocusRing,
21+
useHover,
22+
useLocale,
23+
useRangeCalendar,
24+
VisuallyHidden
25+
} from 'react-aria';
1326
import {ButtonContext} from './Button';
1427
import {CalendarDate, createCalendar, DateDuration, endOfMonth, getWeeksInMonth, isSameDay, isSameMonth} from '@internationalized/date';
1528
import {CalendarState, RangeCalendarState, useCalendarState, useRangeCalendarState} from 'react-stately';
@@ -44,15 +57,15 @@ export interface RangeCalendarRenderProps extends Omit<CalendarRenderProps, 'sta
4457
state: RangeCalendarState
4558
}
4659

47-
export interface CalendarProps<T extends DateValue> extends Omit<BaseCalendarProps<T>, 'errorMessage' | 'validationState'>, RenderProps<CalendarRenderProps>, SlotProps {
60+
export interface CalendarProps<T extends DateValue> extends Omit<AriaCalendarProps<T>, 'errorMessage' | 'validationState'>, RenderProps<CalendarRenderProps>, SlotProps {
4861
/**
4962
* The amount of days that will be displayed at once. This affects how pagination works.
5063
* @default {months: 1}
5164
*/
5265
visibleDuration?: DateDuration
5366
}
5467

55-
export interface RangeCalendarProps<T extends DateValue> extends Omit<BaseRangeCalendarProps<T>, 'errorMessage' | 'validationState'>, RenderProps<RangeCalendarRenderProps>, SlotProps {
68+
export interface RangeCalendarProps<T extends DateValue> extends Omit<AriaRangeCalendarProps<T>, 'errorMessage' | 'validationState'>, RenderProps<RangeCalendarRenderProps>, SlotProps {
5669
/**
5770
* The amount of days that will be displayed at once. This affects how pagination works.
5871
* @default {months: 1}

packages/react-aria-components/test/RangeCalendar.test.js renamed to packages/react-aria-components/test/RangeCalendar.test.tsx

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
import {act, fireEvent, pointerMap, render, within} from '@react-spectrum/test-utils-internal';
1414
import {Button, CalendarCell, CalendarGrid, CalendarGridBody, CalendarGridHeader, CalendarHeaderCell, Heading, RangeCalendar, RangeCalendarContext} from 'react-aria-components';
1515
import {CalendarDate, getLocalTimeZone, startOfMonth, startOfWeek, today} from '@internationalized/date';
16+
import {DateValue} from '@react-types/calendar';
17+
import {RangeValue} from '@react-types/shared';
1618
import React from 'react';
1719
import userEvent from '@testing-library/user-event';
1820

19-
let TestCalendar = ({calendarProps, gridProps, cellProps}) => (
21+
let TestCalendar = ({calendarProps = {}, gridProps = {}, cellProps = {}}) => (
2022
<RangeCalendar aria-label="Trip dates" {...calendarProps}>
2123
<header>
2224
<Button slot="previous"></Button>
@@ -29,7 +31,7 @@ let TestCalendar = ({calendarProps, gridProps, cellProps}) => (
2931
</RangeCalendar>
3032
);
3133

32-
let renderCalendar = (calendarProps, gridProps, cellProps) => render(<TestCalendar {...{calendarProps, gridProps, cellProps}} />);
34+
let renderCalendar = (calendarProps = {}, gridProps = {}, cellProps = {}) => render(<TestCalendar {...{calendarProps, gridProps, cellProps}} />);
3335

3436
describe('RangeCalendar', () => {
3537
let user;
@@ -282,6 +284,72 @@ describe('RangeCalendar', () => {
282284
expect(cells[8]).not.toHaveClass('end');
283285
});
284286

287+
it('should support controlled selected range states', async () => {
288+
function ControlledCalendar() {
289+
let [value, setValue] = React.useState<RangeValue<DateValue> | null>(null);
290+
291+
return (
292+
<>
293+
<RangeCalendar aria-label="Trip dates" value={value} onChange={setValue}>
294+
<header>
295+
<Button slot="previous"></Button>
296+
<Heading />
297+
<Button slot="next"></Button>
298+
</header>
299+
<CalendarGrid>
300+
{(date) => <CalendarCell date={date} className={({isSelectionStart, isSelectionEnd}) => `${isSelectionStart ? 'start' : ''} ${isSelectionEnd ? 'end' : ''}`} />}
301+
</CalendarGrid>
302+
</RangeCalendar>
303+
<Button onPress={() => setValue(null)}>Reset</Button>
304+
</>
305+
);
306+
}
307+
let {getByRole} = render(
308+
<ControlledCalendar />
309+
);
310+
311+
let resetBtn = getByRole('button', {name: 'Reset'});
312+
let grid = getByRole('grid');
313+
let cells = within(grid).getAllByRole('button');
314+
315+
expect(cells[7]).not.toHaveAttribute('data-selection-start');
316+
expect(cells[7]).not.toHaveClass('start');
317+
expect(cells[7]).not.toHaveClass('end');
318+
319+
await user.click(cells[7]);
320+
expect(cells[7]).toHaveAttribute('data-selection-start', 'true');
321+
expect(cells[7]).toHaveClass('start');
322+
expect(cells[7]).toHaveAttribute('data-selection-end', 'true');
323+
expect(cells[7]).toHaveClass('end');
324+
325+
expect(cells[8]).not.toHaveAttribute('data-selection-start', 'true');
326+
expect(cells[8]).not.toHaveClass('start');
327+
expect(cells[8]).not.toHaveAttribute('data-selection-end', 'true');
328+
expect(cells[8]).not.toHaveClass('end');
329+
330+
await user.click(cells[10]);
331+
expect(cells[7]).toHaveAttribute('data-selection-start', 'true');
332+
expect(cells[7]).toHaveClass('start');
333+
expect(cells[7]).not.toHaveAttribute('data-selection-end', 'true');
334+
expect(cells[7]).not.toHaveClass('end');
335+
expect(cells[10]).toHaveAttribute('data-selection-end', 'true');
336+
expect(cells[10]).toHaveClass('end');
337+
338+
expect(cells[8]).not.toHaveAttribute('data-selection-start', 'true');
339+
expect(cells[8]).not.toHaveClass('start');
340+
expect(cells[8]).not.toHaveAttribute('data-selection-end', 'true');
341+
expect(cells[8]).not.toHaveClass('end');
342+
343+
await user.click(resetBtn);
344+
345+
expect(cells[7]).not.toHaveAttribute('data-selection-start');
346+
expect(cells[7]).not.toHaveClass('start');
347+
expect(cells[7]).not.toHaveClass('end');
348+
expect(cells[10]).not.toHaveAttribute('data-selection-end');
349+
expect(cells[10]).not.toHaveClass('end');
350+
351+
});
352+
285353
it('should support unavailable state', () => {
286354
let {getByRole} = renderCalendar({isDateUnavailable: () => true}, {}, {className: ({isUnavailable}) => isUnavailable ? 'unavailable' : ''});
287355
let grid = getByRole('grid');

0 commit comments

Comments
 (0)