imx-select-datepicker 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,64 +1,263 @@
1
- # imx-select-datepicker - InputGroup
1
+ # imx-select-datepicker
2
2
 
3
- A component for managing date inputs with separate controls for day and month.
3
+ A component for handling date selection via select elements for day, month, and year. The component can be used as a single date picker or as a range date picker.
4
4
 
5
- ## Constructor
5
+ ## SelectDatepicker
6
+
7
+ A component for managing date inputs with support for single date and date range modes.
8
+ If no date is explicitly set via `setDate()`, the component defaults to the first day of the first month in the available options range.
9
+
10
+ ### Constructor
6
11
 
7
12
  ```javascript
8
- const inputGroup = new InputGroup(startDate, endDate);
13
+ import { SelectDatepicker } from 'imx-select-datepicker';
14
+
15
+ const selectDatepicker = new SelectDatepicker(config);
9
16
  ```
10
17
 
11
18
  **Parameters:**
12
- - `startDate` (Date, optional) - The start date for the possible select options. Defaults to the current date minus 1 year.
13
- - `endDate` (Date, optional) - The end date for the possible select options. Defaults to the current date plus 1 year.
19
+ - `config` (Object) - Configuration object
20
+ - `mode` (String) - Mode of the component: `'single'` for single date or `'range'` for date range
21
+ - `availableOptions` (Object, optional)
22
+ - `start` (Date, optional) - start date for the range of the day/month options - works only in monthly precision (defaults to one year ago)
23
+ - `end` (Date, optional) - end date for the range of the day/month options - works only in monthly precision (defaults to one year from now)
24
+ - `onChange` (Function, optional) - Callback function that is called when the date changes
25
+
26
+ ### Usage
27
+
28
+ #### Single Date Mode
14
29
 
15
- ## Usage as single date picker
30
+ ```javascript
31
+ const selectDatepicker = new SelectDatepicker({
32
+ mode: 'single',
33
+ availableOptions: {
34
+ start: new Date('2026-01-01'),
35
+ end: new Date('2026-12-31')
36
+ },
37
+ onChange: (dates) => {
38
+ console.log('Selected date:', dates);
39
+ }
40
+ });
41
+
42
+ document.getElementById('datepicker').appendChild(selectDatepicker.getMarkup());
43
+ ```
44
+
45
+ #### Date Range Mode
16
46
 
17
47
  ```javascript
18
- const inputGroup = new InputGroup();
19
- inputDate.appendChild(inputGroup.markup);
48
+ const selectDatepicker = new SelectDatepicker({
49
+ mode: 'range',
50
+ availableOptions: {
51
+ start: new Date('2026-01-01'),
52
+ end: new Date('2026-12-31')
53
+ },
54
+ onChange: (dates) => {
55
+ console.log('From:', dates[0], 'To:', dates[1]);
56
+ }
57
+ });
58
+
59
+ document.getElementById('datepicker').appendChild(selectDatepicker.getMarkup());
60
+ ```
61
+
62
+ ### Methods
63
+
64
+ #### `getMarkup()`
65
+ Returns the DOM element of the component that can be inserted into the document.
66
+
67
+ **Returns:** DOM Element
68
+
69
+ #### `setDate(date)`
70
+ Sets the date(s) of the component.
71
+
72
+ **Parameters:**
73
+ - `date` (Date | Array\<Date\>) - A single date or an array with two dates for the date range
74
+
75
+ ```javascript
76
+ // Single date
77
+ selectDatepicker.setDate(new Date('2026-06-15'));
78
+
79
+ // Date range
80
+ selectDatepicker.setDate([
81
+ new Date('2026-06-01'),
82
+ new Date('2026-06-30')
83
+ ]);
84
+ ```
85
+
86
+ #### `setMinDate(date)`
87
+ Sets the minimal selectable (non disabled) date.
88
+
89
+ **Parameters:**
90
+ - `date` (Date) - A single date.
91
+
92
+ #### `getDate()`
93
+ Returns the currently selected dates.
94
+
95
+ **Returns:** Array\<String\> - Array with one or two ISO date strings (YYYY-MM-DD)
96
+
97
+ ```javascript
98
+ const selectedDates = selectDatepicker.getDate();
99
+ console.log(selectedDates); // ['2026-06-01', '2026-06-30'] or ['2026-06-15']
100
+ ```
101
+
102
+ ### Events
103
+
104
+ The component automatically calls the `onChange` callback function when the date selection changes:
105
+
106
+ ```javascript
107
+ const selectDatepicker = new SelectDatepicker({
108
+ mode: 'range',
109
+ onChange: (dates) => {
110
+ console.log('New start date:', dates[0]);
111
+ console.log('New end date:', dates[1]);
112
+ }
113
+ });
114
+ ```
115
+
116
+ ---
117
+
118
+ ## InputGroup
119
+
120
+ A component for managing date inputs with separate controls for day, month, and year.
121
+ If no date is explicitly set via `setDate()`, the component defaults to the first day of the month specified by `startDate`.
122
+
123
+ ### Constructor
124
+
125
+ ```javascript
126
+ import { InputGroup } from 'imx-select-datepicker';
127
+
128
+ const startDate = new Date('2026-01-01');
129
+ const endDate = new Date('2026-12-31');
20
130
 
21
- inputGroup.update(new Date('2026-01-01'));
131
+ const inputGroup = new InputGroup(startDate, endDate);
132
+
133
+ // or with default options
134
+ const inputGroupWithDefaultOptions = new InputGroup();
22
135
  ```
