@ministryofjustice/frontend 2.2.5 → 3.0.1
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/moj/all.jquery.min.js +11 -3
- package/moj/all.js +327 -138
- package/moj/components/button-menu/_button-menu.scss +71 -109
- package/moj/components/button-menu/button-menu.js +292 -123
- package/moj/components/button-menu/button-menu.spec.js +363 -0
- package/moj/components/button-menu/template.njk +32 -6
- package/moj/components/date-picker/date-picker.js +29 -15
- package/moj/components/date-picker/date-picker.spec.js +571 -0
- package/moj/components/identity-bar/_identity-bar.scss +5 -9
- package/moj/components/identity-bar/template.njk +17 -22
- package/moj/components/page-header-actions/_page-header-actions.scss +9 -39
- package/moj/components/page-header-actions/template.njk +16 -16
- package/moj/objects/_all.scss +1 -0
- package/moj/objects/_button-group.scss +37 -0
- package/package.json +1 -1
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
const { queryByRole, screen } = require("@testing-library/dom");
|
|
2
|
+
const { userEvent } = require("@testing-library/user-event");
|
|
3
|
+
const { configureAxe, toHaveNoViolations } = require("jest-axe");
|
|
4
|
+
expect.extend(toHaveNoViolations);
|
|
5
|
+
|
|
6
|
+
require("../../../../jest.setup.js");
|
|
7
|
+
require("./button-menu.js");
|
|
8
|
+
|
|
9
|
+
const user = userEvent.setup();
|
|
10
|
+
const axe = configureAxe({
|
|
11
|
+
rules: {
|
|
12
|
+
// disable landmark rules when testing isolated components.
|
|
13
|
+
region: { enabled: false },
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const kebabize = (str) => {
|
|
18
|
+
return str.replace(
|
|
19
|
+
/[A-Z]+(?![a-z])|[A-Z]/g,
|
|
20
|
+
($, ofset) => (ofset ? "-" : "") + $.toLowerCase(),
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const configToDataAttributes = (config) => {
|
|
25
|
+
let attributes = "";
|
|
26
|
+
for (let [key, value] of Object.entries(config)) {
|
|
27
|
+
attributes += `data-${kebabize(key)}="${value}" `;
|
|
28
|
+
}
|
|
29
|
+
return attributes;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const createComponent = (config = {}, html) => {
|
|
33
|
+
const dataAttributes = configToDataAttributes(config);
|
|
34
|
+
if(typeof html === "undefined") {
|
|
35
|
+
html = `
|
|
36
|
+
<div class="moj-button-menu" data-module="moj-button-menu" ${dataAttributes}>
|
|
37
|
+
<a href="#one" role="button">First action</a>
|
|
38
|
+
<a href="#two" role="button" class="govuk-button--warning">Second action</a>
|
|
39
|
+
<a href="#three" role="button" class="custom-class">Third action</a>
|
|
40
|
+
</div>`;
|
|
41
|
+
}
|
|
42
|
+
document.body.insertAdjacentHTML("afterbegin", html);
|
|
43
|
+
|
|
44
|
+
component = document.querySelector('[data-module="moj-button-menu"]');
|
|
45
|
+
return component;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
describe("Button menu with defaults", () => {
|
|
49
|
+
let component;
|
|
50
|
+
let toggleButton;
|
|
51
|
+
let menu;
|
|
52
|
+
let items;
|
|
53
|
+
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
component = createComponent();
|
|
56
|
+
new MOJFrontend.ButtonMenu(component).init();
|
|
57
|
+
|
|
58
|
+
toggleButton = queryByRole(component, "button", { hidden: false });
|
|
59
|
+
menu = screen.queryByRole("list", { hidden: true });
|
|
60
|
+
items = menu?.querySelectorAll("a, button");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
document.body.innerHTML = "";
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("initialises component elements", () => {
|
|
68
|
+
expect(toggleButton).not.toBeNull();
|
|
69
|
+
expect(menu).not.toBeNull();
|
|
70
|
+
expect(items).not.toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("intialises toggle button", () => {
|
|
74
|
+
expect(component).toContainElement(toggleButton);
|
|
75
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "false");
|
|
76
|
+
expect(toggleButton).toHaveAttribute("aria-haspopup", "true");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("intialises menu", () => {
|
|
80
|
+
expect(component).toContainElement(menu);
|
|
81
|
+
expect(menu).not.toBeVisible();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("creates menuitems", () => {
|
|
85
|
+
expect(items.length).toBe(3);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("removes other govuk-button classes from menuitems", () => {
|
|
89
|
+
expect(items[1]).not.toHaveClass("govuk-button--warning");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("keeps custom classes on items", () => {
|
|
93
|
+
expect(items[2]).toHaveClass("custom-class");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("clicking toggle button shows menu", async () => {
|
|
97
|
+
await user.click(toggleButton);
|
|
98
|
+
|
|
99
|
+
expect(menu).toBeVisible();
|
|
100
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "true");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("clicking a link in the menu", async () => {
|
|
104
|
+
await user.click(toggleButton);
|
|
105
|
+
|
|
106
|
+
expect(menu).toBeVisible();
|
|
107
|
+
await user.click(items[0]);
|
|
108
|
+
expect(global.window.location.hash).toContain("#one");
|
|
109
|
+
await user.click(items[2]);
|
|
110
|
+
expect(global.window.location.hash).toContain("#three");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("clicking outside closes menu", async () => {
|
|
114
|
+
await user.click(toggleButton);
|
|
115
|
+
expect(menu).toBeVisible();
|
|
116
|
+
|
|
117
|
+
await user.click(document.body);
|
|
118
|
+
expect(menu).not.toBeVisible();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("keyboard interactions", () => {
|
|
122
|
+
test("enter on toggle button opens menu", async () => {
|
|
123
|
+
toggleButton.focus();
|
|
124
|
+
await user.keyboard("[Enter]");
|
|
125
|
+
|
|
126
|
+
expect(menu).toBeVisible();
|
|
127
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "true");
|
|
128
|
+
expect(items[0]).toHaveFocus();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("space on toggle button opens menu", async () => {
|
|
132
|
+
toggleButton.focus();
|
|
133
|
+
await user.keyboard("[Space]");
|
|
134
|
+
|
|
135
|
+
expect(menu).toBeVisible();
|
|
136
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "true");
|
|
137
|
+
expect(items[0]).toHaveFocus();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("esc closes menu", async () => {
|
|
141
|
+
toggleButton.focus();
|
|
142
|
+
await user.keyboard("[Space]");
|
|
143
|
+
expect(menu).toBeVisible();
|
|
144
|
+
await user.keyboard("[Escape]");
|
|
145
|
+
|
|
146
|
+
expect(menu).not.toBeVisible();
|
|
147
|
+
expect(toggleButton).toHaveFocus();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("down arrow on toggle button opens menu with focus on first item", async () => {
|
|
151
|
+
toggleButton.focus();
|
|
152
|
+
await user.keyboard("[ArrowDown]");
|
|
153
|
+
|
|
154
|
+
expect(menu).toBeVisible();
|
|
155
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "true");
|
|
156
|
+
expect(items[0]).toHaveFocus();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("up arrow on toggle button opens menu with focus on last item", async () => {
|
|
160
|
+
toggleButton.focus();
|
|
161
|
+
await user.keyboard("[ArrowUp]");
|
|
162
|
+
|
|
163
|
+
expect(menu).toBeVisible();
|
|
164
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "true");
|
|
165
|
+
expect(items[items.length - 1]).toHaveFocus();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("down arrow on menu item navigates to next item with looping", async () => {
|
|
169
|
+
toggleButton.focus();
|
|
170
|
+
await user.keyboard("[Enter]");
|
|
171
|
+
expect(items[0]).toHaveFocus();
|
|
172
|
+
|
|
173
|
+
await user.keyboard("[ArrowDown]");
|
|
174
|
+
expect(items[1]).toHaveFocus();
|
|
175
|
+
|
|
176
|
+
await user.keyboard("[ArrowDown]");
|
|
177
|
+
expect(items[2]).toHaveFocus();
|
|
178
|
+
|
|
179
|
+
await user.keyboard("[ArrowDown]");
|
|
180
|
+
expect(items[0]).toHaveFocus();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("up arrow on menu item navigates to previous item with looping", async () => {
|
|
184
|
+
toggleButton.focus();
|
|
185
|
+
await user.keyboard("[ArrowUp]");
|
|
186
|
+
expect(items[items.length - 1]).toHaveFocus();
|
|
187
|
+
|
|
188
|
+
await user.keyboard("[ArrowUp]");
|
|
189
|
+
expect(items[1]).toHaveFocus();
|
|
190
|
+
|
|
191
|
+
await user.keyboard("[ArrowUp]");
|
|
192
|
+
expect(items[0]).toHaveFocus();
|
|
193
|
+
|
|
194
|
+
await user.keyboard("[ArrowUp]");
|
|
195
|
+
expect(items[items.length - 1]).toHaveFocus();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("home navigates to first item", async () => {
|
|
199
|
+
toggleButton.focus();
|
|
200
|
+
await user.keyboard("[ArrowUp]");
|
|
201
|
+
expect(items[items.length - 1]).toHaveFocus();
|
|
202
|
+
|
|
203
|
+
await user.keyboard("[Home]");
|
|
204
|
+
expect(items[0]).toHaveFocus();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("end navigates to last item", async () => {
|
|
208
|
+
toggleButton.focus();
|
|
209
|
+
await user.keyboard("[Enter]");
|
|
210
|
+
expect(items[0]).toHaveFocus();
|
|
211
|
+
|
|
212
|
+
await user.keyboard("[End]");
|
|
213
|
+
expect(items[items.length - 1]).toHaveFocus();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("tab moves focus out of the menu", async () => {
|
|
217
|
+
toggleButton.focus();
|
|
218
|
+
await user.keyboard("[Enter]");
|
|
219
|
+
expect(menu).toBeVisible();
|
|
220
|
+
expect(items[0]).toHaveFocus();
|
|
221
|
+
await user.tab();
|
|
222
|
+
|
|
223
|
+
expect(document.body).toHaveFocus();
|
|
224
|
+
expect(menu).not.toBeVisible();
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe("accessibility", () => {
|
|
229
|
+
test("component has no wcag violations", async () => {
|
|
230
|
+
expect(await axe(document.body)).toHaveNoViolations();
|
|
231
|
+
await user.click(toggleButton);
|
|
232
|
+
expect(await axe(document.body)).toHaveNoViolations();
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe("Button menu javascript API", () => {
|
|
238
|
+
let component;
|
|
239
|
+
|
|
240
|
+
beforeEach(() => {
|
|
241
|
+
component = createComponent();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
afterEach(() => {
|
|
245
|
+
document.body.innerHTML = "";
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("setting toggle button text", () => {
|
|
249
|
+
const label = "click me";
|
|
250
|
+
new MOJFrontend.ButtonMenu(component, { buttonText: label }).init();
|
|
251
|
+
const toggleButton = queryByRole(component, "button", { name: label });
|
|
252
|
+
|
|
253
|
+
expect(toggleButton).not.toBeNull;
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("setting menu alignment", () => {
|
|
257
|
+
new MOJFrontend.ButtonMenu(component, { alignMenu: "right" }).init();
|
|
258
|
+
const menu = screen.queryByRole("list", { hidden: true });
|
|
259
|
+
|
|
260
|
+
expect(menu).toHaveClass("moj-button-menu__wrapper--right");
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("setting button classes", () => {
|
|
264
|
+
const defaultClassNames = "govuk-button moj-button-menu__toggle-button";
|
|
265
|
+
const classNames = "classOne classTwo";
|
|
266
|
+
|
|
267
|
+
new MOJFrontend.ButtonMenu(component, { buttonClasses: classNames }).init();
|
|
268
|
+
const toggleButton = queryByRole(component, "button", { hidden: false });
|
|
269
|
+
|
|
270
|
+
expect(toggleButton).toHaveClass(defaultClassNames);
|
|
271
|
+
expect(toggleButton).toHaveClass(classNames);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe("Button menu data-attributes API", () => {
|
|
276
|
+
let component;
|
|
277
|
+
|
|
278
|
+
beforeEach(() => {});
|
|
279
|
+
|
|
280
|
+
afterEach(() => {
|
|
281
|
+
document.body.innerHTML = "";
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test("setting toggle button text", () => {
|
|
285
|
+
const label = "click me";
|
|
286
|
+
|
|
287
|
+
component = createComponent({ buttonText: label });
|
|
288
|
+
new MOJFrontend.ButtonMenu(component).init();
|
|
289
|
+
const toggleButton = queryByRole(component, "button", { name: label });
|
|
290
|
+
|
|
291
|
+
expect(toggleButton).not.toBeNull();
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("setting menu alignment", () => {
|
|
295
|
+
component = createComponent({ alignMenu: "right" });
|
|
296
|
+
new MOJFrontend.ButtonMenu(component).init();
|
|
297
|
+
const menu = screen.queryByRole("list", { hidden: true });
|
|
298
|
+
|
|
299
|
+
expect(menu).toHaveClass("moj-button-menu__wrapper--right");
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("setting button classes", () => {
|
|
303
|
+
const defaultClassNames = "govuk-button moj-button-menu__toggle-button";
|
|
304
|
+
const classNames = "classOne classTwo";
|
|
305
|
+
|
|
306
|
+
component = createComponent({ buttonClasses: classNames });
|
|
307
|
+
new MOJFrontend.ButtonMenu(component).init();
|
|
308
|
+
const toggleButton = queryByRole(component, "button", { hidden: false });
|
|
309
|
+
|
|
310
|
+
expect(toggleButton).toHaveClass(defaultClassNames);
|
|
311
|
+
expect(toggleButton).toHaveClass(classNames);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe("menu button with a single item", () => {
|
|
316
|
+
let component;
|
|
317
|
+
let toggleButton;
|
|
318
|
+
let menu;
|
|
319
|
+
let items;
|
|
320
|
+
|
|
321
|
+
beforeEach(() => {
|
|
322
|
+
const html = `
|
|
323
|
+
<div class="moj-button-menu" data-module="moj-button-menu" data-button-classes="govuk-button--warning custom-class">
|
|
324
|
+
<a href="#one" role="button" class="govuk-button--inverse">First action</a>
|
|
325
|
+
</div>`;
|
|
326
|
+
|
|
327
|
+
component = createComponent({}, html);
|
|
328
|
+
new MOJFrontend.ButtonMenu(component).init();
|
|
329
|
+
|
|
330
|
+
toggleButton = queryByRole(component, "button", { name: "Actions" });
|
|
331
|
+
menu = screen.queryByRole("list", { hidden: true });
|
|
332
|
+
items = menu?.queryByRole("button", { hidden: true });
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
afterEach(() => {
|
|
336
|
+
document.body.innerHTML = "";
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test("menu is not created", () => {
|
|
340
|
+
expect(menu).toBeNull();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test("there are no items", () => {
|
|
344
|
+
expect(items).toBeUndefined();
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test("there is no toggle button", () => {
|
|
348
|
+
expect(toggleButton).toBeNull();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("first item has become button", () => {
|
|
352
|
+
const button = screen.queryByRole("button", { name: "First action" });
|
|
353
|
+
|
|
354
|
+
expect(button).not.toBeNull();
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("first item has buttonClasses config applied", () => {
|
|
358
|
+
const button = screen.queryByRole("button", { name: "First action" });
|
|
359
|
+
|
|
360
|
+
expect(button).toHaveClass("govuk-button--warning", "custom-class");
|
|
361
|
+
expect(button).not.toHaveClass("govuk-button--inverse");
|
|
362
|
+
});
|
|
363
|
+
});
|
|
@@ -1,11 +1,38 @@
|
|
|
1
1
|
{%- from "govuk/components/button/macro.njk" import govukButton %}
|
|
2
|
+
{% from "govuk/macros/attributes.njk" import govukAttributes %}
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
{#- Set classes for this component #}
|
|
5
|
+
{%- set classNames = "moj-button-menu" -%}
|
|
6
|
+
{%- set itemClassNames = "moj-button-menu__item govuk-button--secondary" %}
|
|
7
|
+
|
|
8
|
+
{%- if params.classes %}
|
|
9
|
+
{% set classNames = classNames + " " + params.classes %}
|
|
10
|
+
{% endif %}
|
|
11
|
+
|
|
12
|
+
{%- set buttonAttributes = {
|
|
13
|
+
"data-button-text": {
|
|
14
|
+
value: params.button.text,
|
|
15
|
+
optional: true
|
|
16
|
+
},
|
|
17
|
+
"data-button-classes": {
|
|
18
|
+
value: params.button.classes,
|
|
19
|
+
optional: true
|
|
20
|
+
},
|
|
21
|
+
"data-align-menu": {
|
|
22
|
+
value: params.alignMenu,
|
|
23
|
+
optional: true
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
%}
|
|
27
|
+
|
|
28
|
+
<div class="{{- classNames -}}" data-module="moj-button-menu" {{- govukAttributes(params.attributes) -}} {{- govukAttributes(buttonAttributes) -}}>
|
|
5
29
|
{%- for item in params.items %}
|
|
6
|
-
{
|
|
30
|
+
{%- if item.classes %}
|
|
31
|
+
{% set itemClassNames = itemClassNames + " " + item.classes %}
|
|
32
|
+
{% endif %}
|
|
33
|
+
{{ govukButton({
|
|
7
34
|
element: item.element,
|
|
8
|
-
classes:
|
|
35
|
+
classes: itemClassNames,
|
|
9
36
|
text: item.text,
|
|
10
37
|
html: item.html,
|
|
11
38
|
name: item.name,
|
|
@@ -15,8 +42,7 @@
|
|
|
15
42
|
disabled: item.disabled,
|
|
16
43
|
attributes: item.attributes,
|
|
17
44
|
preventDoubleClick: items.preventDoubleClick
|
|
18
|
-
|
|
45
|
+
}) }}
|
|
19
46
|
{% endfor -%}
|
|
20
|
-
</div>
|
|
21
47
|
</div>
|
|
22
48
|
|
|
@@ -94,7 +94,7 @@ Datepicker.prototype.init = function () {
|
|
|
94
94
|
|
|
95
95
|
this.setOptions();
|
|
96
96
|
this.initControls();
|
|
97
|
-
this.$module.setAttribute(
|
|
97
|
+
this.$module.setAttribute("data-initialized", "true");
|
|
98
98
|
};
|
|
99
99
|
|
|
100
100
|
Datepicker.prototype.initControls = function () {
|
|
@@ -203,6 +203,7 @@ Datepicker.prototype.createDialog = function () {
|
|
|
203
203
|
$dialog.setAttribute("aria-modal", "true");
|
|
204
204
|
$dialog.setAttribute("aria-labelledby", titleId);
|
|
205
205
|
$dialog.innerHTML = this.dialogTemplate(titleId);
|
|
206
|
+
$dialog.hidden = true;
|
|
206
207
|
|
|
207
208
|
return $dialog;
|
|
208
209
|
};
|
|
@@ -340,20 +341,14 @@ Datepicker.prototype.setOptions = function () {
|
|
|
340
341
|
|
|
341
342
|
Datepicker.prototype.setMinAndMaxDatesOnCalendar = function () {
|
|
342
343
|
if (this.config.minDate) {
|
|
343
|
-
this.minDate = this.formattedDateFromString(
|
|
344
|
-
this.config.minDate,
|
|
345
|
-
null,
|
|
346
|
-
);
|
|
344
|
+
this.minDate = this.formattedDateFromString(this.config.minDate, null);
|
|
347
345
|
if (this.minDate && this.currentDate < this.minDate) {
|
|
348
346
|
this.currentDate = this.minDate;
|
|
349
347
|
}
|
|
350
348
|
}
|
|
351
349
|
|
|
352
350
|
if (this.config.maxDate) {
|
|
353
|
-
this.maxDate = this.formattedDateFromString(
|
|
354
|
-
this.config.maxDate,
|
|
355
|
-
null,
|
|
356
|
-
);
|
|
351
|
+
this.maxDate = this.formattedDateFromString(this.config.maxDate, null);
|
|
357
352
|
if (this.maxDate && this.currentDate > this.maxDate) {
|
|
358
353
|
this.currentDate = this.maxDate;
|
|
359
354
|
}
|
|
@@ -483,7 +478,7 @@ Datepicker.prototype.formattedDateFromString = function (
|
|
|
483
478
|
const month = match[3];
|
|
484
479
|
const year = match[4];
|
|
485
480
|
|
|
486
|
-
formattedDate = new Date(`${
|
|
481
|
+
formattedDate = new Date(`${year}-${month}-${day}`);
|
|
487
482
|
if (formattedDate instanceof Date && !isNaN(formattedDate)) {
|
|
488
483
|
return formattedDate;
|
|
489
484
|
}
|
|
@@ -571,7 +566,6 @@ Datepicker.prototype.updateCalendar = function () {
|
|
|
571
566
|
|
|
572
567
|
Datepicker.prototype.setCurrentDate = function (focus = true) {
|
|
573
568
|
const { currentDate } = this;
|
|
574
|
-
|
|
575
569
|
this.calendarDays.forEach((calendarDay) => {
|
|
576
570
|
calendarDay.button.classList.add("moj-datepicker__button");
|
|
577
571
|
calendarDay.button.classList.add("moj-datepicker__calendar-day");
|
|
@@ -599,10 +593,10 @@ Datepicker.prototype.setCurrentDate = function (focus = true) {
|
|
|
599
593
|
calendarDayDate.getTime() === this.inputDate.getTime()
|
|
600
594
|
) {
|
|
601
595
|
calendarDay.button.classList.add(this.currentDayButtonClass);
|
|
602
|
-
calendarDay.button.setAttribute("aria-
|
|
596
|
+
calendarDay.button.setAttribute("aria-current", "date");
|
|
603
597
|
} else {
|
|
604
598
|
calendarDay.button.classList.remove(this.currentDayButtonClass);
|
|
605
|
-
calendarDay.button.removeAttribute("aria-
|
|
599
|
+
calendarDay.button.removeAttribute("aria-current");
|
|
606
600
|
}
|
|
607
601
|
|
|
608
602
|
if (calendarDayDate.getTime() === today.getTime()) {
|
|
@@ -657,6 +651,7 @@ Datepicker.prototype.toggleDialog = function (event) {
|
|
|
657
651
|
};
|
|
658
652
|
|
|
659
653
|
Datepicker.prototype.openDialog = function () {
|
|
654
|
+
this.$dialog.hidden = false;
|
|
660
655
|
this.$dialog.classList.add("moj-datepicker__dialog--open");
|
|
661
656
|
this.$calendarButton.setAttribute("aria-expanded", "true");
|
|
662
657
|
|
|
@@ -677,6 +672,7 @@ Datepicker.prototype.openDialog = function () {
|
|
|
677
672
|
};
|
|
678
673
|
|
|
679
674
|
Datepicker.prototype.closeDialog = function () {
|
|
675
|
+
this.$dialog.hidden = true;
|
|
680
676
|
this.$dialog.classList.remove("moj-datepicker__dialog--open");
|
|
681
677
|
this.$calendarButton.setAttribute("aria-expanded", "false");
|
|
682
678
|
this.$calendarButton.focus();
|
|
@@ -724,13 +720,31 @@ Datepicker.prototype.focusPreviousWeek = function () {
|
|
|
724
720
|
|
|
725
721
|
Datepicker.prototype.focusFirstDayOfWeek = function () {
|
|
726
722
|
const date = new Date(this.currentDate);
|
|
727
|
-
|
|
723
|
+
const firstDayOfWeekIndex = this.config.weekStartDay == "sunday" ? 0 : 1;
|
|
724
|
+
const dayOfWeek = date.getDay();
|
|
725
|
+
const diff =
|
|
726
|
+
dayOfWeek >= firstDayOfWeekIndex
|
|
727
|
+
? dayOfWeek - firstDayOfWeekIndex
|
|
728
|
+
: 6 - dayOfWeek;
|
|
729
|
+
|
|
730
|
+
date.setDate(date.getDate() - diff);
|
|
731
|
+
date.setHours(0, 0, 0, 0);
|
|
732
|
+
|
|
728
733
|
this.goToDate(date);
|
|
729
734
|
};
|
|
730
735
|
|
|
731
736
|
Datepicker.prototype.focusLastDayOfWeek = function () {
|
|
732
737
|
const date = new Date(this.currentDate);
|
|
733
|
-
|
|
738
|
+
const lastDayOfWeekIndex = this.config.weekStartDay == "sunday" ? 6 : 0;
|
|
739
|
+
const dayOfWeek = date.getDay();
|
|
740
|
+
const diff =
|
|
741
|
+
dayOfWeek <= lastDayOfWeekIndex
|
|
742
|
+
? lastDayOfWeekIndex - dayOfWeek
|
|
743
|
+
: 7 - dayOfWeek;
|
|
744
|
+
|
|
745
|
+
date.setDate(date.getDate() + diff);
|
|
746
|
+
date.setHours(0, 0, 0, 0);
|
|
747
|
+
|
|
734
748
|
this.goToDate(date);
|
|
735
749
|
};
|
|
736
750
|
|