@ministryofjustice/frontend 3.2.2 → 3.3.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.js CHANGED
@@ -123,14 +123,7 @@ MOJFrontend.initAll = function (options) {
123
123
  });
124
124
  });
125
125
 
126
- var $sortableTables = scope.querySelectorAll('[data-module="moj-sortable-table"]');
127
- MOJFrontend.nodeListForEach($sortableTables, function ($table) {
128
- new MOJFrontend.SortableTable({
129
- table: $table
130
- });
131
- });
132
-
133
- const $datepickers = document.querySelectorAll('[data-module="moj-date-picker"]')
126
+ const $datepickers = scope.querySelectorAll('[data-module="moj-date-picker"]')
134
127
  MOJFrontend.nodeListForEach($datepickers, function ($datepicker) {
135
128
  new MOJFrontend.DatePicker($datepicker, {}).init();
136
129
  })
@@ -142,7 +135,7 @@ MOJFrontend.initAll = function (options) {
142
135
 
143
136
  }
144
137
 
145
- MOJFrontend.version = '3.2.2'
138
+ MOJFrontend.version = '3.3.1'
146
139
 
147
140
  MOJFrontend.AddAnother = function(container) {
148
141
  this.container = $(container);
@@ -2214,7 +2207,7 @@ MOJFrontend.SearchToggle = function (options) {
2214
2207
  MOJFrontend.SearchToggle.prototype.showMenu = function () {
2215
2208
  this.toggleButton.attr("aria-expanded", "true");
2216
2209
  this.container.removeClass("moj-js-hidden");
2217
- this.container.find("input").first().focus();
2210
+ this.container.find("input").first().get(0).focus();
2218
2211
  };
2219
2212
 
2220
2213
  MOJFrontend.SearchToggle.prototype.hideMenu = function () {
@@ -195,12 +195,12 @@ describe("Date picker with defaults", () => {
195
195
 
196
196
  test("can navigate back in time", async () => {
197
197
  const today = dayjs();
198
- const previousMonth = dayjs().subtract(1, 'month')
199
- const previousYear = previousMonth.subtract(1, 'year')
198
+ const previousMonth = dayjs().subtract(1, "month");
199
+ const previousYear = previousMonth.subtract(1, "year");
200
200
 
201
- const currentTitle = `${today.format('MMMM YYYY')}`;
202
- const previousMonthTitle = `${previousMonth.format('MMMM YYYY')}`;
203
- const previousYearTitle = `${previousYear.format('MMMM YYYY')}`;
201
+ const currentTitle = `${today.format("MMMM YYYY")}`;
202
+ const previousMonthTitle = `${previousMonth.format("MMMM YYYY")}`;
203
+ const previousYearTitle = `${previousYear.format("MMMM YYYY")}`;
204
204
 
205
205
  await user.click(calendarButton);
206
206
  let prevMonthButton = getByText(dialog, "Previous month");
@@ -215,12 +215,12 @@ describe("Date picker with defaults", () => {
215
215
 
216
216
  test("can navigate forward in time", async () => {
217
217
  const today = dayjs();
218
- const nextMonth = dayjs().add(1, 'month')
219
- const nextYear = nextMonth.add(1, 'year')
218
+ const nextMonth = dayjs().add(1, "month");
219
+ const nextYear = nextMonth.add(1, "year");
220
220
 
221
- const currentTitle = `${today.format('MMMM YYYY')}`;
222
- const nextMonthTitle = `${nextMonth.format('MMMM YYYY')}`;
223
- const nextYearTitle = `${nextYear.format('MMMM YYYY')}`;
221
+ const currentTitle = `${today.format("MMMM YYYY")}`;
222
+ const nextMonthTitle = `${nextMonth.format("MMMM YYYY")}`;
223
+ const nextYearTitle = `${nextYear.format("MMMM YYYY")}`;
224
224
 
225
225
  await user.click(calendarButton);
226
226
  let nextMonthButton = getByText(dialog, "Next month");
@@ -728,7 +728,7 @@ describe("button menu JS API", () => {
728
728
  }
729
729
  datesToExclude = datesToExclude.map((date) => date.startOf("day"));
730
730
  config = {
731
- excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
731
+ excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length - 1].format("D/M/YYYY")}`,
732
732
  };
733
733
  const datePicker = new MOJFrontend.DatePicker(component, config);
734
734
  datePicker.init();
@@ -775,13 +775,13 @@ describe("button menu JS API", () => {
775
775
  const config = { leadingZeros: false };
776
776
  new MOJFrontend.DatePicker(component, config).init();
777
777
  calendarButton = screen.getByRole("button", { name: "Choose date" });
778
- const dateToSelect = screen.queryByText("9")?.closest("button");
779
- const selectedDate = dayjs().date(9);
778
+ const dateToSelect = dayjs().date(9);
779
+ const dateButton = screen.getByTestId(dateToSelect.format("D/M/YYYY"));
780
780
 
781
781
  await user.click(calendarButton);
782
- await user.click(dateToSelect);
782
+ await user.click(dateButton);
783
783
 
784
- expect(input).toHaveValue(selectedDate.format("D/M/YYYY"));
784
+ expect(input).toHaveValue(dateToSelect.format("D/M/YYYY"));
785
785
  });
786
786
 
787
787
  test("with leadingZeros true", async () => {
@@ -790,13 +790,13 @@ describe("button menu JS API", () => {
790
790
  const config = { leadingZeros: true };
791
791
  new MOJFrontend.DatePicker(component, config).init();
792
792
  calendarButton = screen.getByRole("button", { name: "Choose date" });
793
- const dateToSelect = screen.queryByText("9")?.closest("button");
794
- const selectedDate = dayjs().date(9);
793
+ const dateToSelect = dayjs().date(9);
794
+ const dateButton = screen.getByTestId(dateToSelect.format("DD/MM/YYYY"));
795
795
 
796
796
  await user.click(calendarButton);
797
- await user.click(dateToSelect);
797
+ await user.click(dateButton);
798
798
 
799
- expect(input).toHaveValue(selectedDate.format("DD/MM/YYYY"));
799
+ expect(input).toHaveValue(dateToSelect.format("DD/MM/YYYY"));
800
800
  });
801
801
 
802
802
  test.skip.failing("minDate", async () => {
@@ -880,7 +880,7 @@ describe("button menu JS API", () => {
880
880
  let daysToExclude = datesToExclude.map((date) => date.date());
881
881
  const lastDayinMonth = dayjs().endOf("month").date();
882
882
  config = {
883
- excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
883
+ excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length - 1].format("D/M/YYYY")}`,
884
884
  };
885
885
 
886
886
  datePicker = new MOJFrontend.DatePicker(component, config).init();
@@ -962,13 +962,13 @@ describe("Datepicker data-attributes API", () => {
962
962
 
963
963
  input = screen.getByLabelText("Date");
964
964
  calendarButton = screen.getByRole("button", { name: "Choose date" });
965
- const dateToSelect = screen.queryByText("9")?.closest("button");
966
- const selectedDate = dayjs().date(9);
965
+ const dateToSelect = dayjs().date(9);
966
+ const dateButton = screen.getByTestId(dateToSelect.format("D/M/YYYY"));
967
967
 
968
968
  await user.click(calendarButton);
969
- await user.click(dateToSelect);
969
+ await user.click(dateButton);
970
970
 
971
- expect(input).toHaveValue(selectedDate.format("D/M/YYYY"));
971
+ expect(input).toHaveValue(dateToSelect.format("D/M/YYYY"));
972
972
  });
973
973
 
974
974
  test("with leadingZeros true", async () => {
@@ -977,13 +977,13 @@ describe("Datepicker data-attributes API", () => {
977
977
 
978
978
  input = screen.getByLabelText("Date");
979
979
  calendarButton = screen.getByRole("button", { name: "Choose date" });
980
- const dateToSelect = screen.queryByText("9")?.closest("button");
981
- const selectedDate = dayjs().date(9);
980
+ const dateToSelect = dayjs().date(9);
981
+ const dateButton = screen.getByTestId(dateToSelect.format("DD/MM/YYYY"));
982
982
 
983
983
  await user.click(calendarButton);
984
- await user.click(dateToSelect);
984
+ await user.click(dateButton);
985
985
 
986
- expect(input).toHaveValue(selectedDate.format("DD/MM/YYYY"));
986
+ expect(input).toHaveValue(dateToSelect.format("DD/MM/YYYY"));
987
987
  });
988
988
 
989
989
  test.skip.failing("minDate", async () => {
@@ -1072,7 +1072,7 @@ describe("Datepicker data-attributes API", () => {
1072
1072
  let daysToExclude = datesToExclude.map((date) => date.date());
1073
1073
  const lastDayinMonth = dayjs().endOf("month").date();
1074
1074
  component = createComponent({
1075
- excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
1075
+ excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length - 1].format("D/M/YYYY")}`,
1076
1076
  });
1077
1077
  datePicker = new MOJFrontend.DatePicker(component).init();
1078
1078
  calendarButton = screen.getByRole("button", { name: "Choose date" });
@@ -14,6 +14,13 @@
14
14
  color: govuk-colour("white");
15
15
  }
16
16
 
17
+ // If $govuk-global-styles is true, we need to override the color and size on elements
18
+ // within the body class directly
19
+ .moj-interruption-card__body > * {
20
+ @include govuk-font-size($size: 24);
21
+ color: govuk-colour("white");
22
+ }
23
+
17
24
  .moj-interruption-card__body:last-child {
18
25
  margin-bottom: 0;
19
26
  }
@@ -0,0 +1,135 @@
1
+ const {
2
+ queryByRole,
3
+ queryAllByRole,
4
+ } = require("@testing-library/dom");
5
+ const { userEvent } = require("@testing-library/user-event");
6
+ const { configureAxe } = require("jest-axe");
7
+
8
+ require("./multi-select.js");
9
+
10
+ const user = userEvent.setup();
11
+ const axe = configureAxe({
12
+ rules: {
13
+ // disable landmark rules when testing isolated components.
14
+ region: { enabled: false },
15
+ },
16
+ });
17
+
18
+ const createComponent = (id = "multi-select", idprefix = false) => {
19
+ html = `
20
+ <table id="${id}" class="govuk-table" data-module="moj-multi-select" data-multi-select-checkbox="#${id}-select-all" ${idprefix ? 'data-multi-select-idprefix="' + idprefix + '-"' : ""}>
21
+ <thead class="govuk-table__head">
22
+ <tr class="govuk-table__row">
23
+ <th class="govuk-table__header" scope="col" id="${id}-select-all"></th>
24
+ <th class="govuk-table__header" scope="col">Name</th>
25
+ </tr>
26
+ </thead>
27
+ <tbody class="govuk-table__body">
28
+ <tr class="govuk-table__row govuk-table__row--selected">
29
+ <td class="govuk-table__cell">
30
+ <div class="govuk-checkboxes__item govuk-checkboxes--small moj-multi-select__checkbox">
31
+ <input type="checkbox" class="govuk-checkboxes__input" id="mountain-aconcagua">
32
+ <label class="govuk-label govuk-checkboxes__label" for="mountain-aconcagua">
33
+ <span class="govuk-visually-hidden">Select Aconcagua</span>
34
+ </label>
35
+ </div>
36
+ </td>
37
+ <td class="govuk-table__cell">Aconcagua</td>
38
+ </tr>
39
+ <tr class="govuk-table__row">
40
+ <td class="govuk-table__cell">
41
+ <div class="govuk-checkboxes__item govuk-checkboxes--small moj-multi-select__checkbox">
42
+ <input type="checkbox" class="govuk-checkboxes__input" id="mountain-denali" value="denali">
43
+ <label class="govuk-label govuk-checkboxes__label" for="mountain-denali">
44
+ <span class="govuk-visually-hidden">Select Denali</span>
45
+ </label>
46
+ </div>
47
+ </td>
48
+ </td>
49
+ <td class="govuk-table__cell">Denali</td>
50
+ </tr>
51
+ </tbody>
52
+ </table>
53
+ `;
54
+ document.body.insertAdjacentHTML("afterbegin", html);
55
+ const component = document.querySelector(`#${id}`);
56
+ return component;
57
+ };
58
+
59
+ describe("multi select", () => {
60
+ let component, container;
61
+
62
+ beforeEach(() => {
63
+ component = createComponent();
64
+ container = component.querySelector("#multi-select-select-all");
65
+ checkboxes = component.querySelectorAll("tbody input[type=checkbox]");
66
+
67
+ new MOJFrontend.MultiSelect({
68
+ container: container,
69
+ checkboxes: checkboxes,
70
+ });
71
+ });
72
+
73
+ afterEach(() => {
74
+ document.body.innerHTML = "";
75
+ component, container, (checkboxes = undefined);
76
+ });
77
+
78
+ test("initialises component", () => {
79
+ const selectToggle = queryByRole(container, "checkbox");
80
+
81
+ expect(selectToggle).not.toBeNull();
82
+ expect(selectToggle).toHaveAccessibleName("Select all");
83
+ });
84
+
85
+ test("toggles all checkboxes", async () => {
86
+ const selectToggle = queryByRole(container, "checkbox");
87
+ const tbody = component.querySelector("tbody");
88
+ let checkboxes = queryAllByRole(tbody, "checkbox");
89
+
90
+ expect(checkboxes.length).toBe(2);
91
+ checkboxes.forEach((checkbox) => {
92
+ expect(checkbox).not.toBeChecked();
93
+ });
94
+
95
+ await user.click(selectToggle);
96
+ expect(selectToggle).toBeChecked();
97
+
98
+ checkboxes.forEach((checkbox) => {
99
+ expect(checkbox).toBeChecked();
100
+ });
101
+
102
+ await user.click(selectToggle);
103
+ expect(selectToggle).not.toBeChecked();
104
+
105
+ checkboxes.forEach((checkbox) => {
106
+ expect(checkbox).not.toBeChecked();
107
+ });
108
+ });
109
+
110
+ test("deselcting single checkbox unchecks all checkbox", async () => {
111
+ const selectToggle = queryByRole(container, "checkbox");
112
+ const tbody = component.querySelector("tbody");
113
+ const checkboxes = queryAllByRole(tbody, "checkbox");
114
+
115
+ expect(checkboxes.length).toBe(2);
116
+ checkboxes.forEach((checkbox) => {
117
+ expect(checkbox).not.toBeChecked();
118
+ });
119
+
120
+ await user.click(selectToggle);
121
+ expect(selectToggle).toBeChecked();
122
+
123
+ checkboxes.forEach((checkbox) => {
124
+ expect(checkbox).toBeChecked();
125
+ });
126
+
127
+ await user.click(checkboxes[0]);
128
+ expect(checkboxes[0]).not.toBeChecked();
129
+ expect(selectToggle).not.toBeChecked();
130
+
131
+ await user.click(checkboxes[0]);
132
+ expect(checkboxes[0]).toBeChecked();
133
+ expect(selectToggle).toBeChecked();
134
+ });
135
+ });
@@ -27,7 +27,7 @@ MOJFrontend.SearchToggle = function (options) {
27
27
  MOJFrontend.SearchToggle.prototype.showMenu = function () {
28
28
  this.toggleButton.attr("aria-expanded", "true");
29
29
  this.container.removeClass("moj-js-hidden");
30
- this.container.find("input").first().focus();
30
+ this.container.find("input").first().get(0).focus();
31
31
  };
32
32
 
33
33
  MOJFrontend.SearchToggle.prototype.hideMenu = function () {
@@ -0,0 +1,134 @@
1
+ const { queryByRole } = require("@testing-library/dom");
2
+ const { userEvent } = require("@testing-library/user-event");
3
+ const { configureAxe } = require("jest-axe");
4
+ const $ = require("jquery");
5
+
6
+ require("./search-toggle.js");
7
+
8
+ const user = userEvent.setup();
9
+ const axe = configureAxe({
10
+ rules: {
11
+ // disable landmark rules when testing isolated components.
12
+ region: { enabled: false },
13
+ },
14
+ });
15
+
16
+ const createComponent = () => {
17
+ html = `
18
+ <div class="moj-search-toggle" data-module="moj-search-toggle" data-moj-search-toggle-text="Find case">
19
+ <div class="moj-search-toggle__toggle"></div>
20
+ <div class="moj-search-toggle__search">
21
+
22
+ <div class="moj-search moj-search--ondark moj-search--toggle moj-js-hidden">
23
+
24
+ <form action="" method="get">
25
+
26
+ <div class="govuk-form-group">
27
+ <label class="govuk-label moj-search__label govuk-visually-hidden" for="search2">
28
+ Search
29
+ </label>
30
+
31
+ <div id="search2-hint" class="govuk-hint moj-search__hint ">
32
+ Enter case number, for example 123456
33
+ </div>
34
+
35
+ <input class="govuk-input moj-search__input " id="search2" name="search2" type="search" aria-describedby="search2-hint">
36
+
37
+ </div>
38
+
39
+ <button type="submit" class="govuk-button moj-search__button " data-module="govuk-button">
40
+ Search
41
+ </button>
42
+
43
+ </form>
44
+ </div>
45
+
46
+ </div>
47
+ </div>
48
+ <a href="#">link</a>`;
49
+ document.body.insertAdjacentHTML("afterbegin", html);
50
+ const component = document.querySelector(".moj-search-toggle");
51
+ return component;
52
+ };
53
+
54
+ describe("search toggle", () => {
55
+ let component, buttonContainer, searchContainer;
56
+
57
+ beforeEach(() => {
58
+ component = createComponent();
59
+ searchContainer = component.querySelector(".moj-search");
60
+ buttonContainer = component.querySelector(".moj-search-toggle__toggle");
61
+
62
+ new MOJFrontend.SearchToggle({
63
+ toggleButton: {
64
+ container: $(buttonContainer),
65
+ text: component.getAttribute("data-moj-search-toggle-text"),
66
+ },
67
+ search: {
68
+ container: $(searchContainer),
69
+ },
70
+ });
71
+ });
72
+
73
+ afterEach(() => {
74
+ document.body.innerHTML = "";
75
+ });
76
+
77
+ test("initialises component", () => {
78
+ const toggleButton = queryByRole(buttonContainer, "button");
79
+
80
+ expect(toggleButton).not.toBeNull();
81
+ expect(toggleButton).toHaveTextContent("Find case");
82
+ expect(toggleButton).toHaveAttribute("aria-haspopup", "true");
83
+ expect(toggleButton).toHaveAttribute("aria-expanded", "false");
84
+
85
+ expect(searchContainer).toHaveClass("moj-js-hidden");
86
+ });
87
+
88
+ test("clicking button toggles search container", async () => {
89
+ const toggleButton = queryByRole(buttonContainer, "button");
90
+
91
+ await user.click(toggleButton);
92
+
93
+ expect(toggleButton).toHaveAttribute("aria-expanded", "true");
94
+ expect(searchContainer).not.toHaveClass("moj-js-hidden");
95
+
96
+ const input = queryByRole(searchContainer, "searchbox");
97
+ expect(input).toHaveFocus();
98
+
99
+ await user.click(toggleButton);
100
+
101
+ expect(toggleButton).toHaveAttribute("aria-expanded", "false");
102
+ expect(searchContainer).toHaveClass("moj-js-hidden");
103
+ expect(toggleButton).toHaveFocus();
104
+ });
105
+
106
+ test("clicking outside closes the search container", async () => {
107
+ const toggleButton = queryByRole(buttonContainer, "button");
108
+
109
+ await user.click(toggleButton);
110
+
111
+ expect(toggleButton).toHaveAttribute("aria-expanded", "true");
112
+ expect(searchContainer).not.toHaveClass("moj-js-hidden");
113
+
114
+ await user.click(document.body);
115
+
116
+ expect(toggleButton).toHaveAttribute("aria-expanded", "false");
117
+ expect(searchContainer).toHaveClass("moj-js-hidden");
118
+ });
119
+
120
+ test("tabbing closes the search container", async () => {
121
+ const toggleButton = queryByRole(buttonContainer, "button");
122
+
123
+ await user.click(toggleButton);
124
+
125
+ expect(toggleButton).toHaveAttribute("aria-expanded", "true");
126
+ expect(searchContainer).not.toHaveClass("moj-js-hidden");
127
+
128
+ await user.tab();
129
+ await user.tab();
130
+
131
+ expect(toggleButton).toHaveAttribute("aria-expanded", "false");
132
+ expect(searchContainer).toHaveClass("moj-js-hidden");
133
+ });
134
+ });