23
136
 
24
- ## Usage as range date picker
137
+ **Parameters:**
138
+ - `startDate` (Date, optional) - start date for the range of the day/month options - works only in monthly precision (defaults to one year ago)
139
+ - `endDate` (Date, optional) - end date for the range of the day/month options - works only in monthly precision (defaults to one year from now)
140
+
141
+ ### Usage
25
142
 
26
143
  ```javascript
27
- const inputGroupFrom = new InputGroup();
28
- inputDate.appendChild(inputGroupFrom.markup);
144
+ const startDate = new Date('2026-01-01');
145
+ const endDate = new Date('2026-12-31');
146
+
147
+ const inputGroupFrom = new InputGroup(startDate, endDate);
148
+ document.getElementById('input-date').appendChild(inputGroupFrom.getMarkup());
29
149
 
30
- const inputGroupTo = new InputGroup();
31
- inputDate.appendChild(inputGroupTo.markup);
150
+ const inputGroupTo = new InputGroup(startDate, endDate);
151
+ document.getElementById('input-date').appendChild(inputGroupTo.getMarkup());
32
152
 
33
153
  inputGroupFrom.update(new Date('2026-01-01'));
34
154
  inputGroupTo.update(new Date('2026-12-31'));
35
155
  ```
36
156
 
37
- ## Properties
157
+ ### Properties
158
+
159
+ - **`daySelect.select`** - Select element for day selection
160
+ - **`monthSelect.select`** - Select element for month selection
161
+ - **`yearSelect.select`** - Select element for year selection
162
+
163
+ ### Methods
164
+
165
+ #### `getMarkup()`
166
+ Returns the HTML markup for the input group to append it to the DOM.
167
+
168
+ **Returns:** DOM Element
169
+
170
+ #### `getMonthSelect()`
171
+ Returns the month select element for registering change listeners.
172
+
173
+ **Returns:** HTMLSelectElement
174
+
175
+ #### `getDaySelect()`
176
+ Returns the day select element for registering change listeners.
38
177
 
39
- - **`markup`** - The DOM element of the component that can be inserted into the document
40
- - **`daySelect.select`** - Select element for day selection, to register a change listener.
41
- - **`monthSelect.select`** - Select element for month selection, to register a change listener.
178
+ **Returns:** HTMLSelectElement
42
179
 
43
- ## Methods
180
+ #### `getValue()`
181
+ Returns the currently selected date as a ISO date string (YYYY-MM-DD).
44
182
 
45
- ### `update(date)`
183
+ **Returns:** String
184
+
185
+ #### `update(date)`
46
186
  Updates the input fields with a new date.
47
187
 
48
188
  **Parameters:**
49
189
  - `date` (Date) - The new date
