@ministryofjustice/frontend 3.3.0 → 3.4.0

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.
Files changed (87) hide show
  1. package/README.md +4 -10
  2. package/govuk-prototype-kit.config.json +5 -16
  3. package/moj/all.jquery.min.js +77 -3
  4. package/moj/all.js +2022 -1444
  5. package/moj/all.scss +2 -0
  6. package/moj/all.spec.js +15 -13
  7. package/moj/components/_all.scss +1 -0
  8. package/moj/components/action-bar/_action-bar.scss +4 -6
  9. package/moj/components/add-another/_add-another.scss +9 -7
  10. package/moj/components/add-another/add-another.js +90 -69
  11. package/moj/components/add-another/add-another.spec.js +165 -0
  12. package/moj/components/alert/README.md +0 -0
  13. package/moj/components/alert/_alert.scss +142 -0
  14. package/moj/components/alert/alert.js +247 -0
  15. package/moj/components/alert/alert.spec.helper.js +67 -0
  16. package/moj/components/alert/alert.spec.js +229 -0
  17. package/moj/components/alert/macro.njk +3 -0
  18. package/moj/components/alert/template.njk +83 -0
  19. package/moj/components/badge/_badge.scss +3 -4
  20. package/moj/components/banner/_banner.scss +5 -10
  21. package/moj/components/button-menu/_button-menu.scss +10 -9
  22. package/moj/components/button-menu/button-menu.js +139 -136
  23. package/moj/components/button-menu/button-menu.spec.js +295 -296
  24. package/moj/components/cookie-banner/_cookie-banner.scss +6 -5
  25. package/moj/components/currency-input/_currency-input.scss +4 -4
  26. package/moj/components/date-picker/README.md +14 -17
  27. package/moj/components/date-picker/_date-picker.scss +122 -106
  28. package/moj/components/date-picker/date-picker.js +473 -471
  29. package/moj/components/date-picker/date-picker.spec.js +962 -914
  30. package/moj/components/filter/README.md +1 -1
  31. package/moj/components/filter/_filter.scss +53 -75
  32. package/moj/components/filter-toggle-button/filter-toggle-button.js +71 -67
  33. package/moj/components/filter-toggle-button/filter-toggle-button.spec.js +203 -205
  34. package/moj/components/form-validator/form-validator.js +117 -109
  35. package/moj/components/header/_header.scss +17 -19
  36. package/moj/components/identity-bar/_identity-bar.scss +5 -5
  37. package/moj/components/interruption-card/_interruption-card.scss +9 -2
  38. package/moj/components/messages/_messages.scss +12 -19
  39. package/moj/components/multi-file-upload/README.md +1 -1
  40. package/moj/components/multi-file-upload/_multi-file-upload.scss +34 -30
  41. package/moj/components/multi-file-upload/multi-file-upload.js +188 -152
  42. package/moj/components/multi-file-upload/multi-file-upload.spec.js +510 -0
  43. package/moj/components/multi-select/_multi-select.scss +4 -3
  44. package/moj/components/multi-select/multi-select.js +55 -50
  45. package/moj/components/multi-select/multi-select.spec.js +128 -0
  46. package/moj/components/notification-badge/_notification-badge.scss +12 -12
  47. package/moj/components/organisation-switcher/_organisation-switcher.scss +1 -1
  48. package/moj/components/page-header-actions/_page-header-actions.scss +3 -2
  49. package/moj/components/pagination/_pagination.scss +26 -31
  50. package/moj/components/password-reveal/_password-reveal.scss +1 -2
  51. package/moj/components/password-reveal/password-reveal.js +22 -21
  52. package/moj/components/password-reveal/password-reveal.spec.js +39 -37
  53. package/moj/components/primary-navigation/_primary-navigation.scss +26 -29
  54. package/moj/components/progress-bar/_progress-bar.scss +21 -26
  55. package/moj/components/rich-text-editor/_rich-text-editor.scss +17 -16
  56. package/moj/components/rich-text-editor/rich-text-editor.js +117 -103
  57. package/moj/components/search/_search.scss +6 -4
  58. package/moj/components/search-toggle/search-toggle.js +29 -30
  59. package/moj/components/search-toggle/search-toggle.scss +21 -15
  60. package/moj/components/search-toggle/search-toggle.spec.js +129 -0
  61. package/moj/components/side-navigation/_side-navigation.scss +12 -21
  62. package/moj/components/sortable-table/_sortable-table.scss +25 -23
  63. package/moj/components/sortable-table/sortable-table.js +139 -117
  64. package/moj/components/sortable-table/sortable-table.spec.js +362 -0
  65. package/moj/components/sub-navigation/_sub-navigation.scss +24 -28
  66. package/moj/components/tag/_tag.scss +8 -9
  67. package/moj/components/task-list/_task-list.scss +8 -7
  68. package/moj/components/ticket-panel/_ticket-panel.scss +14 -6
  69. package/moj/components/timeline/_timeline.scss +18 -20
  70. package/moj/filters/all.js +28 -30
  71. package/moj/filters/prototype-kit-13-filters.js +2 -1
  72. package/moj/helpers/_all.scss +1 -0
  73. package/moj/helpers/_hidden.scss +1 -1
  74. package/moj/helpers/_links.scss +20 -0
  75. package/moj/helpers.js +160 -31
  76. package/moj/helpers.spec.js +235 -0
  77. package/moj/init.js +2 -2
  78. package/moj/moj-frontend.min.css +2 -2
  79. package/moj/moj-frontend.min.js +77 -3
  80. package/moj/namespace.js +2 -1
  81. package/moj/objects/_filter-layout.scss +11 -10
  82. package/moj/objects/_scrollable-pane.scss +11 -14
  83. package/moj/settings/_colours.scss +5 -0
  84. package/moj/settings/_measurements.scss +0 -2
  85. package/moj/utilities/_hidden.scss +3 -3
  86. package/moj/utilities/_width-container.scss +1 -1
  87. package/package.json +1 -1
@@ -4,40 +4,40 @@ const {
4
4
  getByRole,
5
5
  queryByRole,
6
6
  queryByText,
7
- screen,
8
- } = require("@testing-library/dom");
9
- const { userEvent } = require("@testing-library/user-event");
10
- const { configureAxe } = require("jest-axe");
11
- const dayjs = require("dayjs");
7
+ screen
8
+ } = require('@testing-library/dom')
9
+ const { userEvent } = require('@testing-library/user-event')
10
+ const dayjs = require('dayjs')
11
+ const { configureAxe } = require('jest-axe')
12
12
 
13
- require("./date-picker.js");
13
+ require('./date-picker.js')
14
14
 
15
- const user = userEvent.setup();
15
+ const user = userEvent.setup()
16
16
  const axe = configureAxe({
17
17
  rules: {
18
18
  // disable landmark rules when testing isolated components.
19
- region: { enabled: false },
20
- },
21
- });
19
+ region: { enabled: false }
20
+ }
21
+ })
22
22
 
23
23
  const kebabize = (str) => {
24
24
  return str.replace(
25
25
  /[A-Z]+(?![a-z])|[A-Z]/g,
26
- ($, ofset) => (ofset ? "-" : "") + $.toLowerCase(),
27
- );
28
- };
26
+ ($, ofset) => (ofset ? '-' : '') + $.toLowerCase()
27
+ )
28
+ }
29
29
 
30
30
  const configToDataAttributes = (config) => {
31
- let attributes = "";
32
- for (let [key, value] of Object.entries(config)) {
33
- attributes += `data-${kebabize(key)}="${value}" `;
31
+ let attributes = ''
32
+ for (const [key, value] of Object.entries(config)) {
33
+ attributes += `data-${kebabize(key)}="${value}" `
34
34
  }
35
- return attributes;
36
- };
35
+ return attributes
36
+ }
37
37
 
