imx-select-datepicker 0.0.2 → 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,83 +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
 
18
+ **Parameters:**
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
29
+
11
30
  ```javascript
12
- const inputGroupFrom = new InputGroup();
13
- const inputGroupTo = new InputGroup();
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
+ });
14
41
 
15
- const inputRange = new InputRange(inputGroupFrom, inputGroupTo);
42
+ document.getElementById('datepicker').appendChild(selectDatepicker.getMarkup());
43
+ ```
44
+
45
+ #### Date Range Mode
46
+
47
+ ```javascript
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
+ ]);
16
84
  ```
17
85
 
86
+ #### `setMinDate(date)`
87
+ Sets the minimal selectable (non disabled) date.
88
+
18
89
  **Parameters:**
19
- - `startDate` (Date, optional) - The start date for the possible select options. Defaults to the current date minus 1 year.
20
- - `endDate` (Date, optional) - The end date for the possible select options. Defaults to the current date plus 1 year.
90
+ - `date` (Date) - A single date.
91
+
92
+ #### `getDate()`
93
+ Returns the currently selected dates.
21
94
 
22
- ## Usage as single date picker (use InputGroup only)
95
+ **Returns:** Array\<String\> - Array with one or two ISO date strings (YYYY-MM-DD)
23
96
 
24
97
  ```javascript
25
- const inputGroup = new InputGroup();
26
- inputDate.appendChild(inputGroup.markup);
98
+ const selectedDates = selectDatepicker.getDate();
99
+ console.log(selectedDates); // ['2026-06-01', '2026-06-30'] or ['2026-06-15']
100
+ ```
27
101
 
28
- inputGroup.update(new Date('2026-12-01'));
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
+ });
29
114
  ```
30
115
 
31
- ## Usage as range date picker
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
32
124
 
33
125
  ```javascript
34
- const inputGroupFrom = new InputGroup();
35
- inputDate.appendChild(inputGroupFrom.markup);
126
+ import { InputGroup } from 'imx-select-datepicker';
36
127
 
37
- const inputGroupTo = new InputGroup();
38
- inputDate.appendChild(inputGroupTo.markup);
128
+ const startDate = new Date('2026-01-01');
129
+ const endDate = new Date('2026-12-31');
39
130
 
40
- const inputRange = new InputRange(inputGroupFrom, inputGroupTo);
131
+ const inputGroup = new InputGroup(startDate, endDate);
132
+
133
+ // or with default options
134
+ const inputGroupWithDefaultOptions = new InputGroup();
135
+ ```
136
+
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
142
+
143
+ ```javascript
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());
149
+
150
+ const inputGroupTo = new InputGroup(startDate, endDate);
151
+ document.getElementById('input-date').appendChild(inputGroupTo.getMarkup());
41
152
 
42
153
  inputGroupFrom.update(new Date('2026-01-01'));
43
154
  inputGroupTo.update(new Date('2026-12-31'));
44
155
  ```
45
156
 
46
- ## 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
47
162
 
48
- - **`daySelect.select`** - Select element for day selection, to register a change listener.
49
- - **`monthSelect.select`** - Select element for month selection, to register a change listener.
163
+ ### Methods
50
164
 
51
- ## Methods
52
- ### `getMarkup()`
165
+ #### `getMarkup()`
53
166
  Returns the HTML markup for the input group to append it to the DOM.
54
167
 
55
- ### `getMonthSelect()`
168
+ **Returns:** DOM Element
169
+
170
+ #### `getMonthSelect()`
56
171
  Returns the month select element for registering change listeners.
57
172
 
58
- ### `getDaySelect()`
173
+ **Returns:** HTMLSelectElement
174
+
175
+ #### `getDaySelect()`
59
176
  Returns the day select element for registering change listeners.
60
177
 
61
- ### `getValue()`
178
+ **Returns:** HTMLSelectElement
179
+
180
+ #### `getValue()`
62
181
  Returns the currently selected date as a ISO date string (YYYY-MM-DD).
63
182
 
64
- ### `update(date)`
183
+ **Returns:** String
184
+
185
+ #### `update(date)`
65
186
  Updates the input fields with a new date.
66
187
 
67
188
  **Parameters:**
68
189
  - `date` (Date) - The new date
69
190
 
70
- ### `setMinDate(date)`
191
+ #### `setMinDate(date)`
71
192
  Sets the minimum selectable date for the input fields.
72
193
 
73
194
  **Parameters:**
74
195
  - `date` (Date) - The minimum date
75
196
 
76
- ## Events
197
+ ### Events
77
198
 
78
199
  ```javascript
79
200
  inputGroupFrom.getDaySelect().addEventListener('change', (event) => {
80
- const newDate = new Date(event.target.value);
201
+ const newDate = new Date(inputGroupFrom.getValue());
81
202
  console.log('New date:', newDate);
82
203
  });
83
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,2 +1,3 @@
1
1
  export { InputGroup } from "./src/InputGroup/InputGroup.js";
2
- export { InputRange } from "./src/InputRange/InputRange.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.2",
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
  }
@@ -45,11 +45,25 @@ export class InputGroup {
45
45
  return this.daySelect.select;
46
46
  }
47
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
+
48
58
  update(jsDateObject) {
49
59
  if(dayjs(jsDateObject).isValid()) {
50
- this.updateMonth(jsDateObject);
51
- this.updateDayOptions(jsDateObject);
52
- 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
+ }
53
67
  } else {
54
68
  console.warn('Update with invalid date:', jsDateObject);
55
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
  }
@@ -14,10 +14,10 @@ export class InputRange {
14
14
 
15
15
  inputGroupFrom.getDaySelect().addEventListener('change', (event) => {
16
16
  const newDate = new Date(event.target.value);
17
- inputGroupTo.setMinDate(newDate);
18
17
 
19
18
  if(dayjs(newDate).isAfter(dayjs(inputGroupTo.getValue()), 'day')) {
20
19
  inputGroupTo.update(newDate);
20
+ inputGroupTo.setMinDate(newDate);
21
21
  inputGroupTo.getDaySelect().dispatchEvent(new Event('change'));
22
22
  }
23
23
  });
@@ -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
+ });