50
190
 
51
- ### `setMinDate(date)`
191
+ #### `setMinDate(date)`
52
192
  Sets the minimum selectable date for the input fields.
53
193
 
54
194
  **Parameters:**
55
195
  - `date` (Date) - The minimum date
56
196
 
57
- ## Events
197
+ ### Events
58
198
 
59
199
  ```javascript
60
- inputGroupFrom.daySelect.select.addEventListener('change', (event) => {
61
- const newDate = new Date(event.target.value);
200
+ inputGroupFrom.getDaySelect().addEventListener('change', (event) => {
201
+ const newDate = new Date(inputGroupFrom.getValue());
62
202
  console.log('New date:', newDate);
63
203
  });
64
204
  ```
205
+
206
+ ---
207
+
208
+
209
+ ## InputRange
210
+
211
+ A component that connects two InputGroup instances to create a date range picker. The difference to SelectDatepicker is, that you have to handle the InputGroups individually.
212
+
213
+ ### Constructor
214
+
215
+ ```javascript
216
+ import { InputGroup, InputRange } from 'imx-select-datepicker';
217
+
218
+ const startDate = new Date('2026-01-01');
219
+ const endDate = new Date('2026-12-31');
220
+
221
+ const inputGroupFrom = new InputGroup(startDate, endDate);
222
+ const inputGroupTo = new InputGroup(startDate, endDate);
223
+
224
+ const inputRange = new InputRange(inputGroupFrom, inputGroupTo);
225
+ ```
226
+
227
+ **Parameters:**
228
+ - `inputGroupFrom` (InputGroup) - The InputGroup selecting the from date of the range
229
+ - `inputGroupTo` (InputGroup) - The InputGroup selecting the to date of the range
230
+
231
+ ---
232
+
233
+ ## Usage Examples
234
+
235
+ ### Single Date Picker (using InputGroup only)
236
+
237
+ ```javascript
238
+ const startDate = new Date('2026-01-01');
239
+ const endDate = new Date('2026-12-31');
240
+
241
+ const inputGroup = new InputGroup(startDate, endDate);
242
+ document.getElementById('datepicker').appendChild(inputGroup.getMarkup());
243
+
244
+ inputGroup.update(new Date('2026-12-01'));
245
+ ```
246
+
247
+ ### Range Date Picker
248
+
249
+ ```javascript
250
+ const startDate = new Date('2026-01-01');
251
+ const endDate = new Date('2026-12-31');
252
+
253
+ const inputGroupFrom = new InputGroup(startDate, endDate);
254
+ document.getElementById('datepicker-from').appendChild(inputGroupFrom.getMarkup());
255
+
256
+ const inputGroupTo = new InputGroup(startDate, endDate);
257
+ document.getElementById('datepicker-to').appendChild(inputGroupTo.getMarkup());
258
+
259
+ const inputRange = new InputRange(inputGroupFrom, inputGroupTo);
260
+
261
+ inputGroupFrom.update(new Date('2026-01-01'));
262
+ inputGroupTo.update(new Date('2026-12-31'));
263
+ ```
package/index.js CHANGED
@@ -1 +1,3 @@
1
- export { InputGroup } from "./src/InputGroup/InputGroup.js";
1
+ export { InputGroup } from "./src/InputGroup/InputGroup.js";
2
+ export { InputRange } from "./src/InputRange/InputRange.js";
3
+ export { SelectDatepicker } from "./src/SelectDatepicker/SelectDatepicker.js";
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ testEnvironment: "jsdom",
3
+ transform: {}
4
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imx-select-datepicker",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "A simple keyboard accessible select based datepicker",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -15,9 +15,13 @@
15
15
  "type": "module",
16
16
  "main": "index.js",
17
17
  "scripts": {
18
- "test": "echo \"Error: no test specified\" && exit 1"
18
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
19
19
  },
20
20
  "dependencies": {
21
21
  "dayjs": "^1.11.19"
22
+ },
23
+ "devDependencies": {
24
+ "jest": "^30.2.0",
25
+ "jest-environment-jsdom": "^30.2.0"
22
26
  }