38
38
  const createComponent = (config = {}, html) => {
39
- const dataAttributes = configToDataAttributes(config);
40
- if (typeof html === "undefined") {
39
+ const dataAttributes = configToDataAttributes(config)
40
+ if (typeof html === 'undefined') {
41
41
  html = `
42
42
  <div class="moj-datepicker" data-module="moj-date-picker" ${dataAttributes}>
43
43
  <div class="govuk-form-group">
@@ -49,1082 +49,1130 @@ const createComponent = (config = {}, html) => {
49
49
  </div>
50
50
  <input class="govuk-input moj-js-datepicker-input " id="date" name="date" type="text" aria-describedby="date-hint" autocomplete="off">
51
51
  </div>
52
- </div>`;
52
+ </div>`
53
53
  }
54
- document.body.insertAdjacentHTML("afterbegin", html);
55
-
56
- component = document.querySelector('[data-module="moj-date-picker"]');
57
- return component;
58
- };
54
+ document.body.insertAdjacentHTML('afterbegin', html)
55
+ const component = document.querySelector('[data-module="moj-date-picker"]')
56
+ return component
57
+ }
59
58
 
60
59
  const randomIntBetween = (min, max) => {
61
- return Math.floor(Math.random() * (max - min + 1)) + min;
62
- };
63
-
64
- const padToTwoDigits = (number) => {
65
- return number.toString().padStart(2, "0");
66
- };
60
+ return Math.floor(Math.random() * (max - min + 1)) + min
61
+ }
67
62
 
68
63
  const getFirstDayOfWeek = (dateObject, firstDayOfWeekIndex) => {
69
- const dayOfWeek = dateObject.getDay();
70
- const firstDayOfWeek = new Date(dateObject);
64
+ const dayOfWeek = dateObject.getDay()
65
+ const firstDayOfWeek = new Date(dateObject)
71
66
  const diff =
72
67
  dayOfWeek >= firstDayOfWeekIndex
73
68
  ? dayOfWeek - firstDayOfWeekIndex
74
- : 6 - dayOfWeek;
69
+ : 6 - dayOfWeek
75
70
 
76
- firstDayOfWeek.setDate(dateObject.getDate() - diff);
77
- firstDayOfWeek.setHours(0, 0, 0, 0);
71
+ firstDayOfWeek.setDate(dateObject.getDate() - diff)
72
+ firstDayOfWeek.setHours(0, 0, 0, 0)
78
73
 
79
- return firstDayOfWeek;
80
- };
74
+ return firstDayOfWeek
75
+ }
81
76
 
82
77
  const getLastDayOfWeek = (dateObject, lastDayOfWeekIndex) => {
83
- const dayOfWeek = dateObject.getDay();
84
- const lastDayOfWeek = new Date(dateObject);
78
+ const dayOfWeek = dateObject.getDay()
79
+ const lastDayOfWeek = new Date(dateObject)
85
80
  const diff =
86
81
  dayOfWeek <= lastDayOfWeekIndex
87
82
  ? lastDayOfWeekIndex - dayOfWeek
88
- : 7 - dayOfWeek;
83
+ : 7 - dayOfWeek
89
84
 
90
- lastDayOfWeek.setDate(dateObject.getDate() - diff);
91
- lastDayOfWeek.setHours(0, 0, 0, 0);
85
+ lastDayOfWeek.setDate(dateObject.getDate() - diff)
86
+ lastDayOfWeek.setHours(0, 0, 0, 0)
92
87
 
93
- return lastDayOfWeek;
94
- };
88
+ return lastDayOfWeek
89
+ }
90
+
91
+ const getDateFormatted = (value) => {
92
+ return dayjs().date(value).startOf('day').format('D/M/YYYY')
93
+ }
95
94
 
96
95
  const getDateInCurrentMonth = (excluding = []) => {
97
- const today = dayjs().date();
98
- excluding.push(today);
99
- const lastDayOfMonth = dayjs().endOf("month").date();
100
- const days = range(1, lastDayOfMonth).filter((x) => !excluding.includes(x));
101
-
102
- return days[Math.floor(Math.random() * days.length)];
103
- };
104
-
105
- const getDateRangeInCurrentMonth = (startDay, endDay) => {
106
- let date = dayjs().date(startDay); // Convert the start date to a Day.js object
107
- const endDate = dayjs().date(endDay + 1);
108
- const dates = [];
109
-
110
- while (date.isBefore(endDate)) {
111
- dates.push(date);
112
- date = date.add(1, "day");
113
- }
96
+ const today = dayjs().date()
97
+ excluding.push(today)
98
+ const lastDayOfMonth = dayjs().endOf('month').date()
99
+ const days = range(1, lastDayOfMonth).filter((x) => !excluding.includes(x))
114
100
 
115
- return dates;
116
- };
101
+ return days[Math.floor(Math.random() * days.length)]
102
+ }
117
103
 
118
104
  const range = (start, end) => {
119
- return [...Array(end - start + 1).keys()].map((x) => x + start);
120
- };
105
+ return [...Array(end - start + 1).keys()].map((x) => x + start)
106
+ }
121
107
 
122
- describe("Date picker with defaults", () => {
123
- let component;
124
- let calendarButton;
125
- let dialog;
108
+ describe('Date picker with defaults', () => {
109
+ let component
110
+ let calendarButton
111
+ let dialog
126
112
 
127
113
  beforeEach(() => {
128
- component = createComponent();
129
- new MOJFrontend.DatePicker(component, {}).init();
114
+ component = createComponent()
115
+ new MOJFrontend.DatePicker(component, {}).init()
130
116
 
131
- calendarButton = queryByText(component, "Choose date")?.closest("button");
132
- dialog = queryByRole(component, "dialog", { hidden: true });
133
- });
117
+ calendarButton = queryByText(component, 'Choose date')?.closest('button')
118
+ dialog = queryByRole(component, 'dialog', { hidden: true })
119
+ })
134
120
 
135
121
  afterEach(() => {
136
- document.body.innerHTML = "";
137
- });
138
-
139
- test("initialises calendar calendarButton and dialog", () => {
140
- expect(calendarButton).not.toBeNull();
141
- expect(dialog).not.toBeNull();
142
- expect(component).toContainElement(calendarButton);
143
- expect(component).toContainElement(dialog);
144
- expect(dialog).not.toBeVisible();
145
- });
146
-
147
- test("calendar button toggles dialog", async () => {
148
- await user.click(calendarButton);
149
- expect(dialog).toBeVisible();
150
-
151
- await user.click(calendarButton);
152
- expect(dialog).not.toBeVisible();
153
- });
154
-
155
- test("dialog has required buttons", async () => {
156
- await user.click(calendarButton);
157
- let selectButton = queryByText(dialog, "Select");
158
- let closeButton = queryByText(dialog, "Close");
159
- let prevMonthButton = queryByText(dialog, "Previous month");
160
- let prevYearButton = queryByText(dialog, "Previous year");
161
- let nextMonthButton = queryByText(dialog, "Next month");
162
- let nextYearButton = queryByText(dialog, "Next year");
163
-
164
- expect(selectButton).not.toBeNull();
165
- expect(closeButton).not.toBeNull();
166
- expect(prevMonthButton).not.toBeNull();
167
- expect(prevYearButton).not.toBeNull();
168
- expect(nextMonthButton).not.toBeNull();
169
- expect(nextYearButton).not.toBeNull();
170
- });
171
-
172
- test("calendar opens with current month and year", async () => {
173
- await user.click(calendarButton);
174
- const today = new Date();
175
- const currentMonthName = today.toLocaleString("default", { month: "long" });
176
- const currentYear = today.getFullYear();
177
- const dialogTitle = `${currentMonthName} ${currentYear}`;
178
-
179
- expect(dialog).toContainElement(screen.getByText(dialogTitle));
180
- });
181
-
182
- test("today is selected", async () => {
183
- await user.click(calendarButton);
184
- const today = new Date();
185
- const todayButton = getByRole(dialog, "button", { current: "date" });
186
-
187
- expect(todayButton).toHaveFocus();
122
+ document.body.innerHTML = ''
123
+ })
124
+
125
+ test('initialises calendar calendarButton and dialog', () => {
126
+ expect(calendarButton).not.toBeNull()
127
+ expect(dialog).not.toBeNull()
128
+ expect(component).toContainElement(calendarButton)
129
+ expect(component).toContainElement(dialog)
130
+ expect(dialog).not.toBeVisible()
131
+ })
132
+
133
+ test('calendar button toggles dialog', async () => {
134
+ await user.click(calendarButton)
135
+ expect(dialog).toBeVisible()
136
+
137
+ await user.click(calendarButton)
138
+ expect(dialog).not.toBeVisible()
139
+ })
140
+
141
+ test('dialog has required buttons', async () => {
142
+ await user.click(calendarButton)
143
+ const selectButton = queryByText(dialog, 'Select')
144
+ const closeButton = queryByText(dialog, 'Close')
145
+ const prevMonthButton = queryByText(dialog, 'Previous month')
146
+ const prevYearButton = queryByText(dialog, 'Previous year')
147
+ const nextMonthButton = queryByText(dialog, 'Next month')
148
+ const nextYearButton = queryByText(dialog, 'Next year')
149
+
150
+ expect(selectButton).toBeInTheDocument()
151
+ expect(closeButton).toBeInTheDocument()
152
+ expect(prevMonthButton).toBeInTheDocument()
153
+ expect(prevYearButton).toBeInTheDocument()
154
+ expect(nextMonthButton).toBeInTheDocument()
155
+ expect(nextYearButton).toBeInTheDocument()
156
+ })
157
+
158
+ test('calendar opens with current month and year', async () => {
159
+ await user.click(calendarButton)
160
+ const today = new Date()
161
+ const currentMonthName = today.toLocaleString('default', { month: 'long' })
162
+ const currentYear = today.getFullYear()
163
+ const dialogTitle = `${currentMonthName} ${currentYear}`
164
+
165
+ expect(dialog).toContainElement(screen.getByText(dialogTitle))
166
+ })
167
+
168
+ test('today is selected', async () => {
169
+ await user.click(calendarButton)
170
+ const today = new Date()
171
+ const todayButton = getByRole(dialog, 'button', { current: 'date' })
172
+
173
+ expect(todayButton).toHaveFocus()
188
174
  expect(todayButton).toHaveClass(
189
- "moj-datepicker__button--selected",
190
- "moj-datepicker__button--current",
191
- "moj-datepicker__button--today",
192
- );
193
- expect(todayButton.textContent).toContain(`${today.getDate()}`);
194
- });
195
-
196
- test("can navigate back in time", async () => {
197
- const today = dayjs();
175
+ 'moj-datepicker__button--selected',
176
+ 'moj-datepicker__button--current',
177
+ 'moj-datepicker__button--today'
178
+ )
179
+ expect(todayButton).toHaveTextContent(new RegExp(`${today.getDate()}`))
180
+ })
181
+
182
+ test('can navigate back in time', async () => {
183
+ const today = dayjs()
198
184
  const previousMonth = dayjs().subtract(1, 'month')
199
185
  const previousYear = previousMonth.subtract(1, 'year')
200
186
 
201
- const currentTitle = `${today.format('MMMM YYYY')}`;
202
- const previousMonthTitle = `${previousMonth.format('MMMM YYYY')}`;
203
- const previousYearTitle = `${previousYear.format('MMMM YYYY')}`;
187
+ const currentTitle = `${today.format('MMMM YYYY')}`
188
+ const previousMonthTitle = `${previousMonth.format('MMMM YYYY')}`
189
+ const previousYearTitle = `${previousYear.format('MMMM YYYY')}`
204
190
 
205
- await user.click(calendarButton);
206
- let prevMonthButton = getByText(dialog, "Previous month");
207
- let prevYearButton = getByText(dialog, "Previous year");
191
+ await user.click(calendarButton)
192
+ const prevMonthButton = getByText(dialog, 'Previous month')
193
+ const prevYearButton = getByText(dialog, 'Previous year')
208
194
 
209
- expect(dialog).toContainElement(screen.getByText(currentTitle));
210
- await user.click(prevMonthButton);
211
- expect(dialog).toContainElement(screen.getByText(previousMonthTitle));
212
- await user.click(prevYearButton);
213
- expect(dialog).toContainElement(screen.getByText(previousYearTitle));
214
- });
195
+ expect(dialog).toContainElement(screen.getByText(currentTitle))
196
+ await user.click(prevMonthButton)
197
+ expect(dialog).toContainElement(screen.getByText(previousMonthTitle))
198
+ await user.click(prevYearButton)
199
+ expect(dialog).toContainElement(screen.getByText(previousYearTitle))
200
+ })
215
201
 
216
- test("can navigate forward in time", async () => {
217
- const today = dayjs();
202
+ test('can navigate forward in time', async () => {
203
+ const today = dayjs()
218
204
  const nextMonth = dayjs().add(1, 'month')
219
205
  const nextYear = nextMonth.add(1, 'year')
220
206
 
221
- const currentTitle = `${today.format('MMMM YYYY')}`;
222
- const nextMonthTitle = `${nextMonth.format('MMMM YYYY')}`;
223
- const nextYearTitle = `${nextYear.format('MMMM YYYY')}`;
224
-
225
- await user.click(calendarButton);
226
- let nextMonthButton = getByText(dialog, "Next month");
227
- let nextYearButton = getByText(dialog, "Next year");
228
-
229
- expect(dialog).toContainElement(screen.getByText(currentTitle));
230
- await user.click(nextMonthButton);
231
- expect(dialog).toContainElement(screen.getByText(nextMonthTitle));
232
- await user.click(nextYearButton);
233
- expect(dialog).toContainElement(screen.getByText(nextYearTitle));
234
- });
235
-
236
- test("close button closes the calendar popup", async () => {
237
- await user.click(calendarButton);
238
- let closeButton = queryByText(dialog, "Close");
239
-
240
- expect(dialog).toBeVisible();
241
- await user.click(closeButton);
242
- expect(dialog).not.toBeVisible();
243
- expect(calendarButton).toHaveFocus();
244
- });
245
-
246
- test("clicking outside closes the calendar popup", async () => {
247
- let hint = screen.getByText("For example, 17/5/2024.");
248
-
249
- await user.click(calendarButton);
250
- expect(dialog).toBeVisible();
251
- await user.click(hint);
252
- expect(dialog).not.toBeVisible();
253
- });
254
-
255
- describe("date picker with initial value", () => {
256
- let inputString;
257
- let dateString;
258
- let input;
259
- let selectedDate;
260
- let newDate;
207
+ const currentTitle = `${today.format('MMMM YYYY')}`
208
+ const nextMonthTitle = `${nextMonth.format('MMMM YYYY')}`
209
+ const nextYearTitle = `${nextYear.format('MMMM YYYY')}`
210
+
211
+ await user.click(calendarButton)
212
+ const nextMonthButton = getByText(dialog, 'Next month')
213
+ const nextYearButton = getByText(dialog, 'Next year')
214
+
215
+ expect(dialog).toContainElement(screen.getByText(currentTitle))
216
+ await user.click(nextMonthButton)
217
+ expect(dialog).toContainElement(screen.getByText(nextMonthTitle))
218
+ await user.click(nextYearButton)
219
+ expect(dialog).toContainElement(screen.getByText(nextYearTitle))
220
+ })
221
+
222
+ test('close button closes the calendar popup', async () => {
223
+ await user.click(calendarButton)
224
+ const closeButton = queryByText(dialog, 'Close')
225
+
226
+ expect(dialog).toBeVisible()
227
+ await user.click(closeButton)
228
+ expect(dialog).not.toBeVisible()
229
+ expect(calendarButton).toHaveFocus()
230
+ })
231
+
232
+ test('clicking outside closes the calendar popup', async () => {
233
+ const hint = screen.getByText('For example, 17/5/2024.')
234
+
235
+ await user.click(calendarButton)
236
+ expect(dialog).toBeVisible()
237
+ await user.click(hint)
238
+ expect(dialog).not.toBeVisible()
239
+ })
240
+
241
+ describe('date picker with initial value', () => {
242
+ let inputString
243
+ let dateString
244
+ let input
245
+ let selectedDate
246
+ let newDate
261
247
 
262
248
  beforeEach(async () => {
263
- inputString = "19/05/2024";
264
- dateString = "2024-05-19";
265
- input = screen.getByLabelText("Date");
266
- selectedDate = new Date(dateString);
249
+ inputString = '19/05/2024'
250
+ dateString = '2024-05-19'
251
+ input = screen.getByLabelText('Date')
252
+ selectedDate = new Date(dateString)
267
253
 
268
- while (newDate != selectedDate.getDate()) {
269
- newDate = randomIntBetween(7, 21); //outside this we could have duplicate hidden buttons from prev/next month
254
+ while (newDate !== selectedDate.getDate()) {
255
+ newDate = randomIntBetween(7, 21) // outside this we could have duplicate hidden buttons from prev/next month
270
256
  }
271
257
 
272
- await user.type(input, inputString);
273
- await user.click(calendarButton);
274
- });
258
+ await user.type(input, inputString)
259
+ await user.click(calendarButton)
260
+ })
275
261
 
276
- test("opens to date in input field", async () => {
277
- const selectedDateButton = getByRole(dialog, "button", {
278
- current: "date",
279
- });
262
+ test('opens to date in input field', async () => {
263
+ const selectedDateButton = getByRole(dialog, 'button', {
264
+ current: 'date'
265
+ })
280
266
 
281
- expect(selectedDateButton).toHaveFocus();
267
+ expect(selectedDateButton).toHaveFocus()
282
268
  expect(selectedDateButton).toHaveClass(
283
- "moj-datepicker__button--selected",
284
- "moj-datepicker__button--current",
285
- );
269
+ 'moj-datepicker__button--selected',
270
+ 'moj-datepicker__button--current'
271
+ )
286
272
  expect(selectedDateButton).not.toHaveClass(
287
- "moj-datepicker__button--today",
288
- );
289
- expect(selectedDateButton.textContent).toContain(
290
- `${selectedDate.getDate()}`,
291
- );
292
- });
273
+ 'moj-datepicker__button--today'
274
+ )
275
+ expect(selectedDateButton).toHaveTextContent(
276
+ new RegExp(`${selectedDate.getDate()}`)
277
+ )
278
+ })
293
279
 
294
- test("clicking a date selects it, closes dialog, and populates input", async () => {
295
- const newDateButton = queryByText(dialog, newDate)?.closest("button");
280
+ test('clicking a date selects it, closes dialog, and populates input', async () => {
281
+ const newDateButton = queryByText(dialog, newDate)?.closest('button')
296
282
 
297
- await user.click(newDateButton);
283
+ await user.click(newDateButton)
298
284
 
299
- expect(dialog).not.toBeVisible();
300
- expect(input).toHaveValue(`${newDate}/5/2024`);
301
- expect(calendarButton).toHaveFocus();
302
- });
285
+ expect(dialog).not.toBeVisible()
286
+ expect(input).toHaveValue(`${newDate}/5/2024`)
287
+ expect(calendarButton).toHaveFocus()
288
+ })
303
289
 
304
- test("clicking select, closes dialog and populates input", async () => {
305
- const selectButton = getByText(dialog, "Select");
290
+ test('clicking select, closes dialog and populates input', async () => {
291
+ const selectButton = getByText(dialog, 'Select')
306
292
 
307
- await user.keyboard("ArrowRight");
308
- await user.click(selectButton);
293
+ await user.keyboard('ArrowRight')
294
+ await user.click(selectButton)
309
295
 
310
- expect(dialog).not.toBeVisible();
311
- expect(input).toHaveValue(`${newDate}/5/2024`);
312
- expect(calendarButton).toHaveFocus();
313
- });
314
- });
296
+ expect(dialog).not.toBeVisible()
297
+ expect(input).toHaveValue(`${newDate}/5/2024`)
298
+ expect(calendarButton).toHaveFocus()
299
+ })
300
+ })
315
301
 
316
- describe("keyboard interaction", () => {
317
- let inputString;
318
- let dateString;
319
- let input;
320
- let initialDate;
302
+ describe('keyboard interaction', () => {
303
+ let inputString
304
+ let dateString
305
+ let input
306
+ let initialDate
321
307
 
322
308
  beforeEach(async () => {
323
- inputString = "19/5/2024";
324
- dateString = "2024-05-19";
325
- input = screen.getByLabelText("Date");
326
- initialDate = new Date(dateString);
327
-
328
- await user.type(input, inputString);
329
- });
330
-
331
- test("esc closes calendar dialog", async () => {
332
- await user.tab();
333
- expect(calendarButton).toHaveFocus();
334
- await user.keyboard("{enter}");
335
- expect(dialog).toBeVisible();
336
- await user.keyboard("{escape}");
337
- expect(dialog).not.toBeVisible();
338
- expect(calendarButton).toHaveFocus();
339
- });
340
-
341
- test("calendar dialog is a focus trap", async () => {
342
- await user.click(calendarButton);
343
- const selectedDateButton = getByRole(dialog, "button", {
344
- current: "date",
345
- });
346
- const selectButton = queryByText(dialog, "Select");
347
- const closeButton = queryByText(dialog, "Close");
348
- const prevMonthButton = getByRole(dialog, "button", {
349
- name: "Previous month",
350
- });
351
- const prevYearButton = getByRole(dialog, "button", {
352
- name: "Previous year",
353
- });
354
- const nextMonthButton = getByRole(dialog, "button", {
355
- name: "Next month",
356
- });
357
- const nextYearButton = getByRole(dialog, "button", { name: "Next year" });
358
-
359
- expect(selectedDateButton).toHaveFocus();
360
- await user.tab();
361
- expect(selectButton).toHaveFocus();
362
- await user.tab();
363
- expect(closeButton).toHaveFocus();
364
- await user.tab();
365
- expect(prevYearButton).toHaveFocus();
366
- await user.tab();
367
- expect(prevMonthButton).toHaveFocus();
368
- await user.tab();
369
- expect(nextMonthButton).toHaveFocus();
370
- await user.tab();
371
- expect(nextYearButton).toHaveFocus();
372
- await user.tab();
373
- expect(selectedDateButton).toHaveFocus();
374
- });
375
-
376
- test("rigth arrow navigates to next day", async () => {
377
- await user.click(calendarButton);
378
- const initialDateButton = getByRole(dialog, "button", {
379
- current: "date",
380
- });
381
- let selectedDateButton;
382
- let labelRegex;
383
-
384
- expect(initialDateButton).toHaveFocus();
385
-
386
- await user.keyboard("[ArrowRight]");
387
- labelRegex = new RegExp(` ${initialDate.getDate() + 1} `);
388
- selectedDateButton = getByRole(dialog, "button", { name: labelRegex });
389
- expect(selectedDateButton).toHaveClass(
390
- "moj-datepicker__button--selected",
391
- );
392
- expect(selectedDateButton).toHaveFocus();
393
- });
394
-
395
- test("left arrow navigates to next day", async () => {
396
- await user.click(calendarButton);
397
- const initialDateButton = getByRole(dialog, "button", {
398
- current: "date",
399
- });
400
- let selectedDateButton;
401
- let labelRegex;
402
-
403
- expect(initialDateButton).toHaveFocus();
404
-
405
- await user.keyboard("[ArrowLeft]");
406
- labelRegex = new RegExp(` ${initialDate.getDate() - 1} `);
407
- selectedDateButton = getByRole(dialog, "button", { name: labelRegex });
408
- expect(selectedDateButton).toHaveClass(
409
- "moj-datepicker__button--selected",
410
- );
411
- expect(selectedDateButton).toHaveFocus();
412
- });
413
-
414
- test("up arrow navigates to next day", async () => {
415
- await user.click(calendarButton);
416
- const initialDateButton = getByRole(dialog, "button", {
417
- current: "date",
418
- });
419
- let selectedDateButton;
420
- let labelRegex;
421
-
422
- expect(initialDateButton).toHaveFocus();
423
-
424
- await user.keyboard("[ArrowUp]");
425
- labelRegex = new RegExp(` ${initialDate.getDate() - 7} `);
426
- selectedDateButton = getByRole(dialog, "button", { name: labelRegex });
427
- expect(selectedDateButton).toHaveClass(
428
- "moj-datepicker__button--selected",
429
- );
430
- expect(selectedDateButton).toHaveFocus();
431
- });
432
-
433
- test("down arrow navigates to next day", async () => {
434
- await user.click(calendarButton);
435
- const initialDateButton = getByRole(dialog, "button", {
436
- current: "date",
437
- });
438
- let selectedDateButton;
439
- let labelRegex;
440
-
441
- expect(initialDateButton).toHaveFocus();
442
-
443
- await user.keyboard("[ArrowDown]");
444
- labelRegex = new RegExp(` ${initialDate.getDate() + 7} `);
445
- selectedDateButton = getByRole(dialog, "button", { name: labelRegex });
446
- expect(selectedDateButton).toHaveClass(
447
- "moj-datepicker__button--selected",
448
- );
449
- expect(selectedDateButton).toHaveFocus();
450
- });
451
-
452
- test("home key focuses first day of the week", async () => {
453
- await user.click(calendarButton);
454
- const initialDateButton = getByRole(dialog, "button", {
455
- current: "date",
456
- });
457
- const firstDayOfWeek = getFirstDayOfWeek(initialDate, 1);
458
- const labelRegex = new RegExp(` ${firstDayOfWeek.getDate()} `);
459
- const selectedDateButton = getByRole(dialog, "button", {
460
- name: labelRegex,
461
- });
462
-
463
- expect(initialDateButton).toHaveFocus();
464
- await user.keyboard("[Home]");
465
- expect(selectedDateButton).toHaveClass(
466
- "moj-datepicker__button--selected",
467
- );
468
- expect(selectedDateButton).toHaveFocus();
469
- });
470
-
471
- test("end key focuses last day of the week", async () => {
472
- await user.click(calendarButton);
473
- const initialDateButton = getByRole(dialog, "button", {
474
- current: "date",
475
- });
476
- const lastDayOfWeek = getLastDayOfWeek(initialDate, 0);
477
- const labelRegex = new RegExp(` ${lastDayOfWeek.getDate()} `);
478
- const selectedDateButton = getByRole(dialog, "button", {
479
- name: labelRegex,
480
- });
481
-
482
- expect(initialDateButton).toHaveFocus();
483
- await user.keyboard("[End]");
484
- expect(selectedDateButton).toHaveClass(
485
- "moj-datepicker__button--selected",
486
- );
487
- expect(selectedDateButton).toHaveFocus();
488
- });
489
-
490
- test("pageup focuses previous month and year", async () => {
491
- await user.click(calendarButton);
492
- const currentMonthName = initialDate.toLocaleString("default", {
493
- month: "long",
494
- });
495
- const currentYear = initialDate.getFullYear();
309
+ inputString = '19/5/2024'
310
+ dateString = '2024-05-19'
311
+ input = screen.getByLabelText('Date')
312
+ initialDate = new Date(dateString)
313
+
314
+ await user.type(input, inputString)
315
+ })
316
+
317
+ test('esc closes calendar dialog', async () => {
318
+ await user.tab()
319
+ expect(calendarButton).toHaveFocus()
320
+ await user.keyboard('{enter}')
321
+ expect(dialog).toBeVisible()
322
+ await user.keyboard('{escape}')
323
+ expect(dialog).not.toBeVisible()
324
+ expect(calendarButton).toHaveFocus()
325
+ })
326
+
327
+ test('calendar dialog is a focus trap', async () => {
328
+ await user.click(calendarButton)
329
+ const selectedDateButton = getByRole(dialog, 'button', {
330
+ current: 'date'
331
+ })
332
+ const selectButton = queryByText(dialog, 'Select')
333
+ const closeButton = queryByText(dialog, 'Close')
334
+ const prevMonthButton = getByRole(dialog, 'button', {
335
+ name: 'Previous month'
336
+ })
337
+ const prevYearButton = getByRole(dialog, 'button', {
338
+ name: 'Previous year'
339
+ })
340
+ const nextMonthButton = getByRole(dialog, 'button', {
341
+ name: 'Next month'
342
+ })
343
+ const nextYearButton = getByRole(dialog, 'button', { name: 'Next year' })
344
+
345
+ expect(selectedDateButton).toHaveFocus()
346
+ await user.tab()
347
+ expect(selectButton).toHaveFocus()
348
+ await user.tab()
349
+ expect(closeButton).toHaveFocus()
350
+ await user.tab()
351
+ expect(prevYearButton).toHaveFocus()
352
+ await user.tab()
353
+ expect(prevMonthButton).toHaveFocus()
354
+ await user.tab()
355
+ expect(nextMonthButton).toHaveFocus()
356
+ await user.tab()
357
+ expect(nextYearButton).toHaveFocus()
358
+ await user.tab()
359
+ expect(selectedDateButton).toHaveFocus()
360
+ })
361
+
362
+ test('rigth arrow navigates to next day', async () => {
363
+ await user.click(calendarButton)
364
+ const initialDateButton = getByRole(dialog, 'button', {
365
+ current: 'date'
366
+ })
367
+
368
+ expect(initialDateButton).toHaveFocus()
369
+
370
+ await user.keyboard('[ArrowRight]')
371
+ const labelRegex = new RegExp(` ${initialDate.getDate() + 1} `)
372
+ const selectedDateButton = getByRole(dialog, 'button', {
373
+ name: labelRegex
374
+ })
375
+ expect(selectedDateButton).toHaveClass('moj-datepicker__button--selected')
376
+ expect(selectedDateButton).toHaveFocus()
377
+ })
378
+
379
+ test('left arrow navigates to next day', async () => {
380
+ await user.click(calendarButton)
381
+ const initialDateButton = getByRole(dialog, 'button', {
382
+ current: 'date'
383
+ })
384
+
385
+ expect(initialDateButton).toHaveFocus()
386
+
387
+ await user.keyboard('[ArrowLeft]')
388
+ const labelRegex = new RegExp(` ${initialDate.getDate() - 1} `)
389
+ const selectedDateButton = getByRole(dialog, 'button', {
390
+ name: labelRegex
391
+ })
392
+ expect(selectedDateButton).toHaveClass('moj-datepicker__button--selected')
393
+ expect(selectedDateButton).toHaveFocus()
394
+ })
395
+
396
+ test('up arrow navigates to next day', async () => {
397
+ await user.click(calendarButton)
398
+ const initialDateButton = getByRole(dialog, 'button', {
399
+ current: 'date'
400
+ })
401
+
402
+ expect(initialDateButton).toHaveFocus()
403
+
404
+ await user.keyboard('[ArrowUp]')
405
+ const labelRegex = new RegExp(` ${initialDate.getDate() - 7} `)
406
+ const selectedDateButton = getByRole(dialog, 'button', {
407
+ name: labelRegex
408
+ })
409
+ expect(selectedDateButton).toHaveClass('moj-datepicker__button--selected')
410
+ expect(selectedDateButton).toHaveFocus()
411
+ })
412
+
413
+ test('down arrow navigates to next day', async () => {
414
+ await user.click(calendarButton)
415
+ const initialDateButton = getByRole(dialog, 'button', {
416
+ current: 'date'
417
+ })
418
+
419
+ expect(initialDateButton).toHaveFocus()
420
+
421
+ await user.keyboard('[ArrowDown]')
422
+ const labelRegex = new RegExp(` ${initialDate.getDate() + 7} `)
423
+ const selectedDateButton = getByRole(dialog, 'button', {
424
+ name: labelRegex
425
+ })
426
+ expect(selectedDateButton).toHaveClass('moj-datepicker__button--selected')
427
+ expect(selectedDateButton).toHaveFocus()
428
+ })
429
+
430
+ test('home key focuses first day of the week', async () => {
431
+ await user.click(calendarButton)
432
+ const initialDateButton = getByRole(dialog, 'button', {
433
+ current: 'date'
434
+ })
435
+ const firstDayOfWeek = getFirstDayOfWeek(initialDate, 1)
436
+ const labelRegex = new RegExp(` ${firstDayOfWeek.getDate()} `)
437
+ const selectedDateButton = getByRole(dialog, 'button', {
438
+ name: labelRegex
439
+ })
440
+
441
+ expect(initialDateButton).toHaveFocus()
442
+ await user.keyboard('[Home]')
443
+ expect(selectedDateButton).toHaveClass('moj-datepicker__button--selected')
444
+ expect(selectedDateButton).toHaveFocus()
445
+ })
446
+
447
+ test('end key focuses last day of the week', async () => {
448
+ await user.click(calendarButton)
449
+ const initialDateButton = getByRole(dialog, 'button', {
450
+ current: 'date'
451
+ })
452
+ const lastDayOfWeek = getLastDayOfWeek(initialDate, 0)
453
+ const labelRegex = new RegExp(` ${lastDayOfWeek.getDate()} `)
454
+ const selectedDateButton = getByRole(dialog, 'button', {
455
+ name: labelRegex
456
+ })
457
+
458
+ expect(initialDateButton).toHaveFocus()
459
+ await user.keyboard('[End]')
460
+ expect(selectedDateButton).toHaveClass('moj-datepicker__button--selected')
461
+ expect(selectedDateButton).toHaveFocus()
462
+ })
463
+
464
+ test('pageup focuses previous month and year', async () => {
465
+ await user.click(calendarButton)
466
+ const currentMonthName = initialDate.toLocaleString('default', {
467
+ month: 'long'
468
+ })
469
+ const currentYear = initialDate.getFullYear()
496
470
  const previousMonthName = new Date(
497
- new Date(initialDate).setMonth(initialDate.getMonth() - 1, 1),
498
- ).toLocaleString("default", { month: "long" });
471
+ new Date(initialDate).setMonth(initialDate.getMonth() - 1, 1)
472
+ ).toLocaleString('default', { month: 'long' })
499
473
  const previousYear = new Date(
500
- new Date(initialDate).setFullYear(initialDate.getFullYear() - 1),
501
- ).getFullYear();
502
- const currentTitle = `${currentMonthName} ${currentYear}`;
503
- const previousMonthTitle = `${previousMonthName} ${currentYear}`;
504
- const previousYearTitle = `${previousMonthName} ${previousYear}`;
505
- const dialogTitle = getByRole(dialog, "heading", { level: 2 });
506
-
507
- expect(dialogTitle.textContent).toEqual(currentTitle);
508
-
509
- await user.keyboard("{PageUp}");
510
- expect(dialogTitle.textContent).toEqual(previousMonthTitle);
511
-
512
- await user.keyboard("{Shift>}{PageUp}");
513
- expect(dialogTitle.textContent).toEqual(previousYearTitle);
514
- });
515
-
516
- test("pagedown focuses next month and year", async () => {
517
- await user.click(calendarButton);
518
- const currentMonthName = initialDate.toLocaleString("default", {
519
- month: "long",
520
- });
521
- const currentYear = initialDate.getFullYear();
474
+ new Date(initialDate).setFullYear(initialDate.getFullYear() - 1)
475
+ ).getFullYear()
476
+ const currentTitle = `${currentMonthName} ${currentYear}`
477
+ const previousMonthTitle = `${previousMonthName} ${currentYear}`
478
+ const previousYearTitle = `${previousMonthName} ${previousYear}`
479
+ const dialogTitle = getByRole(dialog, 'heading', { level: 2 })
480
+
481
+ expect(dialogTitle).toHaveTextContent(currentTitle)
482
+
483
+ await user.keyboard('{PageUp}')
484
+ expect(dialogTitle).toHaveTextContent(previousMonthTitle)
485
+
486
+ await user.keyboard('{Shift>}{PageUp}')
487
+ expect(dialogTitle).toHaveTextContent(previousYearTitle)
488
+ })
489
+
490
+ test('pagedown focuses next month and year', async () => {
491
+ await user.click(calendarButton)
492
+ const currentMonthName = initialDate.toLocaleString('default', {
493
+ month: 'long'
494
+ })
495
+ const currentYear = initialDate.getFullYear()
522
496
  const nextMonthName = new Date(
523
- new Date(initialDate).setMonth(initialDate.getMonth() + 1, 1),
524
- ).toLocaleString("default", { month: "long" });
497
+ new Date(initialDate).setMonth(initialDate.getMonth() + 1, 1)
498
+ ).toLocaleString('default', { month: 'long' })
525
499
  const nextYear = new Date(
526
- new Date(initialDate).setFullYear(initialDate.getFullYear() + 1),
527
- ).getFullYear();
528
- const currentTitle = `${currentMonthName} ${currentYear}`;
529
- const nextMonthTitle = `${nextMonthName} ${currentYear}`;
530
- const nextYearTitle = `${nextMonthName} ${nextYear}`;
531
- const dialogTitle = getByRole(dialog, "heading", { level: 2 });
532
-
533
- expect(dialogTitle.textContent).toEqual(currentTitle);
534
-
535
- await user.keyboard("{PageDown}");
536
- expect(dialogTitle.textContent).toEqual(nextMonthTitle);
537
-
538
- await user.keyboard("{Shift>}{PageDown}");
539
- expect(dialogTitle.textContent).toEqual(nextYearTitle);
540
- });
541
-
542
- test("enter selects date and closes dialog", async () => {
543
- await user.click(calendarButton);
544
- await user.keyboard("[ArrowRight]");
545
- await user.keyboard("[Enter]");
546
-
547
- expect(dialog).not.toBeVisible();
548
- expect(input).toHaveValue(`20/5/2024`);
549
- expect(calendarButton).toHaveFocus();
550
- });
551
-
552
- test("space selects date and closes dialog", async () => {
553
- await user.click(calendarButton);
554
- await user.keyboard("[ArrowRight]");
555
- await user.keyboard("[Space]");
556
-
557
- expect(dialog).not.toBeVisible();
558
- expect(input).toHaveValue(`20/5/2024`);
559
- expect(calendarButton).toHaveFocus();
560
- });
561
-
562
- test("select button selects date and closes dialog", async () => {
563
- await user.click(calendarButton);
564
- await user.keyboard("[ArrowRight]");
565
- await user.tab();
566
- await user.keyboard("[Enter]");
567
-
568
- expect(dialog).not.toBeVisible();
569
- expect(input).toHaveValue(`20/5/2024`);
570
- expect(calendarButton).toHaveFocus();
571
- });
572
-
573
- test("close button, closes dialog", async () => {
574
- await user.click(calendarButton);
575
- await user.keyboard("[ArrowRight]");
576
- await user.tab();
577
- await user.tab();
578
- await user.keyboard("[Enter]");
579
-
580
- expect(dialog).not.toBeVisible();
581
- expect(input).toHaveValue(`19/5/2024`);
582
- expect(calendarButton).toHaveFocus();
583
- });
584
- });
585
-
586
- describe("accessibility", () => {
587
- test("component has no wcag violations", async () => {
588
- expect(await axe(document.body)).toHaveNoViolations();
589
- await user.click(calendarButton);
590
- expect(await axe(document.body)).toHaveNoViolations();
591
- });
592
- });
593
- });
594
-
595
- describe("button menu JS API", () => {
596
- let component;
500
+ new Date(initialDate).setFullYear(initialDate.getFullYear() + 1)
501
+ ).getFullYear()
502
+ const currentTitle = `${currentMonthName} ${currentYear}`
503
+ const nextMonthTitle = `${nextMonthName} ${currentYear}`
504
+ const nextYearTitle = `${nextMonthName} ${nextYear}`
505
+ const dialogTitle = getByRole(dialog, 'heading', { level: 2 })
506
+
507
+ expect(dialogTitle).toHaveTextContent(currentTitle)
508
+
509
+ await user.keyboard('{PageDown}')
510
+ expect(dialogTitle).toHaveTextContent(nextMonthTitle)
511
+
512
+ await user.keyboard('{Shift>}{PageDown}')
513
+ expect(dialogTitle).toHaveTextContent(nextYearTitle)
514
+ })
515
+
516
+ test('enter selects date and closes dialog', async () => {
517
+ await user.click(calendarButton)
518
+ await user.keyboard('[ArrowRight]')
519
+ await user.keyboard('[Enter]')
520
+
521
+ expect(dialog).not.toBeVisible()
522
+ expect(input).toHaveValue(`20/5/2024`)
523
+ expect(calendarButton).toHaveFocus()
524
+ })
525
+
526
+ test('space selects date and closes dialog', async () => {
527
+ await user.click(calendarButton)
528
+ await user.keyboard('[ArrowRight]')
529
+ await user.keyboard('[Space]')
530
+
531
+ expect(dialog).not.toBeVisible()
532
+ expect(input).toHaveValue(`20/5/2024`)
533
+ expect(calendarButton).toHaveFocus()
534
+ })
535
+
536
+ test('select button selects date and closes dialog', async () => {
537
+ await user.click(calendarButton)
538
+ await user.keyboard('[ArrowRight]')
539
+ await user.tab()
540
+ await user.keyboard('[Enter]')
541
+
542
+ expect(dialog).not.toBeVisible()
543
+ expect(input).toHaveValue(`20/5/2024`)
544
+ expect(calendarButton).toHaveFocus()
545
+ })
546
+
547
+ test('close button, closes dialog', async () => {
548
+ await user.click(calendarButton)
549
+ await user.keyboard('[ArrowRight]')
550
+ await user.tab()
551
+ await user.tab()
552
+ await user.keyboard('[Enter]')
553
+
554
+ expect(dialog).not.toBeVisible()
555
+ expect(input).toHaveValue(`19/5/2024`)
556
+ expect(calendarButton).toHaveFocus()
557
+ })
558
+ })
559
+
560
+ describe('accessibility', () => {
561
+ test('component has no wcag violations', async () => {
562
+ expect(await axe(document.body)).toHaveNoViolations()
563
+ await user.click(calendarButton)
564
+ expect(await axe(document.body)).toHaveNoViolations()
565
+ })
566
+ })
567
+ })
568
+
569
+ describe('button menu JS API', () => {
570
+ let component
597
571
 
598
572
  beforeEach(() => {
599
- component = createComponent();
600
- });
573
+ component = createComponent()
574
+ })
601
575
 
602
576
  afterEach(() => {
603
- document.body.innerHTML = "";
604
- });
577
+ document.body.innerHTML = ''
578
+ })
605
579
 
606
- describe("config", () => {
607
- test("default config values", () => {
608
- const datePicker = new MOJFrontend.DatePicker(component, {});
609
- datePicker.init();
580
+ describe('config', () => {
581
+ test('default config values', () => {
582
+ const datePicker = new MOJFrontend.DatePicker(component, {})
583
+ datePicker.init()
610
584
 
611
585
  expect(datePicker.config).toStrictEqual({
612
586
  leadingZeros: false,
613
- weekStartDay: "monday",
614
- });
615
- });
587
+ weekStartDay: 'monday'
588
+ })
589
+ })
616
590
 
617
- test("leadingZeros", () => {
618
- const config = { leadingZeros: true };
619
- const datePicker = new MOJFrontend.DatePicker(component, config);
620
- datePicker.init();
591
+ test('leadingZeros', () => {
592
+ const config = { leadingZeros: true }
593
+ const datePicker = new MOJFrontend.DatePicker(component, config)
594
+ datePicker.init()
621
595
 
622
- expect(datePicker.config.leadingZeros).toBe(true);
623
- });
596
+ expect(datePicker.config.leadingZeros).toBe(true)
597
+ })
624
598
 
625
- test("weekStartDay can be set to sunday", () => {
626
- const config = { weekStartDay: "Sunday" };
627
- const datePicker = new MOJFrontend.DatePicker(component, config);
628
- datePicker.init();
599
+ test('weekStartDay can be set to sunday', () => {
600
+ const config = { weekStartDay: 'Sunday' }
601
+ const datePicker = new MOJFrontend.DatePicker(component, config)
602
+ datePicker.init()
629
603
 
630
- expect(datePicker.config.weekStartDay).toBe("sunday");
631
- expect(datePicker.dayLabels[0]).toBe("Sunday");
632
- });
604
+ expect(datePicker.config.weekStartDay).toBe('sunday')
605
+ expect(datePicker.dayLabels[0]).toBe('Sunday')
606
+ })
633
607
 
634
608
  test("weekStartDay can't be set to other days", () => {
635
- const config = { weekStartDay: "friday" };
636
- const datePicker = new MOJFrontend.DatePicker(component, config);
637
- datePicker.init();
638
-
639
- expect(datePicker.config.weekStartDay).toBe("monday");
640
- });
641
-
642
- test("minDate", () => {
643
- const minDate = dayjs().subtract("1", "week").startOf("day");
644
- const config = { minDate: minDate.format("D/M/YYYY") };
645
- const datePicker = new MOJFrontend.DatePicker(component, config);
646
- datePicker.init();
647
-
648
- expect(datePicker.minDate).toStrictEqual(minDate.toDate());
649
- });
650
-
651
- test("future minDate sets currentDate to minDate", () => {
652
- const minDate = dayjs().add("1", "week").startOf("day");
653
- const config = { minDate: minDate.format("D/M/YYYY") };
654
- const datePicker = new MOJFrontend.DatePicker(component, config);
655
- datePicker.init();
656
-
657
- expect(datePicker.minDate).toStrictEqual(minDate.toDate());
658
- expect(datePicker.currentDate).toStrictEqual(minDate.toDate());
659
- });
660
-
661
- test("maxDate", () => {
662
- const maxDate = dayjs().add("1", "week").startOf("day");
663
- const config = { maxDate: maxDate.format("D/M/YYYY") };
664
- const datePicker = new MOJFrontend.DatePicker(component, config);
665
- datePicker.init();
666
-
667
- expect(datePicker.maxDate).toStrictEqual(maxDate.toDate());
668
- });
669
-
670
- test("past maxDate sets currentDate to maxDate", () => {
671
- const maxDate = dayjs().subtract("1", "week").startOf("day");
672
- const config = { maxDate: maxDate.format("D/M/YYYY") };
673
- const datePicker = new MOJFrontend.DatePicker(component, config);
674
- datePicker.init();
675
-
676
- expect(datePicker.maxDate).toStrictEqual(maxDate.toDate());
677
- expect(datePicker.currentDate).toStrictEqual(maxDate.toDate());
678
- });
679
-
680
- test("excludedDays", () => {
681
- const config = { excludedDays: "sunday thursday" };
682
- const datePicker = new MOJFrontend.DatePicker(component, config);
683
- datePicker.init();
684
-
685
- expect(datePicker.excludedDays).toEqual([0, 4]);
686
- });
687
-
688
- describe("excludedDates", () => {
689
- test("excluding a day", () => {
609
+ const config = { weekStartDay: 'friday' }
610
+ const datePicker = new MOJFrontend.DatePicker(component, config)
611
+ datePicker.init()
612
+
613
+ expect(datePicker.config.weekStartDay).toBe('monday')
614
+ })
615
+
616
+ test('minDate', () => {
617
+ const minDate = dayjs().subtract('1', 'week').startOf('day')
618
+ const config = { minDate: minDate.format('D/M/YYYY') }
619
+ const datePicker = new MOJFrontend.DatePicker(component, config)
620
+ datePicker.init()
621
+
622
+ expect(datePicker.minDate).toStrictEqual(minDate.toDate())
623
+ })
624
+
625
+ test('future minDate sets currentDate to minDate', () => {
626
+ const minDate = dayjs().add('1', 'week').startOf('day')
627
+ const config = { minDate: minDate.format('D/M/YYYY') }
628
+ const datePicker = new MOJFrontend.DatePicker(component, config)
629
+ datePicker.init()
630
+
631
+ expect(datePicker.minDate).toStrictEqual(minDate.toDate())
632
+ expect(datePicker.currentDate).toStrictEqual(minDate.toDate())
633
+ })
634
+
635
+ test('maxDate', () => {
636
+ const maxDate = dayjs().add('1', 'week').startOf('day')
637
+ const config = { maxDate: maxDate.format('D/M/YYYY') }
638
+ const datePicker = new MOJFrontend.DatePicker(component, config)
639
+ datePicker.init()
640
+
641
+ expect(datePicker.maxDate).toStrictEqual(maxDate.toDate())
642
+ })
643
+
644
+ test('past maxDate sets currentDate to maxDate', () => {
645
+ const maxDate = dayjs().subtract('1', 'week').startOf('day')
646
+ const config = { maxDate: maxDate.format('D/M/YYYY') }
647
+ const datePicker = new MOJFrontend.DatePicker(component, config)
648
+ datePicker.init()
649
+
650
+ expect(datePicker.maxDate).toStrictEqual(maxDate.toDate())
651
+ expect(datePicker.currentDate).toStrictEqual(maxDate.toDate())
652
+ })
653
+
654
+ test('excludedDays', () => {
655
+ const config = { excludedDays: 'sunday thursday' }
656
+ const datePicker = new MOJFrontend.DatePicker(component, config)
657
+ datePicker.init()
658
+
659
+ expect(datePicker.excludedDays).toEqual([0, 4])
660
+ })
661
+
662
+ describe('excludedDates', () => {
663
+ test('excluding a day', () => {
690
664
  const dateToExclude = dayjs()
691
665
  .date(getDateInCurrentMonth())
692
- .startOf("day");
693
- config = { excludedDates: dateToExclude.format("D/M/YYYY") };
694
- const datePicker = new MOJFrontend.DatePicker(component, config);
695
- datePicker.init();
666
+ .startOf('day')
667
+ const config = { excludedDates: dateToExclude.format('D/M/YYYY') }
668
+ const datePicker = new MOJFrontend.DatePicker(component, config)
669
+ datePicker.init()
696
670
 
697
- expect(datePicker.excludedDates).toStrictEqual([
698
- dateToExclude.toDate(),
699
- ]);
700
- });
671
+ expect(datePicker.excludedDates).toStrictEqual([dateToExclude.toDate()])
672
+ })
701
673
 
702
- test("excluding multiple dates", () => {
674
+ test('excluding multiple dates', () => {
703
675
  const firstDateToExclude = dayjs()
704
676
  .date(getDateInCurrentMonth())
705
- .startOf("day");
677
+ .startOf('day')
706
678
  const secondDateToExclude = dayjs()
707
679
  .date(getDateInCurrentMonth([firstDateToExclude.date()]))
708
- .startOf("day");
709
- config = {
710
- excludedDates: `${firstDateToExclude.format("D/M/YYYY")} ${secondDateToExclude.format("D/M/YYYY")}`,
711
- };
712
- const datePicker = new MOJFrontend.DatePicker(component, config);
713
- datePicker.init();
714
-
715
- expect(datePicker.excludedDates.length).toEqual(2);
680
+ .startOf('day')
681
+ const config = {
682
+ excludedDates: `${firstDateToExclude.format('D/M/YYYY')} ${secondDateToExclude.format('D/M/YYYY')}`
683
+ }
684
+ const datePicker = new MOJFrontend.DatePicker(component, config)
685
+ datePicker.init()
686
+
687
+ expect(datePicker.excludedDates).toHaveLength(2)
716
688
  expect(datePicker.excludedDates).toStrictEqual([
717
689
  firstDateToExclude.toDate(),
718
- secondDateToExclude.toDate(),
719
- ]);
720
- });
690
+ secondDateToExclude.toDate()
691
+ ])
692
+ })
721
693
 
722
- test("excluding a range of days", () => {
723
- let datesToExclude;
694
+ test('excluding a range of days', () => {
695
+ let datesToExclude = []
724
696
  if (dayjs().date() < 15) {
725
- datesToExclude = getDateRangeInCurrentMonth(18, 20);
697
+ datesToExclude.push(dayjs().date(18))
698
+ datesToExclude.push(dayjs().date(19))
699
+ datesToExclude.push(dayjs().date(20))
726
700
  } else {
727
- datesToExclude = getDateRangeInCurrentMonth(3, 5);
701
+ datesToExclude.push(dayjs().date(3))
702
+ datesToExclude.push(dayjs().date(4))
703
+ datesToExclude.push(dayjs().date(5))
704
+ }
705
+ datesToExclude = datesToExclude.map((date) => date.startOf('day'))
706
+
707
+ const config = {
708
+ excludedDates: `${datesToExclude[0].format('D/M/YYYY')}-${datesToExclude[datesToExclude.length - 1].format('D/M/YYYY')}`
728
709
  }
729
- datesToExclude = datesToExclude.map((date) => date.startOf("day"));
730
- config = {
731
- excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
732
- };
733
- const datePicker = new MOJFrontend.DatePicker(component, config);
734
- datePicker.init();
735
-
736
- expect(datePicker.excludedDates.length).toEqual(3);
710
+
711
+ const datePicker = new MOJFrontend.DatePicker(component, config)
712
+ datePicker.init()
713
+
714
+ // expect(datePicker.excludedDates.length).toEqual(3);
737
715
  expect(datePicker.excludedDates).toStrictEqual(
738
- datesToExclude.map((date) => date.toDate()),
739
- );
740
- });
716
+ datesToExclude.map((date) => date.toDate())
717
+ )
718
+ })
741
719
 
742
- test("excluding individual dates and a range of days", () => {
743
- let datesToExclude;
720
+ test('excluding individual dates and a range of days', () => {
721
+ let datesToExclude = []
744
722
  if (dayjs().date() < 15) {
745
- datesToExclude = getDateRangeInCurrentMonth(18, 20);
746
- datesToExclude.push(dayjs().date(22));
747
- datesToExclude.push(dayjs().date(25));
723
+ datesToExclude.push(dayjs().date(18))
724
+ datesToExclude.push(dayjs().date(19))
725
+ datesToExclude.push(dayjs().date(20))
726
+ datesToExclude.push(dayjs().date(22))
727
+ datesToExclude.push(dayjs().date(25))
748
728
  } else {
749
- datesToExclude = getDateRangeInCurrentMonth(3, 5);
750
- datesToExclude.push(dayjs().date(7));
751
- datesToExclude.push(dayjs().date(11));
729
+ datesToExclude.push(dayjs().date(3))
730
+ datesToExclude.push(dayjs().date(4))
731
+ datesToExclude.push(dayjs().date(5))
732
+ datesToExclude.push(dayjs().date(7))
733
+ datesToExclude.push(dayjs().date(11))
734
+ }
735
+ datesToExclude = datesToExclude.map((date) => date.startOf('day'))
736
+ const config = {
737
+ excludedDates: `${datesToExclude[0].format('D/M/YYYY')}-${datesToExclude[2].format('D/M/YYYY')} ${datesToExclude[3].format('D/M/YYYY')} ${datesToExclude[4].format('D/M/YYYY')}`
752
738
  }
753
- datesToExclude = datesToExclude.map((date) => date.startOf("day"));
754
- config = {
755
- excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[2].format("D/M/YYYY")} ${datesToExclude[3].format("D/M/YYYY")} ${datesToExclude[4].format("D/M/YYYY")} `,
756
- };
757
- const datePicker = new MOJFrontend.DatePicker(component, config);
758
- datePicker.init();
759
-
760
- expect(datePicker.excludedDates.length).toEqual(5);
739
+ const datePicker = new MOJFrontend.DatePicker(component, config)
740
+ datePicker.init()
741
+
761
742
  expect(datePicker.excludedDates).toStrictEqual(
762
- datesToExclude.map((date) => date.toDate()),
763
- );
764
- });
765
- });
766
- });
743
+ datesToExclude.map((date) => date.toDate())
744
+ )
745
+ })
746
+ })
747
+ })
767
748
 
