@@ -6,6 +6,8 @@ import { CalendarDay } from '../calendar/model.js';
66import { defineComponents } from '../common/definitions/defineComponents.js' ;
77import { simulateClick } from '../common/utils.spec.js' ;
88import IgcDateTimeInputComponent from '../date-time-input/date-time-input.js' ;
9+ import { DateTimeUtil } from '../date-time-input/date-util.js' ;
10+ import IgcInputComponent from '../input/input.js' ;
911import IgcDateRangePickerComponent from './date-range-picker.js' ;
1012
1113describe ( 'Date range picker' , ( ) => {
@@ -15,6 +17,9 @@ describe('Date range picker', () => {
1517 let _dateTimeInput : IgcDateTimeInputComponent ;
1618 let calendar : IgcCalendarComponent ;
1719
20+ const today = CalendarDay . from ( new Date ( ) ) ;
21+ const tomorrow = today . add ( 'day' , 1 ) ;
22+
1823 beforeEach ( async ( ) => {
1924 picker = await fixture < IgcDateRangePickerComponent > (
2025 html `< igc-date-range-picker > </ igc-date-range-picker > `
@@ -82,18 +87,14 @@ describe('Date range picker', () => {
8287 } ) ;
8388
8489 it ( 'should be successfully initialized with value' , async ( ) => {
85- const expectedValue = [ new Date ( 2024 , 2 , 19 ) , new Date ( 2025 , 2 , 20 ) ] ;
90+ const expectedValue = [ today . native , tomorrow . native ] ;
8691 picker = await fixture < IgcDateRangePickerComponent > (
8792 html `< igc-date-range-picker
8893 .value ="${ expectedValue } "
8994 > </ igc-date-range-picker > `
9095 ) ;
91- _dateTimeInput = picker . renderRoot . querySelector (
92- IgcDateTimeInputComponent . tagName
93- ) ! ;
9496
95- expect ( picker . value ) . not . to . be . null ;
96- expect ( picker . value ) . to . deep . equal ( expectedValue ) ;
97+ checkSelectedRange ( picker , expectedValue ) ;
9798 } ) ;
9899
99100 it ( 'should be successfully initialized in open state in dropdown mode' , async ( ) => {
@@ -125,104 +126,155 @@ describe('Date range picker', () => {
125126 expect ( calendar ) . not . to . be . undefined ;
126127 expect ( calendar . parentElement ) . to . equal ( dialog ) ;
127128 } ) ;
128- } ) ;
129- describe ( 'Selection via the calendar' , ( ) => {
130- const today = CalendarDay . from ( new Date ( ) ) ;
131- const tomorrow = today . add ( 'day' , 1 ) ;
132-
133- it ( 'should select a single date in dropdown mode and emit igcChange' , async ( ) => {
134- const eventSpy = spy ( picker , 'emitEvent' ) ;
135- picker . open = true ;
136- await elementUpdated ( picker ) ;
137-
138- await selectDates ( today , null , calendar ) ;
139129
140- expect ( eventSpy ) . calledWith ( 'igcChange' ) ;
141- expect ( picker . value ?. length ) . to . equal ( 2 ) ;
142- expect ( picker . value ?. [ 0 ] ) . to . deep . equal ( today . native ) ;
143- expect ( picker . value ?. [ 1 ] ) . to . deep . equal ( today . native ) ;
144-
145- const popover = picker . renderRoot . querySelector ( 'igc-popover' ) ;
146- // when selecting a single date, the calendar won't close
147- expect ( popover ?. hasAttribute ( 'open' ) ) . to . equal ( true ) ;
148- } ) ;
149-
150- it ( 'should select a range of dates in dropdown mode and emit igcChange' , async ( ) => {
151- const eventSpy = spy ( picker , 'emitEvent' ) ;
152-
153- picker . open = true ;
154- await elementUpdated ( picker ) ;
155-
156- await selectDates ( today , tomorrow , calendar ) ;
130+ it ( 'should be initialized as single input' , async ( ) => {
131+ picker = await fixture < IgcDateRangePickerComponent > (
132+ html `< igc-date-range-picker single-input > </ igc-date-range-picker > `
133+ ) ;
157134
158- expect ( eventSpy ) . calledWith ( 'igcChange' ) ;
159- expect ( picker . value ?. length ) . to . equal ( 2 ) ;
160- expect ( picker . value ?. [ 0 ] ) . to . deep . equal ( today . native ) ;
161- expect ( picker . value ?. [ 1 ] ) . to . deep . equal ( tomorrow . native ) ;
135+ expect ( picker . singleInput ) . to . equal ( true ) ;
136+ await picker . show ( ) ;
162137
163- const popover = picker . renderRoot . querySelector ( 'igc-popover' ) ;
164- // with the second click, the calendar closes
165- expect ( popover ?. hasAttribute ( 'open' ) ) . to . equal ( false ) ;
138+ const input = picker . renderRoot . querySelectorAll (
139+ IgcInputComponent . tagName
140+ ) ! ;
141+ expect ( input ) . to . have . length ( 1 ) ;
142+ const dateTimeInputs = picker . renderRoot . querySelectorAll (
143+ IgcDateTimeInputComponent . tagName
144+ ) ;
145+ expect ( dateTimeInputs ) . to . have . length ( 0 ) ;
166146 } ) ;
147+ } ) ;
148+ describe ( 'Properties' , ( ) => {
149+ it ( 'should keep the calendar selection and input values on changing the mode' , async ( ) => {
150+ const expectedValue = [ today . native , tomorrow . native ] ;
151+ picker = await fixture < IgcDateRangePickerComponent > (
152+ html `< igc-date-range-picker
153+ .value ="${ expectedValue } "
154+ > </ igc-date-range-picker > `
155+ ) ;
167156
168- it ( 'should select a range of dates in dialog mode and emit igcChange when done is clicked' , async ( ) => {
169- const eventSpy = spy ( picker , 'emitEvent' ) ;
157+ checkSelectedRange ( picker , expectedValue ) ;
170158
171159 picker . mode = 'dialog' ;
172160 await elementUpdated ( picker ) ;
173- picker . open = true ;
174- await elementUpdated ( picker ) ;
175-
176- await selectDates ( today , tomorrow , calendar ) ;
177-
178- expect ( eventSpy ) . not . to . be . calledWith ( 'igcChange' ) ;
179- let dialog = picker . renderRoot . querySelector ( 'igc-dialog' ) ;
180- expect ( dialog ?. hasAttribute ( 'open' ) ) . to . equal ( true ) ;
181161
182- const doneBtn = picker . shadowRoot ! . querySelector (
183- 'igc-button[slot="footer"]:last-of-type'
184- ) as HTMLButtonElement ;
185- doneBtn ?. click ( ) ;
186- await elementUpdated ( picker ) ;
187-
188- expect ( eventSpy ) . calledWith ( 'igcChange' ) ;
189- expect ( picker . value ?. length ) . to . equal ( 2 ) ;
190- expect ( picker . value ?. [ 0 ] ) . to . deep . equal ( today . native ) ;
191- expect ( picker . value ?. [ 1 ] ) . to . deep . equal ( tomorrow . native ) ;
192-
193- dialog = picker . renderRoot . querySelector ( 'igc-dialog' ) ;
194- expect ( dialog ?. hasAttribute ( 'open' ) ) . to . equal ( false ) ;
162+ checkSelectedRange ( picker , expectedValue ) ;
195163 } ) ;
196164
197- it ( 'should not emit igcChange when cancel is clicked and the value should be the initial value' , async ( ) => {
198- const eventSpy = spy ( picker , 'emitEvent' ) ;
165+ it ( 'should keep the calendar selection and input values on switching to singleInput and back' , async ( ) => {
166+ const expectedValue = [ today . native , tomorrow . native ] ;
167+ picker = await fixture < IgcDateRangePickerComponent > (
168+ html `< igc-date-range-picker
169+ .value ="${ expectedValue } "
170+ > </ igc-date-range-picker > `
171+ ) ;
172+ checkSelectedRange ( picker , expectedValue ) ;
199173
200- picker . mode = 'dialog' ;
201- const date1 = today . add ( 'day' , - 2 ) ;
202- const date2 = today . add ( 'day' , 2 ) ;
203- picker . value = [ date1 . native , date2 . native ] ;
174+ picker . singleInput = true ;
204175 await elementUpdated ( picker ) ;
205- picker . open = true ;
176+ checkSelectedRange ( picker , expectedValue , true ) ;
177+ picker . singleInput = false ;
206178 await elementUpdated ( picker ) ;
207-
208- await selectDates ( date1 , date2 , calendar ) ;
209-
210- expect ( eventSpy ) . not . to . be . calledWith ( 'igcChange' ) ;
211- let dialog = picker . renderRoot . querySelector ( 'igc-dialog' ) ;
212- expect ( dialog ?. hasAttribute ( 'open' ) ) . to . equal ( true ) ;
213-
214- const cancelBtn = picker . shadowRoot ! . querySelector (
215- 'igc-button[slot="footer"]'
216- ) as HTMLButtonElement ;
217- cancelBtn ?. click ( ) ;
218- await elementUpdated ( picker ) ;
219-
220- expect ( eventSpy ) . not . to . be . calledWith ( 'igcChange' ) ;
221- expect ( picker . value ?. length ) . to . equal ( 2 ) ;
222- expect ( picker . value ?. [ 0 ] ) . to . deep . equal ( date1 . native ) ;
223- expect ( picker . value ?. [ 1 ] ) . to . deep . equal ( date2 . native ) ;
224- dialog = picker . renderRoot . querySelector ( 'igc-dialog' ) ;
225- expect ( dialog ?. hasAttribute ( 'open' ) ) . to . equal ( false ) ;
179+ checkSelectedRange ( picker , expectedValue ) ;
180+ } ) ;
181+ } ) ;
182+ describe ( 'Interactions' , ( ) => {
183+ describe ( 'Selection via the calendar' , ( ) => {
184+ it ( 'should select a single date in dropdown mode and emit igcChange' , async ( ) => {
185+ const eventSpy = spy ( picker , 'emitEvent' ) ;
186+ picker . open = true ;
187+ await elementUpdated ( picker ) ;
188+
189+ await selectDates ( today , null , calendar ) ;
190+
191+ expect ( eventSpy ) . calledWith ( 'igcChange' ) ;
192+ expect ( picker . value ?. length ) . to . equal ( 2 ) ;
193+ expect ( picker . value ?. [ 0 ] ) . to . deep . equal ( today . native ) ;
194+ expect ( picker . value ?. [ 1 ] ) . to . deep . equal ( today . native ) ;
195+
196+ const popover = picker . renderRoot . querySelector ( 'igc-popover' ) ;
197+ // when selecting a single date, the calendar won't close
198+ expect ( popover ?. hasAttribute ( 'open' ) ) . to . equal ( true ) ;
199+ } ) ;
200+
201+ it ( 'should select a range of dates in dropdown mode and emit igcChange' , async ( ) => {
202+ const eventSpy = spy ( picker , 'emitEvent' ) ;
203+
204+ picker . open = true ;
205+ await elementUpdated ( picker ) ;
206+
207+ await selectDates ( today , tomorrow , calendar ) ;
208+
209+ expect ( eventSpy ) . calledWith ( 'igcChange' ) ;
210+ expect ( picker . value ?. length ) . to . equal ( 2 ) ;
211+ expect ( picker . value ?. [ 0 ] ) . to . deep . equal ( today . native ) ;
212+ expect ( picker . value ?. [ 1 ] ) . to . deep . equal ( tomorrow . native ) ;
213+
214+ const popover = picker . renderRoot . querySelector ( 'igc-popover' ) ;
215+ // with the second click, the calendar closes
216+ expect ( popover ?. hasAttribute ( 'open' ) ) . to . equal ( false ) ;
217+ } ) ;
218+
219+ it ( 'should select a range of dates in dialog mode and emit igcChange when done is clicked' , async ( ) => {
220+ const eventSpy = spy ( picker , 'emitEvent' ) ;
221+
222+ picker . mode = 'dialog' ;
223+ await elementUpdated ( picker ) ;
224+ picker . open = true ;
225+ await elementUpdated ( picker ) ;
226+
227+ await selectDates ( today , tomorrow , calendar ) ;
228+
229+ expect ( eventSpy ) . not . to . be . calledWith ( 'igcChange' ) ;
230+ let dialog = picker . renderRoot . querySelector ( 'igc-dialog' ) ;
231+ expect ( dialog ?. hasAttribute ( 'open' ) ) . to . equal ( true ) ;
232+
233+ const doneBtn = picker . shadowRoot ! . querySelector (
234+ 'igc-button[slot="footer"]:last-of-type'
235+ ) as HTMLButtonElement ;
236+ doneBtn ?. click ( ) ;
237+ await elementUpdated ( picker ) ;
238+
239+ expect ( eventSpy ) . calledWith ( 'igcChange' ) ;
240+ expect ( picker . value ?. length ) . to . equal ( 2 ) ;
241+ expect ( picker . value ?. [ 0 ] ) . to . deep . equal ( today . native ) ;
242+ expect ( picker . value ?. [ 1 ] ) . to . deep . equal ( tomorrow . native ) ;
243+
244+ dialog = picker . renderRoot . querySelector ( 'igc-dialog' ) ;
245+ expect ( dialog ?. hasAttribute ( 'open' ) ) . to . equal ( false ) ;
246+ } ) ;
247+
248+ it ( 'should not emit igcChange when cancel is clicked and the value should be the initial value' , async ( ) => {
249+ const eventSpy = spy ( picker , 'emitEvent' ) ;
250+
251+ picker . mode = 'dialog' ;
252+ const date1 = today . add ( 'day' , - 2 ) ;
253+ const date2 = today . add ( 'day' , 2 ) ;
254+ picker . value = [ date1 . native , date2 . native ] ;
255+ await elementUpdated ( picker ) ;
256+ picker . open = true ;
257+ await elementUpdated ( picker ) ;
258+
259+ await selectDates ( date1 , date2 , calendar ) ;
260+
261+ expect ( eventSpy ) . not . to . be . calledWith ( 'igcChange' ) ;
262+ let dialog = picker . renderRoot . querySelector ( 'igc-dialog' ) ;
263+ expect ( dialog ?. hasAttribute ( 'open' ) ) . to . equal ( true ) ;
264+
265+ const cancelBtn = picker . shadowRoot ! . querySelector (
266+ 'igc-button[slot="footer"]'
267+ ) as HTMLButtonElement ;
268+ cancelBtn ?. click ( ) ;
269+ await elementUpdated ( picker ) ;
270+
271+ expect ( eventSpy ) . not . to . be . calledWith ( 'igcChange' ) ;
272+ expect ( picker . value ?. length ) . to . equal ( 2 ) ;
273+ expect ( picker . value ?. [ 0 ] ) . to . deep . equal ( date1 . native ) ;
274+ expect ( picker . value ?. [ 1 ] ) . to . deep . equal ( date2 . native ) ;
275+ dialog = picker . renderRoot . querySelector ( 'igc-dialog' ) ;
276+ expect ( dialog ?. hasAttribute ( 'open' ) ) . to . equal ( false ) ;
277+ } ) ;
226278 } ) ;
227279 } ) ;
228280} ) ;
@@ -244,3 +296,38 @@ const selectDates = async (
244296 await elementUpdated ( calendar ) ;
245297 }
246298} ;
299+
300+ const checkSelectedRange = (
301+ picker : IgcDateRangePickerComponent ,
302+ expectedValue : ( Date | null ) [ ] ,
303+ singleInput = false
304+ ) => {
305+ const calendar = picker . renderRoot . querySelector (
306+ IgcCalendarComponent . tagName
307+ ) ! ;
308+
309+ expect ( picker . value ) . not . to . be . null ;
310+ expect ( picker . value ) . to . deep . equal ( expectedValue ) ;
311+
312+ if ( singleInput ) {
313+ const input = picker . renderRoot . querySelector ( IgcInputComponent . tagName ) ! ;
314+ const start = DateTimeUtil . formatDate (
315+ expectedValue [ 0 ] ! ,
316+ picker . locale ,
317+ picker . displayFormat || picker . inputFormat
318+ ) ;
319+ const end = DateTimeUtil . formatDate (
320+ expectedValue [ 1 ] ! ,
321+ picker . locale ,
322+ picker . displayFormat || picker . inputFormat
323+ ) ;
324+ expect ( input . value ) . to . equal ( `${ start } - ${ end } ` ) ;
325+ } else {
326+ const inputs = picker . renderRoot . querySelectorAll (
327+ IgcDateTimeInputComponent . tagName
328+ ) ;
329+ expect ( inputs [ 0 ] . value ) . to . deep . equal ( expectedValue [ 0 ] ) ;
330+ expect ( inputs [ 1 ] . value ) . to . deep . equal ( expectedValue [ 1 ] ) ;
331+ }
332+ expect ( calendar . values ) . to . deep . equal ( expectedValue ) ;
333+ } ;
0 commit comments