23
27
  }
@@ -33,11 +33,37 @@ export class InputGroup {
33
33
  return this.daySelect.select.value;
34
34
  }
35
35
 
36
+ getMarkup() {
37
+ return this.markup;
38
+ }
39
+
40
+ getMonthSelect() {
41
+ return this.monthSelect.select;
42
+ }
43
+
44
+ getDaySelect() {
45
+ return this.daySelect.select;
46
+ }
47
+
48
+ isAvailable(jsDateObject) {
49
+ const dayJsDate = dayjs(jsDateObject);
50
+
51
+ const optionGroup = this.optionGroups.find((optionGroup) => {
52
+ return dayjs(optionGroup.index).isSame(dayJsDate, 'month');
53
+ });
54
+
55
+ return !!optionGroup;
56
+ }
57
+
36
58
  update(jsDateObject) {
37
59
  if(dayjs(jsDateObject).isValid()) {
38
- this.updateMonth(jsDateObject);
39
- this.updateDayOptions(jsDateObject);
40
- this.updateDay(jsDateObject);
60
+ if(this.isAvailable(jsDateObject)) {
61
+ this.updateMonth(jsDateObject);
62
+ this.updateDayOptions(jsDateObject);
63
+ this.updateDay(jsDateObject);
64
+ } else {
65
+ console.warn('Date is not available in the options:', jsDateObject);
66
+ }
41
67
  } else {
42
68
  console.warn('Update with invalid date:', jsDateObject);
43
69
  }
@@ -0,0 +1,30 @@
1
+ import { InputGroup } from './InputGroup.js';
2
+ import { jest } from '@jest/globals'
3
+
4
+ describe('InputGroup', () => {
5
+ let inputGroup;
6
+
7
+ beforeEach(() => {
8
+ jest.spyOn(console, 'warn').mockImplementation(() => {});
9
+ inputGroup = new InputGroup(new Date('2026-01-01'), new Date('2026-12-31'));
10
+ inputGroup.update(new Date('2026-03-15'));
11
+ });
12
+
13
+ afterEach(() => {
14
+ jest.restoreAllMocks();
15
+ });
16
+
17
+ test('sets date March 15th 2026', () => {
18
+ expect(inputGroup.getValue()).toEqual('2026-03-15');
19
+ });
20
+
21
+ test('sets date before the available options', () => {
22
+ inputGroup.update(new Date('2025-11-03'));
23
+ expect(inputGroup.getValue()).toEqual('2026-03-15');
24
+ });
25
+
26
+ test('sets date after the available options', () => {
27
+ inputGroup.update(new Date('2027-02-17'));
28
+ expect(inputGroup.getValue()).toEqual('2026-03-15');
29
+ });
30
+ });
@@ -57,6 +57,7 @@ export class DaySelect {
57
57
  if(dayOptions.length > 0) {
58
58
  this._select.innerHTML = dayOptions.map((day) => day.outerHTML).join('');
59
59
  this._evalMinDate();
60
+
60
61
  } else {
61
62
  console.warn('No options found for:', dayJsDate);
62
63
  }
@@ -39,4 +39,15 @@ export class MonthSelect {
39
39
  });
40
40
  }
41
41
  }
42
+
43
+ setMaxDate(jsDateObject) {
44
+ if(!jsDateObject) {
45
+ this.enableOptions();
46
+ } else {
47
+ const dayJsDate = dayjs(jsDateObject);
48
+ this.getOptions().forEach((option) => {
49
+ option.disabled = dayJsDate.isBefore(dayjs(option.value), 'month');
50
+ });
51
+ }
52
+ }
42
53
  }
