@ministryofjustice/frontend 3.1.0 → 3.2.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 +2 -2
- package/moj/all.js +74 -67
- package/moj/components/_all.scss +1 -0
- package/moj/components/button-menu/button-menu.spec.js +1 -3
- package/moj/components/date-picker/date-picker.spec.js +14 -29
- package/moj/components/filter-toggle-button/filter-toggle-button.js +1 -1
- package/moj/components/filter-toggle-button/filter-toggle-button.spec.js +304 -0
- package/moj/components/interruption-card/_interruption-card.scss +35 -0
- package/moj/components/interruption-card/macro.njk +3 -0
- package/moj/components/interruption-card/template.njk +35 -0
- package/moj/components/multi-select/README.md +1 -1
- package/moj/components/multi-select/multi-select.js +11 -5
- package/moj/components/pagination/template.njk +2 -2
- package/moj/components/password-reveal/password-reveal.spec.js +14 -5
- package/package.json +1 -1
package/moj/all.js
CHANGED
|
@@ -77,7 +77,8 @@ MOJFrontend.initAll = function (options) {
|
|
|
77
77
|
MOJFrontend.nodeListForEach($multiSelects, function ($multiSelect) {
|
|
78
78
|
new MOJFrontend.MultiSelect({
|
|
79
79
|
container: $multiSelect.querySelector($multiSelect.getAttribute('data-multi-select-checkbox')),
|
|
80
|
-
checkboxes: $multiSelect.querySelectorAll('tbody .govuk-checkboxes__input')
|
|
80
|
+
checkboxes: $multiSelect.querySelectorAll('tbody .govuk-checkboxes__input'),
|
|
81
|
+
id_prefix: $multiSelect.getAttribute('data-multi-select-idprefix'),
|
|
81
82
|
});
|
|
82
83
|
});
|
|
83
84
|
|
|
@@ -1592,7 +1593,7 @@ MOJFrontend.FilterToggleButton.prototype.onMenuButtonClick = function() {
|
|
|
1592
1593
|
MOJFrontend.FilterToggleButton.prototype.toggle = function() {
|
|
1593
1594
|
if(this.menuButton.attr('aria-expanded') == 'false') {
|
|
1594
1595
|
this.showMenu();
|
|
1595
|
-
this.filterContainer.focus();
|
|
1596
|
+
this.filterContainer.get(0).focus();
|
|
1596
1597
|
} else {
|
|
1597
1598
|
this.hideMenu();
|
|
1598
1599
|
}
|
|
@@ -1938,6 +1939,77 @@ if(MOJFrontend.dragAndDropSupported() && MOJFrontend.formDataSupported() && MOJF
|
|
|
1938
1939
|
};
|
|
1939
1940
|
}
|
|
1940
1941
|
|
|
1942
|
+
MOJFrontend.MultiSelect = function(options) {
|
|
1943
|
+
this.container = $(options.container);
|
|
1944
|
+
|
|
1945
|
+
if (this.container.data('moj-multi-select-initialised')) {
|
|
1946
|
+
return
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
this.container.data('moj-multi-select-initialised', true);
|
|
1950
|
+
|
|
1951
|
+
const idPrefix = options.id_prefix;
|
|
1952
|
+
let allId = 'checkboxes-all';
|
|
1953
|
+
if (typeof idPrefix !== 'undefined') {
|
|
1954
|
+
allId = idPrefix + 'checkboxes-all';
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
this.toggle = $(this.getToggleHtml(allId));
|
|
1958
|
+
this.toggleButton = this.toggle.find('input');
|
|
1959
|
+
this.toggleButton.on('click', $.proxy(this, 'onButtonClick'));
|
|
1960
|
+
this.container.append(this.toggle);
|
|
1961
|
+
this.checkboxes = $(options.checkboxes);
|
|
1962
|
+
this.checkboxes.on('click', $.proxy(this, 'onCheckboxClick'));
|
|
1963
|
+
this.checked = options.checked || false;
|
|
1964
|
+
};
|
|
1965
|
+
|
|
1966
|
+
MOJFrontend.MultiSelect.prototype.getToggleHtml = function (allId) {
|
|
1967
|
+
let html = '';
|
|
1968
|
+
html += '<div class="govuk-checkboxes__item govuk-checkboxes--small moj-multi-select__checkbox">';
|
|
1969
|
+
html += ` <input type="checkbox" class="govuk-checkboxes__input" id="${allId}">`;
|
|
1970
|
+
html += ` <label class="govuk-label govuk-checkboxes__label moj-multi-select__toggle-label" for="${allId}">`;
|
|
1971
|
+
html += ' <span class="govuk-visually-hidden">Select all</span>';
|
|
1972
|
+
html += ' </label>';
|
|
1973
|
+
html += '</div>';
|
|
1974
|
+
return html;
|
|
1975
|
+
};
|
|
1976
|
+
|
|
1977
|
+
MOJFrontend.MultiSelect.prototype.onButtonClick = function(e) {
|
|
1978
|
+
if(this.checked) {
|
|
1979
|
+
this.uncheckAll();
|
|
1980
|
+
this.toggleButton[0].checked = false;
|
|
1981
|
+
} else {
|
|
1982
|
+
this.checkAll();
|
|
1983
|
+
this.toggleButton[0].checked = true;
|
|
1984
|
+
}
|
|
1985
|
+
};
|
|
1986
|
+
|
|
1987
|
+
MOJFrontend.MultiSelect.prototype.checkAll = function() {
|
|
1988
|
+
this.checkboxes.each($.proxy(function(index, el) {
|
|
1989
|
+
el.checked = true;
|
|
1990
|
+
}, this));
|
|
1991
|
+
this.checked = true;
|
|
1992
|
+
};
|
|
1993
|
+
|
|
1994
|
+
MOJFrontend.MultiSelect.prototype.uncheckAll = function() {
|
|
1995
|
+
this.checkboxes.each($.proxy(function(index, el) {
|
|
1996
|
+
el.checked = false;
|
|
1997
|
+
}, this));
|
|
1998
|
+
this.checked = false;
|
|
1999
|
+
};
|
|
2000
|
+
|
|
2001
|
+
MOJFrontend.MultiSelect.prototype.onCheckboxClick = function(e) {
|
|
2002
|
+
if(!e.target.checked) {
|
|
2003
|
+
this.toggleButton[0].checked = false;
|
|
2004
|
+
this.checked = false;
|
|
2005
|
+
} else {
|
|
2006
|
+
if(this.checkboxes.filter(':checked').length === this.checkboxes.length) {
|
|
2007
|
+
this.toggleButton[0].checked = true;
|
|
2008
|
+
this.checked = true;
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
};
|
|
2012
|
+
|
|
1941
2013
|
MOJFrontend.PasswordReveal = function(element) {
|
|
1942
2014
|
this.el = element;
|
|
1943
2015
|
var $el = $(this.el)
|
|
@@ -2111,71 +2183,6 @@ if('contentEditable' in document.documentElement) {
|
|
|
2111
2183
|
|
|
2112
2184
|
}
|
|
2113
2185
|
|
|
2114
|
-
MOJFrontend.MultiSelect = function(options) {
|
|
2115
|
-
this.container = $(options.container);
|
|
2116
|
-
|
|
2117
|
-
if (this.container.data('moj-multi-select-initialised')) {
|
|
2118
|
-
return
|
|
2119
|
-
}
|
|
2120
|
-
|
|
2121
|
-
this.container.data('moj-multi-select-initialised', true);
|
|
2122
|
-
|
|
2123
|
-
this.toggle = $(this.getToggleHtml());
|
|
2124
|
-
this.toggleButton = this.toggle.find('input');
|
|
2125
|
-
this.toggleButton.on('click', $.proxy(this, 'onButtonClick'));
|
|
2126
|
-
this.container.append(this.toggle);
|
|
2127
|
-
this.checkboxes = $(options.checkboxes);
|
|
2128
|
-
this.checkboxes.on('click', $.proxy(this, 'onCheckboxClick'));
|
|
2129
|
-
this.checked = options.checked || false;
|
|
2130
|
-
};
|
|
2131
|
-
|
|
2132
|
-
MOJFrontend.MultiSelect.prototype.getToggleHtml = function() {
|
|
2133
|
-
var html = '';
|
|
2134
|
-
html += '<div class="govuk-checkboxes__item govuk-checkboxes--small moj-multi-select__checkbox">';
|
|
2135
|
-
html += ' <input type="checkbox" class="govuk-checkboxes__input" id="checkboxes-all">';
|
|
2136
|
-
html += ' <label class="govuk-label govuk-checkboxes__label moj-multi-select__toggle-label" for="checkboxes-all">';
|
|
2137
|
-
html += ' <span class="govuk-visually-hidden">Select all</span>';
|
|
2138
|
-
html += ' </label>';
|
|
2139
|
-
html += '</div>';
|
|
2140
|
-
return html;
|
|
2141
|
-
};
|
|
2142
|
-
|
|
2143
|
-
MOJFrontend.MultiSelect.prototype.onButtonClick = function(e) {
|
|
2144
|
-
if(this.checked) {
|
|
2145
|
-
this.uncheckAll();
|
|
2146
|
-
this.toggleButton[0].checked = false;
|
|
2147
|
-
} else {
|
|
2148
|
-
this.checkAll();
|
|
2149
|
-
this.toggleButton[0].checked = true;
|
|
2150
|
-
}
|
|
2151
|
-
};
|
|
2152
|
-
|
|
2153
|
-
MOJFrontend.MultiSelect.prototype.checkAll = function() {
|
|
2154
|
-
this.checkboxes.each($.proxy(function(index, el) {
|
|
2155
|
-
el.checked = true;
|
|
2156
|
-
}, this));
|
|
2157
|
-
this.checked = true;
|
|
2158
|
-
};
|
|
2159
|
-
|
|
2160
|
-
MOJFrontend.MultiSelect.prototype.uncheckAll = function() {
|
|
2161
|
-
this.checkboxes.each($.proxy(function(index, el) {
|
|
2162
|
-
el.checked = false;
|
|
2163
|
-
}, this));
|
|
2164
|
-
this.checked = false;
|
|
2165
|
-
};
|
|
2166
|
-
|
|
2167
|
-
MOJFrontend.MultiSelect.prototype.onCheckboxClick = function(e) {
|
|
2168
|
-
if(!e.target.checked) {
|
|
2169
|
-
this.toggleButton[0].checked = false;
|
|
2170
|
-
this.checked = false;
|
|
2171
|
-
} else {
|
|
2172
|
-
if(this.checkboxes.filter(':checked').length === this.checkboxes.length) {
|
|
2173
|
-
this.toggleButton[0].checked = true;
|
|
2174
|
-
this.checked = true;
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
};
|
|
2178
|
-
|
|
2179
2186
|
MOJFrontend.SearchToggle = function (options) {
|
|
2180
2187
|
this.options = options;
|
|
2181
2188
|
this.container = $(this.options.search.container);
|
package/moj/components/_all.scss
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
@import "filter/filter";
|
|
10
10
|
@import "header/header";
|
|
11
11
|
@import "identity-bar/identity-bar";
|
|
12
|
+
@import "interruption-card/interruption-card";
|
|
12
13
|
@import "messages/messages";
|
|
13
14
|
@import "multi-file-upload/multi-file-upload";
|
|
14
15
|
@import "multi-select/multi-select";
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
const { queryByRole, screen } = require("@testing-library/dom");
|
|
2
2
|
const { userEvent } = require("@testing-library/user-event");
|
|
3
|
-
const { configureAxe
|
|
4
|
-
expect.extend(toHaveNoViolations);
|
|
3
|
+
const { configureAxe } = require("jest-axe");
|
|
5
4
|
|
|
6
|
-
require("../../../../jest.setup.js");
|
|
7
5
|
require("./button-menu.js");
|
|
8
6
|
|
|
9
7
|
const user = userEvent.setup();
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @jest-environment jsdom
|
|
3
|
-
*/
|
|
4
1
|
const {
|
|
5
2
|
getAllByRole,
|
|
6
3
|
getByText,
|
|
@@ -10,11 +7,9 @@ const {
|
|
|
10
7
|
screen,
|
|
11
8
|
} = require("@testing-library/dom");
|
|
12
9
|
const { userEvent } = require("@testing-library/user-event");
|
|
10
|
+
const { configureAxe } = require("jest-axe");
|
|
13
11
|
const dayjs = require("dayjs");
|
|
14
|
-
const { configureAxe, toHaveNoViolations } = require("jest-axe");
|
|
15
|
-
expect.extend(toHaveNoViolations);
|
|
16
12
|
|
|
17
|
-
require("../../../../jest.setup.js");
|
|
18
13
|
require("./date-picker.js");
|
|
19
14
|
|
|
20
15
|
const user = userEvent.setup();
|
|
@@ -199,18 +194,13 @@ describe("Date picker with defaults", () => {
|
|
|
199
194
|
});
|
|
200
195
|
|
|
201
196
|
test("can navigate back in time", async () => {
|
|
202
|
-
const today =
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
const currentMonth = today.getMonth();
|
|
206
|
-
const previousMonthName = new Date(
|
|
207
|
-
today.setMonth(currentMonth - 1),
|
|
208
|
-
).toLocaleString("default", { month: "long" });
|
|
209
|
-
const previousYear = currentYear - 1;
|
|
197
|
+
const today = dayjs();
|
|
198
|
+
const previousMonth = dayjs().subtract(1, 'month')
|
|
199
|
+
const previousYear = previousMonth.subtract(1, 'year')
|
|
210
200
|
|
|
211
|
-
const currentTitle = `${
|
|
212
|
-
const previousMonthTitle = `${
|
|
213
|
-
const previousYearTitle = `${
|
|
201
|
+
const currentTitle = `${today.format('MMMM YYYY')}`;
|
|
202
|
+
const previousMonthTitle = `${previousMonth.format('MMMM YYYY')}`;
|
|
203
|
+
const previousYearTitle = `${previousYear.format('MMMM YYYY')}`;
|
|
214
204
|
|
|
215
205
|
await user.click(calendarButton);
|
|
216
206
|
let prevMonthButton = getByText(dialog, "Previous month");
|
|
@@ -224,18 +214,13 @@ describe("Date picker with defaults", () => {
|
|
|
224
214
|
});
|
|
225
215
|
|
|
226
216
|
test("can navigate forward in time", async () => {
|
|
227
|
-
const today =
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const nextYear = currentYear + 1;
|
|
235
|
-
|
|
236
|
-
const currentTitle = `${currentMonthName} ${currentYear}`;
|
|
237
|
-
const nextMonthTitle = `${nextMonthName} ${currentYear}`;
|
|
238
|
-
const nextYearTitle = `${nextMonthName} ${nextYear}`;
|
|
217
|
+
const today = dayjs();
|
|
218
|
+
const nextMonth = dayjs().add(1, 'month')
|
|
219
|
+
const nextYear = nextMonth.add(1, 'year')
|
|
220
|
+
|
|
221
|
+
const currentTitle = `${today.format('MMMM YYYY')}`;
|
|
222
|
+
const nextMonthTitle = `${nextMonth.format('MMMM YYYY')}`;
|
|
223
|
+
const nextYearTitle = `${nextYear.format('MMMM YYYY')}`;
|
|
239
224
|
|
|
240
225
|
await user.click(calendarButton);
|
|
241
226
|
let nextMonthButton = getByText(dialog, "Next month");
|
|
@@ -80,7 +80,7 @@ MOJFrontend.FilterToggleButton.prototype.onMenuButtonClick = function() {
|
|
|
80
80
|
MOJFrontend.FilterToggleButton.prototype.toggle = function() {
|
|
81
81
|
if(this.menuButton.attr('aria-expanded') == 'false') {
|
|
82
82
|
this.showMenu();
|
|
83
|
-
this.filterContainer.focus();
|
|
83
|
+
this.filterContainer.get(0).focus();
|
|
84
84
|
} else {
|
|
85
85
|
this.hideMenu();
|
|
86
86
|
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
const {
|
|
2
|
+
queryByRole,
|
|
3
|
+
} = require("@testing-library/dom");
|
|
4
|
+
const { userEvent } = require("@testing-library/user-event");
|
|
5
|
+
const { configureAxe } = require("jest-axe");
|
|
6
|
+
const merge = require("lodash.merge");
|
|
7
|
+
const { setMedia } = require("mock-match-media");
|
|
8
|
+
|
|
9
|
+
require("./filter-toggle-button.js");
|
|
10
|
+
|
|
11
|
+
const user = userEvent.setup();
|
|
12
|
+
const axe = configureAxe({
|
|
13
|
+
rules: {
|
|
14
|
+
// disable landmark rules when testing isolated components.
|
|
15
|
+
region: { enabled: false },
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const createTemplate = () => {
|
|
20
|
+
html = `
|
|
21
|
+
<div class="moj-filter">
|
|
22
|
+
<div class="moj-filter__header">
|
|
23
|
+
<div class="moj-filter__header-title">
|
|
24
|
+
<h2 class="govuk-heading-m">Filter</h2>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="moj-filter__header-action"></div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="moj-action-bar">
|
|
30
|
+
<div class="moj-action-bar__filter"></div>
|
|
31
|
+
</div>`;
|
|
32
|
+
document.body.insertAdjacentHTML("afterbegin", html);
|
|
33
|
+
|
|
34
|
+
const buttonContainer = document.querySelector(".moj-action-bar__filter");
|
|
35
|
+
const closeButtonContainer = document.querySelector(
|
|
36
|
+
".moj-filter__header-action",
|
|
37
|
+
);
|
|
38
|
+
const filterContainer = document.querySelector(".moj-filter");
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
buttonContainer: buttonContainer,
|
|
42
|
+
closeButtonContainer: closeButtonContainer,
|
|
43
|
+
filterContainer: filterContainer,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
let baseConfig;
|
|
48
|
+
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
baseConfig = {
|
|
51
|
+
bigModeMediaQuery: "(min-width: 600px)",
|
|
52
|
+
startHidden: true,
|
|
53
|
+
toggleButton: {
|
|
54
|
+
container: document.querySelector(".moj-action-bar__filter"),
|
|
55
|
+
showText: "Show filter",
|
|
56
|
+
hideText: "Hide filter",
|
|
57
|
+
classes: "govuk-button--secondary",
|
|
58
|
+
},
|
|
59
|
+
closeButton: {
|
|
60
|
+
container: document.querySelector(".moj-filter__header-action"),
|
|
61
|
+
text: "Close",
|
|
62
|
+
},
|
|
63
|
+
filter: {
|
|
64
|
+
container: document.querySelector(".moj-filter"),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("Filter toggle in big mode", () => {
|
|
70
|
+
let defaultConfig, buttonContainer, closeButtonContainer, filterContainer;
|
|
71
|
+
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
setMedia({
|
|
74
|
+
width: "800px",
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
({ buttonContainer, closeButtonContainer, filterContainer } = createTemplate());
|
|
78
|
+
|
|
79
|
+
defaultConfig = merge(baseConfig, {
|
|
80
|
+
toggleButton: {
|
|
81
|
+
container: document.querySelector(".moj-action-bar__filter"),
|
|
82
|
+
},
|
|
83
|
+
closeButton: {
|
|
84
|
+
container: document.querySelector(".moj-filter__header-action"),
|
|
85
|
+
},
|
|
86
|
+
filter: { container: document.querySelector(".moj-filter") },
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
afterEach(() => {
|
|
91
|
+
document.body.innerHTML = "";
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("creates toggle button", () => {
|
|
95
|
+
new MOJFrontend.FilterToggleButton(defaultConfig);
|
|
96
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
97
|
+
|
|
98
|
+
expect(toggleButton).not.toBeNull();
|
|
99
|
+
expect(toggleButton.innerHTML).toBe("Show filter");
|
|
100
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "false");
|
|
101
|
+
expect(toggleButton).toHaveClass("govuk-button--secondary");
|
|
102
|
+
|
|
103
|
+
expect(filterContainer).toHaveAttribute("tabindex", "-1");
|
|
104
|
+
expect(filterContainer).toHaveClass("moj-js-hidden");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("toggle button reveals filters", async () => {
|
|
108
|
+
new MOJFrontend.FilterToggleButton(defaultConfig);
|
|
109
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
110
|
+
|
|
111
|
+
expect(filterContainer).toHaveAttribute("tabindex", "-1");
|
|
112
|
+
expect(filterContainer).toHaveClass("moj-js-hidden");
|
|
113
|
+
|
|
114
|
+
await user.click(toggleButton);
|
|
115
|
+
|
|
116
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "true");
|
|
117
|
+
expect(toggleButton.innerHTML).toBe("Hide filter");
|
|
118
|
+
expect(filterContainer).not.toHaveClass("moj-js-hidden");
|
|
119
|
+
expect(filterContainer).toHaveFocus();
|
|
120
|
+
|
|
121
|
+
await user.click(toggleButton);
|
|
122
|
+
|
|
123
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "false");
|
|
124
|
+
expect(toggleButton.innerHTML).toBe("Show filter");
|
|
125
|
+
expect(filterContainer).toHaveClass("moj-js-hidden");
|
|
126
|
+
|
|
127
|
+
expect(toggleButton).toHaveFocus();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("start visible", () => {
|
|
131
|
+
const config = merge(defaultConfig, { startHidden: false });
|
|
132
|
+
new MOJFrontend.FilterToggleButton(config);
|
|
133
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
134
|
+
|
|
135
|
+
expect(toggleButton.innerHTML).toBe("Hide filter");
|
|
136
|
+
expect(filterContainer).not.toHaveClass("moj-js-hidden");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("custom button text", async () => {
|
|
140
|
+
const config = merge(defaultConfig, {
|
|
141
|
+
toggleButton: { showText: "Custom label", hideText: "Hide me" },
|
|
142
|
+
});
|
|
143
|
+
new MOJFrontend.FilterToggleButton(config);
|
|
144
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
145
|
+
|
|
146
|
+
expect(toggleButton.innerHTML).toBe("Custom label");
|
|
147
|
+
await user.click(toggleButton);
|
|
148
|
+
expect(toggleButton.innerHTML).toBe("Hide me");
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("custom toggle button classes", () => {
|
|
152
|
+
const config = merge(defaultConfig, {
|
|
153
|
+
toggleButton: { classes: "classname-1 classname-2" },
|
|
154
|
+
});
|
|
155
|
+
new MOJFrontend.FilterToggleButton(config);
|
|
156
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
157
|
+
|
|
158
|
+
expect(toggleButton).toHaveClass("classname-1 classname-2");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("accessibility", () => {
|
|
162
|
+
test("component has no wcag violations", async () => {
|
|
163
|
+
new MOJFrontend.FilterToggleButton(defaultConfig);
|
|
164
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
165
|
+
expect(await axe(document.body)).toHaveNoViolations();
|
|
166
|
+
await user.click(toggleButton);
|
|
167
|
+
expect(await axe(document.body)).toHaveNoViolations();
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe("Filter toggle in small mode", () => {
|
|
173
|
+
let defaultConfig, buttonContainer, closeButtonContainer, filterContainer;
|
|
174
|
+
|
|
175
|
+
beforeEach(() => {
|
|
176
|
+
setMedia({
|
|
177
|
+
width: "500px",
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
({ buttonContainer, closeButtonContainer, filterContainer } = createTemplate()),
|
|
181
|
+
|
|
182
|
+
defaultConfig = merge(baseConfig, {
|
|
183
|
+
toggleButton: {
|
|
184
|
+
container: document.querySelector(".moj-action-bar__filter"),
|
|
185
|
+
},
|
|
186
|
+
closeButton: {
|
|
187
|
+
container: document.querySelector(".moj-filter__header-action"),
|
|
188
|
+
},
|
|
189
|
+
filter: { container: document.querySelector(".moj-filter") },
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
afterEach(() => {
|
|
194
|
+
document.body.innerHTML = "";
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("creates toggle button", () => {
|
|
198
|
+
new MOJFrontend.FilterToggleButton(defaultConfig);
|
|
199
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
200
|
+
|
|
201
|
+
expect(toggleButton).not.toBeNull();
|
|
202
|
+
expect(toggleButton.innerHTML).toBe("Show filter");
|
|
203
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "false");
|
|
204
|
+
expect(toggleButton).toHaveClass("govuk-button--secondary");
|
|
205
|
+
|
|
206
|
+
expect(filterContainer).toHaveAttribute("tabindex", "-1");
|
|
207
|
+
expect(filterContainer).toHaveClass("moj-js-hidden");
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("toggle button reveals filters", async () => {
|
|
211
|
+
new MOJFrontend.FilterToggleButton(defaultConfig);
|
|
212
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
213
|
+
|
|
214
|
+
expect(filterContainer).toHaveAttribute("tabindex", "-1");
|
|
215
|
+
expect(filterContainer).toHaveClass("moj-js-hidden");
|
|
216
|
+
|
|
217
|
+
await user.click(toggleButton);
|
|
218
|
+
|
|
219
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "true");
|
|
220
|
+
expect(toggleButton.innerHTML).toBe("Hide filter");
|
|
221
|
+
expect(filterContainer).not.toHaveClass("moj-js-hidden");
|
|
222
|
+
expect(filterContainer).toHaveFocus();
|
|
223
|
+
|
|
224
|
+
await user.click(toggleButton);
|
|
225
|
+
|
|
226
|
+
expect(toggleButton).toHaveAttribute("aria-expanded", "false");
|
|
227
|
+
expect(toggleButton.innerHTML).toBe("Show filter");
|
|
228
|
+
expect(filterContainer).toHaveClass("moj-js-hidden");
|
|
229
|
+
|
|
230
|
+
expect(toggleButton).toHaveFocus();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("start visible is ignored", () => {
|
|
234
|
+
const config = merge(defaultConfig, { startHidden: false });
|
|
235
|
+
new MOJFrontend.FilterToggleButton(config);
|
|
236
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
237
|
+
|
|
238
|
+
expect(toggleButton.innerHTML).toBe("Show filter");
|
|
239
|
+
expect(filterContainer).toHaveClass("moj-js-hidden");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test("adds a close button", async () => {
|
|
243
|
+
const config = merge(defaultConfig, { startHidden: false });
|
|
244
|
+
new MOJFrontend.FilterToggleButton(config);
|
|
245
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
246
|
+
|
|
247
|
+
await user.click(toggleButton);
|
|
248
|
+
|
|
249
|
+
const closeButton = queryByRole(closeButtonContainer, "button");
|
|
250
|
+
|
|
251
|
+
expect(closeButton).not.toBeNull();
|
|
252
|
+
expect(closeButton.innerHTML).toBe("Close");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("hides on resize from big to small", async () => {
|
|
256
|
+
setMedia({
|
|
257
|
+
width: "800px",
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const config = merge(defaultConfig, { startHidden: false });
|
|
261
|
+
new MOJFrontend.FilterToggleButton(config);
|
|
262
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
263
|
+
|
|
264
|
+
expect(toggleButton.innerHTML).toBe("Hide filter");
|
|
265
|
+
expect(filterContainer).not.toHaveClass("moj-js-hidden");
|
|
266
|
+
|
|
267
|
+
setMedia({
|
|
268
|
+
width: "500px",
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
expect(toggleButton.innerHTML).toBe("Show filter");
|
|
272
|
+
expect(filterContainer).toHaveClass("moj-js-hidden");
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("shows on resize from small to big", async () => {
|
|
276
|
+
setMedia({
|
|
277
|
+
width: "500px",
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const config = merge(defaultConfig);
|
|
281
|
+
new MOJFrontend.FilterToggleButton(config);
|
|
282
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
283
|
+
|
|
284
|
+
expect(toggleButton.innerHTML).toBe("Show filter");
|
|
285
|
+
expect(filterContainer).toHaveClass("moj-js-hidden");
|
|
286
|
+
|
|
287
|
+
setMedia({
|
|
288
|
+
width: "800px",
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
expect(toggleButton.innerHTML).toBe("Hide filter");
|
|
292
|
+
expect(filterContainer).not.toHaveClass("moj-js-hidden");
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe("accessibility", () => {
|
|
296
|
+
test("component has no wcag violations", async () => {
|
|
297
|
+
new MOJFrontend.FilterToggleButton(defaultConfig);
|
|
298
|
+
const toggleButton = queryByRole(buttonContainer, "button");
|
|
299
|
+
expect(await axe(document.body)).toHaveNoViolations();
|
|
300
|
+
await user.click(toggleButton);
|
|
301
|
+
expect(await axe(document.body)).toHaveNoViolations();
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.moj-interruption-card {
|
|
2
|
+
background-color: govuk-colour("blue");
|
|
3
|
+
margin-bottom: govuk-spacing(3);
|
|
4
|
+
@include govuk-responsive-padding(7, $adjustment: $govuk-border-width*-1);
|
|
5
|
+
border: $govuk-border-width solid transparent;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.moj-interruption-card__content {
|
|
9
|
+
max-width: 960px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.moj-interruption-card__heading,
|
|
13
|
+
.moj-interruption-card__body {
|
|
14
|
+
color: govuk-colour("white");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.moj-interruption-card__body:last-child {
|
|
18
|
+
margin-bottom: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.moj-interruption-card__actions:last-child {
|
|
22
|
+
margin-bottom: 0;
|
|
23
|
+
|
|
24
|
+
.govuk-button:last-child,
|
|
25
|
+
.govuk-link:last-child {
|
|
26
|
+
margin-bottom: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@include govuk-media-query($from: tablet) {
|
|
30
|
+
.govuk-button,
|
|
31
|
+
.govuk-link {
|
|
32
|
+
margin-bottom: 0;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{% from "govuk/components/button/macro.njk" import govukButton %}
|
|
2
|
+
<div class="moj-interruption-card" data-test="{{ params.id }}">
|
|
3
|
+
<div class="moj-interruption-card__content">
|
|
4
|
+
<h1 class="govuk-heading-l moj-interruption-card__heading">{{- params.heading -}}</h1>
|
|
5
|
+
<div class="govuk-body-l moj-interruption-card__body">{{ caller() }}</div>
|
|
6
|
+
<div class="govuk-button-group moj-interruption-card__actions">
|
|
7
|
+
{% if params.primaryAction.style == "link" %}
|
|
8
|
+
<a class="govuk-link govuk-link--inverse"
|
|
9
|
+
href="{{ params.primaryAction.href }}">{{- params.primaryAction.text -}}</a>
|
|
10
|
+
{% else %}
|
|
11
|
+
{{ govukButton({
|
|
12
|
+
text: params.primaryAction.text,
|
|
13
|
+
classes: "govuk-button--inverse",
|
|
14
|
+
href: params.primaryAction.href,
|
|
15
|
+
preventDoubleClick: true,
|
|
16
|
+
attributes: params.primaryAction.attributes
|
|
17
|
+
}) }}
|
|
18
|
+
{% endif %}
|
|
19
|
+
{% if params.secondaryAction %}
|
|
20
|
+
{% if params.secondaryAction.style == "button" %}
|
|
21
|
+
{{ govukButton({
|
|
22
|
+
text: params.secondaryAction.text,
|
|
23
|
+
classes: "govuk-button--inverse",
|
|
24
|
+
href: params.secondaryAction.href,
|
|
25
|
+
preventDoubleClick: true,
|
|
26
|
+
attributes: params.secondaryAction.attributes
|
|
27
|
+
}) }}
|
|
28
|
+
{% else %}
|
|
29
|
+
<a class="govuk-link govuk-link--inverse"
|
|
30
|
+
href="{{ params.secondaryAction.href }}">{{- params.secondaryAction.text -}}</a>
|
|
31
|
+
{% endif %}
|
|
32
|
+
{% endif %}
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
@@ -7,7 +7,13 @@ MOJFrontend.MultiSelect = function(options) {
|
|
|
7
7
|
|
|
8
8
|
this.container.data('moj-multi-select-initialised', true);
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
const idPrefix = options.id_prefix;
|
|
11
|
+
let allId = 'checkboxes-all';
|
|
12
|
+
if (typeof idPrefix !== 'undefined') {
|
|
13
|
+
allId = idPrefix + 'checkboxes-all';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
this.toggle = $(this.getToggleHtml(allId));
|
|
11
17
|
this.toggleButton = this.toggle.find('input');
|
|
12
18
|
this.toggleButton.on('click', $.proxy(this, 'onButtonClick'));
|
|
13
19
|
this.container.append(this.toggle);
|
|
@@ -16,11 +22,11 @@ MOJFrontend.MultiSelect = function(options) {
|
|
|
16
22
|
this.checked = options.checked || false;
|
|
17
23
|
};
|
|
18
24
|
|
|
19
|
-
MOJFrontend.MultiSelect.prototype.getToggleHtml = function() {
|
|
20
|
-
|
|
25
|
+
MOJFrontend.MultiSelect.prototype.getToggleHtml = function (allId) {
|
|
26
|
+
let html = '';
|
|
21
27
|
html += '<div class="govuk-checkboxes__item govuk-checkboxes--small moj-multi-select__checkbox">';
|
|
22
|
-
html +=
|
|
23
|
-
html +=
|
|
28
|
+
html += ` <input type="checkbox" class="govuk-checkboxes__input" id="${allId}">`;
|
|
29
|
+
html += ` <label class="govuk-label govuk-checkboxes__label moj-multi-select__toggle-label" for="${allId}">`;
|
|
24
30
|
html += ' <span class="govuk-visually-hidden">Select all</span>';
|
|
25
31
|
html += ' </label>';
|
|
26
32
|
html += '</div>';
|