1313import { act , fireEvent , pointerMap , render , within } from '@react-spectrum/test-utils-internal' ;
1414import { Button , CalendarCell , CalendarGrid , CalendarGridBody , CalendarGridHeader , CalendarHeaderCell , Heading , RangeCalendar , RangeCalendarContext } from 'react-aria-components' ;
1515import { CalendarDate , getLocalTimeZone , startOfMonth , startOfWeek , today } from '@internationalized/date' ;
16+ import { DateValue } from '@react-types/calendar' ;
17+ import { RangeValue } from '@react-types/shared' ;
1618import React from 'react' ;
1719import 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
3436describe ( '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