@@ -1,11 +1,11 @@
1
1
  export class Option {
2
2
  static createMonthOption(date) {
3
- const formatted = date.format('MMMM YYYY');
3
+ const formatted = date.toDate().toLocaleDateString('de-DE', { month: 'long', year: 'numeric' });
4
4
  return this.createOption(date.format('YYYY-MM-DD'), formatted);
5
5
  }
6
6
 
7
7
  static createDayOption(date) {
8
- const formatted = date.format('ddd D');
8
+ const formatted = date.toDate().toLocaleDateString('de-DE', { weekday: 'short', day: '2-digit' })
9
9
  return this.createOption(date.format('YYYY-MM-DD'), formatted);
10
10
  }
11
11
 
@@ -0,0 +1,40 @@
1
+ import dayjs from 'dayjs';
2
+
3
+ export class InputRange {
4
+
5
+ constructor(inputGroupFrom, inputGroupTo) {
6
+ this.inputGroupFrom = inputGroupFrom;
7
+ this.inputGroupTo = inputGroupTo;
8
+
9
+ this.registerSync();
10
+ }
11
+
12
+ registerSync() {
13
+ const { inputGroupFrom, inputGroupTo } = this;
14
+
15
+ inputGroupFrom.getDaySelect().addEventListener('change', (event) => {
16
+ const newDate = new Date(event.target.value);
17
+
18
+ if(dayjs(newDate).isAfter(dayjs(inputGroupTo.getValue()), 'day')) {
19
+ inputGroupTo.update(newDate);
20
+ inputGroupTo.setMinDate(newDate);
21
+ inputGroupTo.getDaySelect().dispatchEvent(new Event('change'));
22
+ }
23
+ });
24
+
25
+ inputGroupFrom.getMonthSelect().addEventListener('change', (event) => {
26
+ const newDate = new Date(event.target.value);
27
+ inputGroupTo.setMinDate(newDate);
28
+ });
29
+
30
+ inputGroupTo.getMonthSelect().addEventListener('change', (event) => {
31
+ const newDate = new Date(event.target.value);
32
+
33
+ if(dayjs(newDate).isSame(dayjs(inputGroupFrom.getValue()), 'month')) {
34
+ const fromDate = new Date(inputGroupFrom.getValue());
35
+ inputGroupTo.update(fromDate);
36
+ inputGroupTo.setMinDate(fromDate);
37
+ }
38
+ });
39
+ }
40
+ }
@@ -0,0 +1,128 @@
1
+ import dayjs from 'dayjs';
2
+ import { InputGroup } from '../InputGroup/InputGroup.js';
3
+ import { InputRange } from '../InputRange/InputRange.js';
4
+
5
+ export class SelectDatepicker {
6
+
7
+ constructor(config) {
8
+ this.config = config;
9
+ this._init();
10
+ this.registerEventListeners();
11
+ }
12
+
13
+ _init() {
14
+ this.config.mode === 'range' ? this._initRange() : this._initSingle();
15
+ }
16
+
17
+ _initRange() {
18
+ const { start, end} = this._readAvailableOptions();
19
+
20
+ this.inputGroupFrom = new InputGroup(start, end);
21
+ this.inputGroupTo = new InputGroup(start, end);
22
+
23
+ new InputRange(this.inputGroupFrom, this.inputGroupTo);
24
+ }
25
+
26
+ _initSingle() {
27
+ this.inputGroupFrom = new InputGroup(this.config.availableOptions.start, this.config.availableOptions.end);
28
+ }
29
+
30
+ _readAvailableOptions() {
31
+ const defaultStart = dayjs().subtract(1, 'y').toDate();
32
+ const defaultEnd = dayjs().add(1, 'y').toDate();
33
+
34
+ const start = this.config.availableOptions?.start ?? defaultStart;
35
+ const end = this.config.availableOptions?.end ?? defaultEnd;
36
+
37
+ return {
38
+ start: start,
39
+ end: end
40
+ }
41
+ }
42
+
43
+ setMinDate(date) {
44
+ if(this.inputGroupFrom) {
45
+ this.inputGroupFrom.setMinDate(date);
46
+ }
47
+
48
+ if(this.inputGroupTo) {
49
+ this.inputGroupTo.setMinDate(date);
50
+ }
51
+ }
52
+
53
+ setDate(date) {
54
+ if(date instanceof Array) {
55
+ if(date[0]) {
56
+ this.inputGroupFrom.update(this._convertDate(date[0]));
57
+ }
58
+
59
+ if(date[1]) {
60
+ this.inputGroupTo.update(this._convertDate(date[1]));
61
+ }
62
+ } else {
63
+ this.inputGroupFrom.update(this._convertDate(date));
64
+ }
65
+ }
66
+
67
+ getDate() {
68
+ const value = [];
69
+
70
+ if(this.inputGroupFrom) {
71
+ value.push(this.inputGroupFrom.getValue());
72
+ }
73
+
74
+ if(this.inputGroupTo) {
75
+ value.push(this.inputGroupTo.getValue());
76
+ }
77
+
78
+ return value;
79
+ }
80
+
81
+ _convertDate(date) {
82
+ return dayjs(date).toDate();
83
+ }
84
+
85
+ getMarkup() {
86
+ const markup = document.createElement('div');
87
+ markup.className = 'imx-select-datepicker';
88
+
89
+ if(this.inputGroupFrom) {
90
+ markup.appendChild(this.inputGroupFrom.getMarkup());
91
+ }
92
+
93
+ if(this.inputGroupTo) {
94
+ markup.appendChild(this.inputGroupTo.getMarkup());
95
+ }
96
+
97
+ return markup;
98
+ }
99
+
100
+ getSelects() {
101
+ const selects = [];
102
+
103
+ if(this.inputGroupFrom) {
104
+ selects.push(this.inputGroupFrom.getDaySelect());
105
+ selects.push(this.inputGroupFrom.getMonthSelect());
106
+ }
107
+
108
+ if(this.inputGroupTo) {
109
+ selects.push(this.inputGroupTo.getDaySelect());
110
+ selects.push(this.inputGroupTo.getMonthSelect());
111
+ }
112
+
113
+ return selects;
114
+ }
115
+
116
+ registerEventListeners() {
117
+ const listenerTargets = this.getSelects();
118
+
119
+ listenerTargets.forEach(target => {
120
+ target.addEventListener('change', () => {
121
+ const selectedDates = this.getDate();
122
+ if(this.config.onChange && typeof this.config.onChange === 'function') {
123
+ this.config.onChange(selectedDates);
124
+ }
125
+ });
126
+ })
127
+ }
128
+ }
@@ -0,0 +1,160 @@
1
+ import { SelectDatepicker } from './SelectDatepicker.js';
2
+ import dayjs from 'dayjs';
3
+ import { jest } from '@jest/globals'
4
+
5
+ describe('SelectDatepicker', () => {
6
+ let datepicker;
7
+
8
+ const defaultDateString = '2026-01-01';
9
+
10
+ const config = {
11
+ mode: 'range',
12
+ availableOptions: {
13
+ start: new Date('2026-01-01'),
14
+ end: new Date('2026-12-31')
15
+ },
16
+ onChange: jest.fn()
17
+ };
18
+
19
+ beforeEach(() => {
20
+ jest.clearAllMocks();
21
+ jest.spyOn(console, 'warn').mockImplementation(() => {});
22
+ datepicker = new SelectDatepicker(config);
23
+ });
24
+
25
+ afterEach(() => {
26
+ jest.restoreAllMocks();
27
+ });
28
+
29
+ describe('initialization', () => {
30
+ test('initializes in range mode', () => {
31
+ expect(datepicker.inputGroupFrom).toBeDefined();
32
+ expect(datepicker.inputGroupTo).toBeDefined();
33
+ });
34
+
35
+ test('initializes in single mode', () => {
36
+ const singleConfig = {
37
+ mode: 'single',
38
+ availableOptions: {
39
+ start: new Date('2026-01-01'),
40
+ end: new Date('2026-12-31')
41
+ }
42
+ };
43
+ const singleDatepicker = new SelectDatepicker(singleConfig);
44
+ expect(singleDatepicker.inputGroupFrom).toBeDefined();
45
+ expect(singleDatepicker.inputGroupTo).toBeUndefined();
46
+ });
47
+ });
48
+
49
+ describe('setDate', () => {
50
+ test('sets single date in range mode', () => {
51
+ const dateString = '2026-03-15';
52
+ const date = new Date(dateString);
53
+ datepicker.setDate(date);
54
+ expect(datepicker.getDate()).toEqual([dateString, defaultDateString]);
55
+ });
56
+
57
+ test('sets date range with array', () => {
58
+ const fromDateString = '2026-03-15';
59
+ const toDateString = '2026-06-20';
60
+
61
+ const fromDate = new Date(fromDateString);
62
+ const toDate = new Date(toDateString);
63
+
64
+ datepicker.setDate([fromDate, toDate]);
65
+ expect(datepicker.getDate()).toEqual([fromDateString, toDateString]);
66
+ });
67
+
68
+ test('handles partial array (only start date)', () => {
69
+ const fromDateString = '2026-03-15';
70
+ const fromDate = new Date(fromDateString);
71
+
72
+ datepicker.setDate([fromDate]);
73
+ expect(datepicker.getDate()).toEqual([fromDateString, defaultDateString]);
74
+ });
75
+
76
+ test('sets an invalid date', () => {
77
+ const fromDateString = 'invalid';
78
+ const fromDate = new Date(fromDateString);
79
+
80
+ datepicker.setDate([fromDate]);
81
+ expect(datepicker.getDate()).toEqual([defaultDateString, defaultDateString]);
82
+ });
83
+ });
84
+
85
+ describe('getDate', () => {
86
+ test('returns array of selected dates', () => {
87
+ datepicker.setDate([new Date('2026-03-15'), new Date('2026-06-20')]);
88
+ const dates = datepicker.getDate();
89
+
90
+ expect(Array.isArray(dates)).toBe(true);
91
+ expect(dates.length).toBe(2);
92
+ });
93
+
94
+ test('returns single date in range mode', () => {
95
+ datepicker.setDate(new Date('2026-03-15'));
96
+ const dates = datepicker.getDate();
97
+
98
+ expect(dates.length).toBeGreaterThan(0);
99
+ });
100
+
101
+ test('returns the default dates if no availableOptions are passed', () => {
102
+ const emptyDatepicker = new SelectDatepicker({
103
+ mode: 'range',
104
+ });
105
+ const dates = emptyDatepicker.getDate();
106
+ expect(dates).toEqual([dayjs().subtract(1, 'year').startOf('month').format('YYYY-MM-DD'), dayjs().subtract(1, 'year').startOf('month').format('YYYY-MM-DD')]);
107
+ });
108
+ });
109
+
110
+ describe('getMarkup', () => {
111
+ test('returns a div element', () => {
112
+ const markup = datepicker.getMarkup();
113
+ expect(markup.tagName).toBe('DIV');
114
+ expect(markup.className).toBe('imx-select-datepicker');
115
+ });
116
+
117
+ test('includes both input groups in markup', () => {
118
+ const markup = datepicker.getMarkup();
119
+ expect(markup.children.length).toBe(2);
120
+ });
121
+ });
122
+
123
+ describe('getSelects', () => {
124
+ test('returns array of select elements', () => {
125
+ const selects = datepicker.getSelects();
126
+ expect(Array.isArray(selects)).toBe(true);
127
+ expect(selects.length).toBeGreaterThan(0);
128
+ });
129
+
130
+ test('returns 4 selects in range mode (day, month x2)', () => {
131
+ const selects = datepicker.getSelects();
132
+ expect(selects.length).toBe(4);
133
+ });
134
+ });
135
+
136
+ describe('registerEventListeners', () => {
137
+ test('calls onChange callback on select change', () => {
138
+ const selects = datepicker.getSelects();
139
+ const event = new Event('change');
140
+
141
+ selects[0].dispatchEvent(event);
142
+ expect(config.onChange).toHaveBeenCalled();
143
+ });
144
+
145
+ test('does not call onChange if not provided', () => {
146
+ const noCbConfig = {
147
+ mode: 'range',
148
+ availableOptions: {
149
+ start: new Date('2026-01-01'),
150
+ end: new Date('2026-12-31')
151
+ }
152
+ };
153
+ const noCbDatepicker = new SelectDatepicker(noCbConfig);
154
+ const selects = noCbDatepicker.getSelects();
155
+ const event = new Event('change');
156
+
157
+ expect(() => selects[0].dispatchEvent(event)).not.toThrow();
158
+ });
159
+ });
160
+ });