768
- describe("UI", () => {
769
- let calendarButton;
770
- let input;
749
+ describe('UI', () => {
750
+ let calendarButton
751
+ let input
771
752
 
772
- test("with leadingZeros false", async () => {
773
- input = screen.getByLabelText("Date");
753
+ test('with leadingZeros false', async () => {
754
+ input = screen.getByLabelText('Date')
774
755
 
775
- const config = { leadingZeros: false };
776
- new MOJFrontend.DatePicker(component, config).init();
777
- calendarButton = screen.getByRole("button", { name: "Choose date" });
778
- const dateToSelect = screen.queryByText("9")?.closest("button");
779
- const selectedDate = dayjs().date(9);
756
+ const config = { leadingZeros: false }
757
+ new MOJFrontend.DatePicker(component, config).init()
758
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
759
+ const dateToSelect = dayjs().date(9)
760
+ const dateButton = screen.getByTestId(dateToSelect.format('D/M/YYYY'))
780
761
 
781
- await user.click(calendarButton);
782
- await user.click(dateToSelect);
762
+ await user.click(calendarButton)
763
+ await user.click(dateButton)
783
764
 
784
- expect(input).toHaveValue(selectedDate.format("D/M/YYYY"));
785
- });
765
+ expect(input).toHaveValue(dateToSelect.format('D/M/YYYY'))
766
+ })
786
767
 
787
- test("with leadingZeros true", async () => {
788
- input = screen.getByLabelText("Date");
768
+ test('with leadingZeros true', async () => {
769
+ input = screen.getByLabelText('Date')
789
770
 
790
- const config = { leadingZeros: true };
791
- new MOJFrontend.DatePicker(component, config).init();
792
- calendarButton = screen.getByRole("button", { name: "Choose date" });
793
- const dateToSelect = screen.queryByText("9")?.closest("button");
794
- const selectedDate = dayjs().date(9);
771
+ const config = { leadingZeros: true }
772
+ new MOJFrontend.DatePicker(component, config).init()
773
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
774
+ const dateToSelect = dayjs().date(9)
775
+ const dateButton = screen.getByTestId(dateToSelect.format('DD/MM/YYYY'))
795
776
 
796
- await user.click(calendarButton);
797
- await user.click(dateToSelect);
777
+ await user.click(calendarButton)
778
+ await user.click(dateButton)
798
779
 
799
- expect(input).toHaveValue(selectedDate.format("DD/MM/YYYY"));
800
- });
780
+ expect(input).toHaveValue(dateToSelect.format('DD/MM/YYYY'))
781
+ })
801
782
 
802
- test.skip.failing("minDate", async () => {
803
- const minDay = 3;
804
- const lastDayinMonth = dayjs().endOf("month").date();
805
- const minDate = dayjs().date(minDay);
806
- const config = { minDate: minDate.format("DD/MM/YYYY") };
783
+ test('minDate', async () => {
784
+ const minDay = 3
785
+ const lastDayinMonth = dayjs().endOf('month').date()
786
+ const minDate = dayjs().date(minDay)
787
+ const config = { minDate: minDate.format('DD/MM/YYYY') }
807
788
 
808
- new MOJFrontend.DatePicker(component, config).init();
809
- calendarButton = screen.getByRole("button", { name: "Choose date" });
810
- await user.click(calendarButton);
789
+ new MOJFrontend.DatePicker(component, config).init()
790
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
811
791
 
812
- for (let i = 1; i <= lastDayinMonth; i++) {
813
- const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
814
- const dayButton = screen.getByTestId(testId);
792
+ await user.click(calendarButton)
815
793
 
816
- if (i <= minDay) {
817
- expect(dayButton).toHaveAttribute("aria-disabled", "true");
818
- } else {
819
- expect(dayButton).not.toHaveAttribute("aria-disabled");
820
- }
794
+ const dayButtonsDisabled = range(1, minDay - 1)
795
+ .map(getDateFormatted)
796
+ .map(screen.getByTestId)
797
+
798
+ const dayButtonsEnabled = range(minDay, lastDayinMonth)
799
+ .map(getDateFormatted)
800
+ .map(screen.getByTestId)
801
+
802
+ for (const dayButton of dayButtonsDisabled) {
803
+ expect(dayButton).toHaveAttribute('aria-disabled', 'true')
821
804
  }
822
- });
823
805
 
824
- test("maxDate", async () => {
825
- const maxDay = 21;
826
- const lastDayinMonth = dayjs().endOf("month").date();
827
- const maxDate = dayjs().date(maxDay);
828
- const config = { maxDate: maxDate.format("DD/MM/YYYY") };
806
+ for (const dayButton of dayButtonsEnabled) {
807
+ expect(dayButton).not.toHaveAttribute('aria-disabled')
808
+ }
809
+ })
829
810
 
830
- new MOJFrontend.DatePicker(component, config).init();
831
- calendarButton = screen.getByRole("button", { name: "Choose date" });
832
- await user.click(calendarButton);
811
+ test('maxDate', async () => {
812
+ const maxDay = 21
813
+ const lastDayinMonth = dayjs().endOf('month').date()
814
+ const maxDate = dayjs().date(maxDay)
815
+ const config = { maxDate: maxDate.format('DD/MM/YYYY') }
833
816
 
834
- for (let i = 1; i <= lastDayinMonth; i++) {
835
- const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
836
- const dayButton = screen.getByTestId(testId);
817
+ new MOJFrontend.DatePicker(component, config).init()
818
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
837
819
 
838
- if (i > maxDay) {
839
- expect(dayButton).toHaveAttribute("aria-disabled", "true");
840
- } else {
841
- expect(dayButton).not.toHaveAttribute("aria-disabled");
842
- }
820
+ await user.click(calendarButton)
821
+
822
+ const dayButtonsDisabled = range(maxDay + 1, lastDayinMonth)
823
+ .map(getDateFormatted)
824
+ .map(screen.getByTestId)
825
+
826
+ const dayButtonsEnabled = range(1, maxDay)
827
+ .map(getDateFormatted)
828
+ .map(screen.getByTestId)
829
+
830
+ for (const dayButton of dayButtonsDisabled) {
831
+ expect(dayButton).toHaveAttribute('aria-disabled', 'true')
843
832
  }
844
- });
845
833
 
846
- describe("excludedDates", () => {
847
- test("excluding a day", async () => {
834
+ for (const dayButton of dayButtonsEnabled) {
835
+ expect(dayButton).not.toHaveAttribute('aria-disabled')
836
+ }
837
+ })
838
+
839
+ describe('excludedDates', () => {
840
+ test('excluding a day', async () => {
848
841
  const dateToExclude = dayjs()
849
842
  .date(getDateInCurrentMonth())
850
- .startOf("day");
851
- const excludedDay = dateToExclude.date();
852
- const config = { excludedDates: dateToExclude.format("D/M/YYYY") };
843
+ .startOf('day')
844
+ const excludedDay = dateToExclude.date()
845
+ const config = { excludedDates: dateToExclude.format('D/M/YYYY') }
846
+
847
+ const lastDayinMonth = dayjs().endOf('month').date()
848
+
849
+ new MOJFrontend.DatePicker(component, config).init()
850
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
851
+
852
+ await user.click(calendarButton)
853
853
 
854
- const lastDayinMonth = dayjs().endOf("month").date();
854
+ const dayButtonsDisabled = [excludedDay]
855
+ .map(getDateFormatted)
856
+ .map(screen.getByTestId)
855
857
 
856
- new MOJFrontend.DatePicker(component, config).init();
857
- calendarButton = screen.getByRole("button", { name: "Choose date" });
858
- await user.click(calendarButton);
858
+ const dayButtonsEnabled = range(1, lastDayinMonth)
859
+ .filter((day) => day !== excludedDay)
860
+ .map(getDateFormatted)
861
+ .map(screen.getByTestId)
859
862
 
860
- for (let i = 1; i <= lastDayinMonth; i++) {
861
- const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
862
- const dayButton = screen.getByTestId(testId);
863
+ for (const dayButton of dayButtonsDisabled) {
864
+ expect(dayButton).toHaveAttribute('aria-disabled', 'true')
865
+ }
863
866
 
864
- if (i == excludedDay) {
865
- expect(dayButton).toHaveAttribute("aria-disabled", "true");
866
- } else {
867
- expect(dayButton).not.toHaveAttribute("aria-disabled");
868
- }
867
+ for (const dayButton of dayButtonsEnabled) {
868
+ expect(dayButton).not.toHaveAttribute('aria-disabled')
869
869
  }
870
- });
870
+ })
871
871
 
872
- test("excluding a range of days", async () => {
873
- let datesToExclude;
872
+ test('excluding a range of days', async () => {
873
+ let datesToExclude = []
874
874
  if (dayjs().date() < 15) {
875
- datesToExclude = getDateRangeInCurrentMonth(18, 20);
875
+ datesToExclude.push(dayjs().date(18))
876
+ datesToExclude.push(dayjs().date(19))
877
+ datesToExclude.push(dayjs().date(20))
876
878
  } else {
877
- datesToExclude = getDateRangeInCurrentMonth(3, 5);
879
+ datesToExclude.push(dayjs().date(3))
880
+ datesToExclude.push(dayjs().date(4))
881
+ datesToExclude.push(dayjs().date(5))
882
+ }
883
+ datesToExclude = datesToExclude.map((date) => date.startOf('day'))
884
+ const daysToExclude = datesToExclude.map((date) => date.date())
885
+ const lastDayinMonth = dayjs().endOf('month').date()
886
+ const config = {
887
+ excludedDates: `${datesToExclude[0].format('D/M/YYYY')}-${datesToExclude[datesToExclude.length - 1].format('D/M/YYYY')}`
888
+ }
889
+
890
+ new MOJFrontend.DatePicker(component, config).init()
891
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
892
+
893
+ await user.click(calendarButton)
894
+
895
+ const dayButtonsDisabled = daysToExclude
896
+ .map(getDateFormatted)
897
+ .map(screen.getByTestId)
898
+
899
+ const dayButtonsEnabled = range(1, lastDayinMonth)
900
+ .filter((day) => !daysToExclude.includes(day))
901
+ .map(getDateFormatted)
902
+ .map(screen.getByTestId)
903
+
904
+ for (const dayButton of dayButtonsDisabled) {
905
+ expect(dayButton).toHaveAttribute('aria-disabled', 'true')
878
906
  }
879
- datesToExclude = datesToExclude.map((date) => date.startOf("day"));
880
- let daysToExclude = datesToExclude.map((date) => date.date());
881
- const lastDayinMonth = dayjs().endOf("month").date();
882
- config = {
883
- excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
884
- };
885
-
886
- datePicker = new MOJFrontend.DatePicker(component, config).init();
887
- calendarButton = screen.getByRole("button", { name: "Choose date" });
888
- await user.click(calendarButton);
889
-
890
- for (let i = 1; i <= lastDayinMonth; i++) {
891
- const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
892
- const dayButton = screen.getByTestId(testId);
893
-
894
- if (daysToExclude.includes(i)) {
895
- expect(dayButton).toHaveAttribute("aria-disabled", "true");
896
- } else {
897
- expect(dayButton).not.toHaveAttribute("aria-disabled");
898
- }
907
+
908
+ for (const dayButton of dayButtonsEnabled) {
909
+ expect(dayButton).not.toHaveAttribute('aria-disabled')
899
910
  }
900
- });
901
- });
911
+ })
912
+ })
902
913
 
903
- test("excludedDays", async () => {
904
- const config = { excludedDays: "sunday" };
905
- const lastDayinMonth = dayjs().endOf("month").date();
906
- let excludedDays = [];
914
+ test('excludedDays', async () => {
915
+ const config = { excludedDays: 'sunday' }
916
+ const lastDayinMonth = dayjs().endOf('month').date()
917
+ const excludedDays = []
907
918
  for (let i = 1; i <= lastDayinMonth; i++) {
908
919
  if (dayjs().date(i).day() === 0) {
909
- excludedDays.push(i);
920
+ excludedDays.push(i)
910
921
  }
911
922
  }
912
- new MOJFrontend.DatePicker(component, config).init();
913
- calendarButton = screen.getByRole("button", { name: "Choose date" });
914
- await user.click(calendarButton);
923
+ new MOJFrontend.DatePicker(component, config).init()
924
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
915
925
 
916
- for (let i = 1; i <= lastDayinMonth; i++) {
917
- const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
918
- const dayButton = screen.getByTestId(testId);
926
+ await user.click(calendarButton)
919
927
 
920
- if (excludedDays.includes(i)) {
921
- expect(dayButton).toHaveAttribute("aria-disabled", "true");
922
- } else {
923
- expect(dayButton).not.toHaveAttribute("aria-disabled");
924
- }
928
+ const dayButtonsDisabled = excludedDays
929
+ .map(getDateFormatted)
930
+ .map(screen.getByTestId)
931
+
932
+ const dayButtonsEnabled = range(1, lastDayinMonth)
933
+ .filter((day) => !excludedDays.includes(day))
934
+ .map(getDateFormatted)
935
+ .map(screen.getByTestId)
936
+
937
+ for (const dayButton of dayButtonsDisabled) {
938
+ expect(dayButton).toHaveAttribute('aria-disabled', 'true')
939
+ }
940
+
941
+ for (const dayButton of dayButtonsEnabled) {
942
+ expect(dayButton).not.toHaveAttribute('aria-disabled')
925
943
  }
926
- });
944
+ })
927
945
 
928
- test("default weekStartDay", async () => {
929
- new MOJFrontend.DatePicker(component, {}).init();
930
- calendarButton = screen.getByRole("button", { name: "Choose date" });
931
- await user.click(calendarButton);
932
- const headers = getAllByRole(component, "columnheader");
946
+ test('default weekStartDay', async () => {
947
+ new MOJFrontend.DatePicker(component, {}).init()
948
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
949
+ await user.click(calendarButton)
950
+ const headers = getAllByRole(component, 'columnheader')
933
951
 
934
- expect(headers[0]).toHaveAccessibleName("Monday");
935
- });
952
+ expect(headers[0]).toHaveAccessibleName('Monday')
953
+ })
936
954
 
937
- test("weekStartDay Sunday", async () => {
938
- new MOJFrontend.DatePicker(component, { weekStartDay: "sunday" }).init();
939
- calendarButton = screen.getByRole("button", { name: "Choose date" });
940
- await user.click(calendarButton);
941
- const headers = getAllByRole(component, "columnheader");
955
+ test('weekStartDay Sunday', async () => {
956
+ new MOJFrontend.DatePicker(component, { weekStartDay: 'sunday' }).init()
957
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
958
+ await user.click(calendarButton)
959
+ const headers = getAllByRole(component, 'columnheader')
942
960
 
943
- expect(headers[0]).toHaveAccessibleName("Sunday");
944
- });
945
- });
946
- });
961
+ expect(headers[0]).toHaveAccessibleName('Sunday')
962
+ })
963
+ })
964
+ })
947
965
 
948
- describe("Datepicker data-attributes API", () => {
949
- let component;
950
- let calendarButton;
951
- let input;
966
+ describe('Datepicker data-attributes API', () => {
967
+ let component
968
+ let calendarButton
969
+ let input
952
970
 
953
- beforeEach(() => {});
971
+ beforeEach(() => {})
954
972
 
955
973
  afterEach(() => {
956
- document.body.innerHTML = "";
957
- });
974
+ document.body.innerHTML = ''
975
+ })
958
976
 
959
- test("with leadingZeros false", async () => {
960
- component = createComponent({ leadingZeros: "false" });
961
- new MOJFrontend.DatePicker(component).init();
977
+ test('with leadingZeros false', async () => {
978
+ component = createComponent({ leadingZeros: 'false' })
979
+ new MOJFrontend.DatePicker(component).init()
962
980
 
963
- input = screen.getByLabelText("Date");
964
- calendarButton = screen.getByRole("button", { name: "Choose date" });
965
- const dateToSelect = screen.queryByText("9")?.closest("button");
966
- const selectedDate = dayjs().date(9);
981
+ input = screen.getByLabelText('Date')
982
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
983
+ const dateToSelect = dayjs().date(9)
984
+ const dateButton = screen.getByTestId(dateToSelect.format('D/M/YYYY'))
967
985
 
968
- await user.click(calendarButton);
969
- await user.click(dateToSelect);
986
+ await user.click(calendarButton)
987
+ await user.click(dateButton)
970
988
 
971
- expect(input).toHaveValue(selectedDate.format("D/M/YYYY"));
972
- });
989
+ expect(input).toHaveValue(dateToSelect.format('D/M/YYYY'))
990
+ })
973
991
 
974
- test("with leadingZeros true", async () => {
975
- const component = createComponent({ leadingZeros: "true" });
976
- new MOJFrontend.DatePicker(component).init();
992
+ test('with leadingZeros true', async () => {
993
+ const component = createComponent({ leadingZeros: 'true' })
994
+ new MOJFrontend.DatePicker(component).init()
977
995
 
978
- input = screen.getByLabelText("Date");
979
- calendarButton = screen.getByRole("button", { name: "Choose date" });
980
- const dateToSelect = screen.queryByText("9")?.closest("button");
981
- const selectedDate = dayjs().date(9);
996
+ input = screen.getByLabelText('Date')
997
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
998
+ const dateToSelect = dayjs().date(9)
999
+ const dateButton = screen.getByTestId(dateToSelect.format('DD/MM/YYYY'))
982
1000
 
983
- await user.click(calendarButton);
984
- await user.click(dateToSelect);
1001
+ await user.click(calendarButton)
1002
+ await user.click(dateButton)
985
1003
 
986
- expect(input).toHaveValue(selectedDate.format("DD/MM/YYYY"));
987
- });
1004
+ expect(input).toHaveValue(dateToSelect.format('DD/MM/YYYY'))
1005
+ })
988
1006
 
989
- test.skip.failing("minDate", async () => {
990
- const minDay = 3;
991
- const lastDayinMonth = dayjs().endOf("month").date();
992
- const minDate = dayjs().date(minDay);
1007
+ test('minDate', async () => {
1008
+ const minDay = 3
1009
+ const lastDayinMonth = dayjs().endOf('month').date()
1010
+ const minDate = dayjs().date(minDay)
993
1011
  const component = createComponent({
994
- minDate: minDate.format("DD/MM/YYYY"),
995
- });
996
- new MOJFrontend.DatePicker(component).init();
997
- calendarButton = screen.getByRole("button", { name: "Choose date" });
1012
+ minDate: minDate.format('DD/MM/YYYY')
1013
+ })
1014
+ new MOJFrontend.DatePicker(component).init()
1015
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
998
1016
 
999
- await user.click(calendarButton);
1017
+ await user.click(calendarButton)
1000
1018
 
1001
- for (let i = 1; i <= lastDayinMonth; i++) {
1002
- const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1003
- const dayButton = screen.getByTestId(testId);
1019
+ const dayButtonsDisabled = range(1, minDay - 1)
1020
+ .map(getDateFormatted)
1021
+ .map(screen.getByTestId)
1004
1022
 
1005
- if (i <= minDay) {
1006
- expect(dayButton).toHaveAttribute("aria-disabled", "true");
1007
- } else {
1008
- expect(dayButton).not.toHaveAttribute("aria-disabled");
1009
- }
1023
+ const dayButtonsEnabled = range(minDay, lastDayinMonth)
1024
+ .map(getDateFormatted)
1025
+ .map(screen.getByTestId)
1026
+
1027
+ for (const dayButton of dayButtonsDisabled) {
1028
+ expect(dayButton).toHaveAttribute('aria-disabled', 'true')
1029
+ }
1030
+
1031
+ for (const dayButton of dayButtonsEnabled) {
1032
+ expect(dayButton).not.toHaveAttribute('aria-disabled')
1010
1033
  }
1011
- });
1034
+ })
1012
1035
 
1013
- test("maxDate", async () => {
1014
- const maxDay = 21;
1015
- const lastDayinMonth = dayjs().endOf("month").date();
1016
- const maxDate = dayjs().date(maxDay);
1036
+ test('maxDate', async () => {
1037
+ const maxDay = 21
1038
+ const lastDayinMonth = dayjs().endOf('month').date()
1039
+ const maxDate = dayjs().date(maxDay)
1017
1040
  const component = createComponent({
1018
- maxDate: maxDate.format("DD/MM/YYYY"),
1019
- });
1020
- new MOJFrontend.DatePicker(component).init();
1021
- calendarButton = screen.getByRole("button", { name: "Choose date" });
1041
+ maxDate: maxDate.format('DD/MM/YYYY')
1042
+ })
1043
+ new MOJFrontend.DatePicker(component).init()
1044
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
1022
1045
 
1023
- await user.click(calendarButton);
1046
+ await user.click(calendarButton)
1024
1047
 
1025
- for (let i = 1; i <= lastDayinMonth; i++) {
1026
- const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1027
- const dayButton = screen.getByTestId(testId);
1048
+ const dayButtonsDisabled = range(maxDay + 1, lastDayinMonth)
1049
+ .map(getDateFormatted)
1050
+ .map(screen.getByTestId)
1028
1051
 
1029
- if (i > maxDay) {
1030
- expect(dayButton).toHaveAttribute("aria-disabled", "true");
1031
- } else {
1032
- expect(dayButton).not.toHaveAttribute("aria-disabled");
1033
- }
1052
+ const dayButtonsEnabled = range(1, maxDay)
1053
+ .map(getDateFormatted)
1054
+ .map(screen.getByTestId)
1055
+
1056
+ for (const dayButton of dayButtonsDisabled) {
1057
+ expect(dayButton).toHaveAttribute('aria-disabled', 'true')
1034
1058
  }
1035
- });
1036
-
1037
- describe("excludedDates", () => {
1038
- test("excluding a day", async () => {
1039
- const dateToExclude = dayjs()
1040
- .date(getDateInCurrentMonth())
1041
- .startOf("day");
1042
- const excludedDay = dateToExclude.date();
1043
- const lastDayinMonth = dayjs().endOf("month").date();
1059
+
1060
+ for (const dayButton of dayButtonsEnabled) {
1061
+ expect(dayButton).not.toHaveAttribute('aria-disabled')
1062
+ }
1063
+ })
1064
+
1065
+ describe('excludedDates', () => {
1066
+ test('excluding a day', async () => {
1067
+ const dateToExclude = dayjs().date(getDateInCurrentMonth()).startOf('day')
1068
+ const excludedDay = dateToExclude.date()
1069
+ const lastDayinMonth = dayjs().endOf('month').date()
1044
1070
  const component = createComponent({
1045
- excludedDates: dateToExclude.format("D/M/YYYY"),
1046
- });
1047
- new MOJFrontend.DatePicker(component).init();
1048
- calendarButton = screen.getByRole("button", { name: "Choose date" });
1071
+ excludedDates: dateToExclude.format('D/M/YYYY')
1072
+ })
1073
+ new MOJFrontend.DatePicker(component).init()
1074
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
1049
1075
 
1050
- await user.click(calendarButton);
1076
+ await user.click(calendarButton)
1051
1077
 
1052
- for (let i = 1; i <= lastDayinMonth; i++) {
1053
- const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1054
- const dayButton = screen.getByTestId(testId);
1078
+ const dayButtonsDisabled = [excludedDay]
1079
+ .map(getDateFormatted)
1080
+ .map(screen.getByTestId)
1055
1081
 
1056
- if (i == excludedDay) {
1057
- expect(dayButton).toHaveAttribute("aria-disabled", "true");
1058
- } else {
1059
- expect(dayButton).not.toHaveAttribute("aria-disabled");
1060
- }
1082
+ const dayButtonsEnabled = range(1, lastDayinMonth)
1083
+ .filter((day) => day !== excludedDay)
1084
+ .map(getDateFormatted)
1085
+ .map(screen.getByTestId)
1086
+
1087
+ for (const dayButton of dayButtonsDisabled) {
1088
+ expect(dayButton).toHaveAttribute('aria-disabled', 'true')
1089
+ }
1090
+
1091
+ for (const dayButton of dayButtonsEnabled) {
1092
+ expect(dayButton).not.toHaveAttribute('aria-disabled')
1061
1093
  }
1062
- });
1094
+ })
1063
1095
 
1064
- test("excluding a range of days", async () => {
1065
- let datesToExclude;
1096
+ test('excluding a range of days', async () => {
1097
+ let datesToExclude = []
1066
1098
  if (dayjs().date() < 15) {
1067
- datesToExclude = getDateRangeInCurrentMonth(18, 20);
1099
+ datesToExclude.push(dayjs().date(18))
1100
+ datesToExclude.push(dayjs().date(19))
1101
+ datesToExclude.push(dayjs().date(20))
1068
1102
  } else {
1069
- datesToExclude = getDateRangeInCurrentMonth(3, 5);
1103
+ datesToExclude.push(dayjs().date(3))
1104
+ datesToExclude.push(dayjs().date(4))
1105
+ datesToExclude.push(dayjs().date(5))
1070
1106
  }
1071
- datesToExclude = datesToExclude.map((date) => date.startOf("day"));
1072
- let daysToExclude = datesToExclude.map((date) => date.date());
1073
- const lastDayinMonth = dayjs().endOf("month").date();
1107
+ datesToExclude = datesToExclude.map((date) => date.startOf('day'))
1108
+ const daysToExclude = datesToExclude.map((date) => date.date())
1109
+ const lastDayinMonth = dayjs().endOf('month').date()
1074
1110
  component = createComponent({
1075
- excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
1076
- });
1077
- datePicker = new MOJFrontend.DatePicker(component).init();
1078
- calendarButton = screen.getByRole("button", { name: "Choose date" });
1111
+ excludedDates: `${datesToExclude[0].format('D/M/YYYY')}-${datesToExclude[datesToExclude.length - 1].format('D/M/YYYY')}`
1112
+ })
1113
+ new MOJFrontend.DatePicker(component).init()
1114
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
1079
1115
 
1080
- await user.click(calendarButton);
1116
+ await user.click(calendarButton)
1081
1117
 
1082
- for (let i = 1; i <= lastDayinMonth; i++) {
1083
- const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1084
- const dayButton = screen.getByTestId(testId);
1118
+ const dayButtonsDisabled = daysToExclude
1119
+ .map(getDateFormatted)
1120
+ .map(screen.getByTestId)
1085
1121
 
1086
- if (daysToExclude.includes(i)) {
1087
- expect(dayButton).toHaveAttribute("aria-disabled", "true");
1088
- } else {
1089
- expect(dayButton).not.toHaveAttribute("aria-disabled");
1090
- }
1122
+ const dayButtonsEnabled = range(1, lastDayinMonth)
1123
+ .filter((day) => !daysToExclude.includes(day))
1124
+ .map(getDateFormatted)
1125
+ .map(screen.getByTestId)
1126
+
1127
+ for (const dayButton of dayButtonsDisabled) {
1128
+ expect(dayButton).toHaveAttribute('aria-disabled', 'true')
1091
1129
  }
1092
- });
1093
- });
1094
1130
 
1095
- test("excludedDays", async () => {
1096
- const component = createComponent({ excludedDays: "sunday" });
1097
- const lastDayinMonth = dayjs().endOf("month").date();
1098
- let excludedDays = [];
1131
+ for (const dayButton of dayButtonsEnabled) {
1132
+ expect(dayButton).not.toHaveAttribute('aria-disabled')
1133
+ }
1134
+ })
1135
+ })
1136
+
1137
+ test('excludedDays', async () => {
1138
+ const component = createComponent({ excludedDays: 'sunday' })
1139
+ const lastDayinMonth = dayjs().endOf('month').date()
1140
+ const excludedDays = []
1099
1141
  for (let i = 1; i <= lastDayinMonth; i++) {
1100
1142
  if (dayjs().date(i).day() === 0) {
1101
- excludedDays.push(i);
1143
+ excludedDays.push(i)
1102
1144
  }
1103
1145
  }
1104
- new MOJFrontend.DatePicker(component).init();
1105
- calendarButton = screen.getByRole("button", { name: "Choose date" });
1146
+ new MOJFrontend.DatePicker(component).init()
1147
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
1106
1148
 
1107
- await user.click(calendarButton);
1149
+ await user.click(calendarButton)
1108
1150
 
1109
- for (let i = 1; i <= lastDayinMonth; i++) {
1110
- const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1111
- const dayButton = screen.getByTestId(testId);
1151
+ const dayButtonsDisabled = excludedDays
1152
+ .map(getDateFormatted)
1153
+ .map(screen.getByTestId)
1112
1154
 
1113
- if (excludedDays.includes(i)) {
1114
- expect(dayButton).toHaveAttribute("aria-disabled", "true");
1115
- } else {
1116
- expect(dayButton).not.toHaveAttribute("aria-disabled");
1117
- }
1155
+ const dayButtonsEnabled = range(1, lastDayinMonth)
1156
+ .filter((day) => !excludedDays.includes(day))
1157
+ .map(getDateFormatted)
1158
+ .map(screen.getByTestId)
1159
+
1160
+ for (const dayButton of dayButtonsDisabled) {
1161
+ expect(dayButton).toHaveAttribute('aria-disabled', 'true')
1162
+ }
1163
+
1164
+ for (const dayButton of dayButtonsEnabled) {
1165
+ expect(dayButton).not.toHaveAttribute('aria-disabled')
1118
1166
  }
1119
- });
1120
-
1121
- test("weekStartDay", async () => {
1122
- component = createComponent({ weekStartDay: "sunday" });
1123
- new MOJFrontend.DatePicker(component).init();
1124
- calendarButton = screen.getByRole("button", { name: "Choose date" });
1125
- await user.click(calendarButton);
1126
- const headers = getAllByRole(component, "columnheader");
1127
-
1128
- expect(headers[0]).toHaveAccessibleName("Sunday");
1129
- });
1130
- });
1167
+ })
1168
+
1169
+ test('weekStartDay', async () => {
1170
+ component = createComponent({ weekStartDay: 'sunday' })
1171
+ new MOJFrontend.DatePicker(component).init()
1172
+ calendarButton = screen.getByRole('button', { name: 'Choose date' })
1173
+ await user.click(calendarButton)
1174
+ const headers = getAllByRole(component, 'columnheader')
1175
+
1176
+ expect(headers[0]).toHaveAccessibleName('Sunday')
1177
+ })
1178
+ })