@ons/design-system 72.9.1 → 72.9.2

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.
@@ -5,6 +5,7 @@ import * as cheerio from 'cheerio';
5
5
  import axe from '../../tests/helpers/axe';
6
6
  import { renderComponent, templateFaker } from '../../tests/helpers/rendering';
7
7
  import { mapAll } from '../../tests/helpers/cheerio';
8
+ import NavigationToggle from '../../components/navigation/navigation';
8
9
 
9
10
  import {
10
11
  EXAMPLE_HEADER_BASIC,
@@ -15,6 +16,8 @@ import {
15
16
  EXAMPLE_HEADER_LANGUAGE_CONFIG,
16
17
  EXAMPLE_HEADER_NAVIGATION_WITH_SUBNAVIGATION_CONFIG,
17
18
  EXAMPLE_HEADER_NAVIGATION_WITH_SITESEARCHAUTOSUGGEST,
19
+ EXAMPLE_HEADER_MENU_LINKS,
20
+ EXAMPLE_HEADER_SEARCH_AND_MENU_LINKS,
18
21
  } from './_test-examples';
19
22
 
20
23
  describe('FOR: Macro: Header', () => {
@@ -794,13 +797,14 @@ describe('FOR: Macro: Header', () => {
794
797
  test('THEN: renders search icon button', () => {
795
798
  expect(buttonSpy.occurrences[0]).toEqual({
796
799
  iconType: 'search',
797
- classes: 'ons-u-fs-s--b ons-js-toggle-header-search ons-btn--search ons-btn--search-icon',
800
+ classes: 'ons-u-fs-s--b ons-js-toggle-header-search ons-btn--close ons-btn--search-icon active disabled',
798
801
  type: 'button',
799
802
  variants: 'search',
800
803
  attributes: {
801
- 'aria-label': 'Example aria label',
802
- 'aria-expanded': 'false',
803
804
  'aria-controls': 'search-links-id',
805
+ 'aria-expanded': 'true',
806
+ 'aria-label': 'Example aria label',
807
+ 'aria-disabled': 'true',
804
808
  },
805
809
  });
806
810
  });
@@ -880,5 +884,111 @@ describe('FOR: Macro: Header', () => {
880
884
  expect($('.ons-header-nav-search').hasClass('custom-class')).toBe(true);
881
885
  });
882
886
  });
887
+
888
+ describe('WHEN: using basic header variant and search is active & disabled by default before JS loads', () => {
889
+ const $ = cheerio.load(renderComponent('header', { ...EXAMPLE_HEADER_SEARCH_LINKS, variants: 'basic' }));
890
+ const $searchBtn = $('.ons-js-toggle-header-search');
891
+
892
+ test('THEN: adds the "active" class to the search toggle button', () => {
893
+ expect($searchBtn.hasClass('active')).toBe(true);
894
+ });
895
+
896
+ test('THEN: adds the "disabled" class to the search toggle button', () => {
897
+ expect($searchBtn.hasClass('disabled')).toBe(true);
898
+ });
899
+
900
+ test('THEN: sets aria-disabled="true" on the search toggle button', () => {
901
+ expect($searchBtn.attr('aria-disabled')).toBe('true');
902
+ });
903
+ });
904
+ });
905
+
906
+ describe('GIVEN: Params: menuLinks', () => {
907
+ describe('WHEN: using basic header variant and menu toggle is rendered before JS loads', () => {
908
+ const $ = cheerio.load(renderComponent('header', EXAMPLE_HEADER_MENU_LINKS));
909
+ const $menuBtn = $('.ons-js-toggle-nav-menu');
910
+
911
+ test('THEN: adds the "active" class to the menu toggle button', () => {
912
+ expect($menuBtn.hasClass('active')).toBe(true);
913
+ });
914
+
915
+ test('THEN: adds the "disabled" class to the menu toggle button', () => {
916
+ expect($menuBtn.hasClass('disabled')).toBe(true);
917
+ });
918
+
919
+ test('THEN: sets aria-disabled="true" on the menu toggle button', () => {
920
+ expect($menuBtn.attr('aria-disabled')).toBe('true');
921
+ });
922
+
923
+ test('THEN: sets aria-expanded="true" on the menu toggle button', () => {
924
+ expect($menuBtn.attr('aria-expanded')).toBe('true');
925
+ });
926
+
927
+ test('THEN: sets aria-controls to the correct menu ID', () => {
928
+ expect($menuBtn.attr('aria-controls')).toBe('menu-links-id');
929
+ });
930
+ });
931
+
932
+ describe('GIVEN: Progressive enhancement via JS', () => {
933
+ describe('WHEN: both menu and search toggles are present', () => {
934
+ let $, menuBtn, searchBtn, menuEl, searchEl;
935
+
936
+ beforeEach(() => {
937
+ $ = cheerio.load(renderComponent('header', EXAMPLE_HEADER_SEARCH_AND_MENU_LINKS));
938
+ document.body.innerHTML = $.html();
939
+
940
+ menuBtn = document.querySelector('.ons-js-toggle-nav-menu');
941
+ menuEl = document.querySelector('.ons-js-nav-menu');
942
+ searchBtn = document.querySelector('.ons-js-toggle-header-search');
943
+ searchEl = document.querySelector('.ons-js-header-search');
944
+
945
+ if (menuBtn && menuEl) {
946
+ const navToggleMenu = new NavigationToggle(menuBtn, menuEl, 'ons-u-d-no');
947
+ navToggleMenu.registerEvents();
948
+ }
949
+
950
+ if (searchBtn && searchEl) {
951
+ const navToggleSearch = new NavigationToggle(searchBtn, searchEl, 'ons-u-d-no');
952
+ navToggleSearch.registerEvents();
953
+ }
954
+ });
955
+
956
+ test('THEN: menu toggle is active (aria-disabled false, no disabled class)', () => {
957
+ expect(menuBtn).not.toBeNull();
958
+ expect(menuBtn.getAttribute('aria-disabled')).toBe('false');
959
+ expect(menuBtn.classList.contains('disabled')).toBe(false);
960
+ });
961
+
962
+ test('THEN: search toggle is active (aria-disabled false, no disabled class)', () => {
963
+ expect(searchBtn).not.toBeNull();
964
+ expect(searchBtn.getAttribute('aria-disabled')).toBe('false');
965
+ expect(searchBtn.classList.contains('disabled')).toBe(false);
966
+ });
967
+
968
+ test('WHEN: clicking menu toggle closes search if open', () => {
969
+ expect(menuBtn && searchBtn).not.toBeNull();
970
+
971
+ // open search followed by menu
972
+ searchBtn.click();
973
+ menuBtn.click();
974
+
975
+ expect(searchBtn.classList.contains('active')).toBe(false);
976
+ expect(searchEl.getAttribute('aria-hidden')).toBe('true');
977
+ expect(searchEl.classList.contains('ons-u-d-no')).toBe(true);
978
+ });
979
+
980
+ test('WHEN: clicking search toggle closes menu if open', () => {
981
+ expect(menuBtn && searchBtn).not.toBeNull();
982
+
983
+ // open menu followed by search
984
+ menuBtn.click();
985
+ searchBtn.click();
986
+
987
+ expect(menuBtn.classList.contains('active')).toBe(false);
988
+ expect(menuEl.getAttribute('aria-hidden')).toBe('true');
989
+ expect(menuEl.classList.contains('ons-u-d-no')).toBe(true);
990
+ });
991
+ });
992
+ });
883
993
  });
884
994
  });
@@ -109,7 +109,7 @@ describe('macro: hero', () => {
109
109
  const $ = cheerio.load(renderComponent('hero', { ...EXAMPLE_HERO, variants: 'grey', officialStatisticsBadge: true }));
110
110
 
111
111
  expect($('.ons-hero__badge').length).toBe(1);
112
- expect($('.ons-hero__badge svg title').text().trim()).toBe('Offical Statistics Badge');
112
+ expect($('.ons-hero__badge svg title').text().trim()).toBe('Official Statistics Badge');
113
113
  });
114
114
 
115
115
  it('outputs the statistics badge as a link when officialStatisticsBadgeUrl is provided', () => {
@@ -15,7 +15,6 @@
15
15
  focusable="false"
16
16
  aria-hidden="true"
17
17
  role="img"
18
- title="ons-icon-arrow-forward"
19
18
  >
20
19
  <path
21
20
  d="M4.2,12c0-0.6,0.4-1,1-1h11.2l-4.9-4.9c-0.4-0.4-0.4-1,0-1.4c0.4-0.4,1-0.4,1.4,0l6.6,6.6c0.4,0.4,0.4,1,0,1.4l-6.6,6.6c-0.4,0.4-1,0.4-1.4,0c-0.4-0.4-0.4-1,0-1.4l4.9-4.9H5.2C4.7,13,4.2,12.6,4.2,12z"
@@ -30,7 +29,7 @@
30
29
  focusable="false"
31
30
  fill="currentColor"
32
31
  role="img"
33
- title="ons-icon-arrow-next"
32
+ aria-hidden="true"
34
33
  >
35
34
  <path
36
35
  d="m10 .2-.9.9c-.1.1-.1.4 0 .5l4 4H.6c-.2 0-.4.2-.4.4v1.2c0 .2.2.4.4.4h12.5l-3.9 3.7c-.2.2-.2.4 0 .6l.8.9c.2.2.4.2.6 0L16.8 7c.2-.2.2-.4 0-.6L10.7.3c-.3-.2-.5-.2-.7-.1z"
@@ -44,7 +43,7 @@
44
43
  focusable="false"
45
44
  fill="currentColor"
46
45
  role="img"
47
- title="ons-icon-arrow-previous"
46
+ aria-hidden="true"
48
47
  >
49
48
  <path
50
49
  d="M6.4.2.3 6.4c-.2.2-.2.4 0 .6l6.2 5.8c.2.2.4.1.6 0l.8-.9c.2-.2.1-.4 0-.6l-4-3.7h12.5c.2 0 .4-.2.4-.4V6c0-.2-.2-.4-.4-.4H3.8l4-4c.2-.1.2-.4.1-.5L7 .2c-.1-.1-.4-.1-.6 0z"
@@ -58,7 +57,7 @@
58
57
  focusable="false"
59
58
  fill="currentColor"
60
59
  role="img"
61
- title="ons-icon-arrow-up"
60
+ aria-hidden="true"
62
61
  >
63
62
  <path fill="currentColor" d="M6.5 0L0 6.5 1.4 8l4-4v12.7h2V4l4.3 4L13 6.4z"></path>
64
63
  </svg>
@@ -70,7 +69,7 @@
70
69
  focusable="false"
71
70
  fill="currentColor"
72
71
  role="img"
73
- title="ons-icon-check"
72
+ aria-hidden="true"
74
73
  >
75
74
  <path
76
75
  d="M14.35,3.9l-.71-.71a.5.5,0,0,0-.71,0h0L5.79,10.34,3.07,7.61a.51.51,0,0,0-.71,0l-.71.71a.51.51,0,0,0,0,.71l3.78,3.78a.5.5,0,0,0,.71,0h0L14.35,4.6A.5.5,0,0,0,14.35,3.9Z"
@@ -85,7 +84,7 @@
85
84
  focusable="false"
86
85
  fill="currentColor"
87
86
  role="img"
88
- title="ons-icon-chevron"
87
+ aria-hidden="true"
89
88
  >
90
89
  <path
91
90
  d="M5.74,14.28l-.57-.56a.5.5,0,0,1,0-.71h0l5-5-5-5a.5.5,0,0,1,0-.71h0l.57-.56a.5.5,0,0,1,.71,0h0l5.93,5.93a.5.5,0,0,1,0,.7L6.45,14.28a.5.5,0,0,1-.71,0Z"
@@ -100,7 +99,7 @@
100
99
  focusable="false"
101
100
  fill="currentColor"
102
101
  role="img"
103
- title="ons-icon-download"
102
+ aria-hidden="true"
104
103
  >
105
104
  <path
106
105
  d="M5.6 9a.48.48 0 0 0 .7 0l3-3.2a.48.48 0 0 0 0-.7C9.3 5 9.2 5 9 5H7.5V.5A.47.47 0 0 0 7 0H5a.47.47 0 0 0-.5.5V5H3a.47.47 0 0 0-.5.5.37.37 0 0 0 .1.3Z"
@@ -117,7 +116,7 @@
117
116
  focusable="false"
118
117
  fill="currentColor"
119
118
  role="img"
120
- title="ons-icon-exit"
119
+ aria-hidden="true"
121
120
  >
122
121
  <path
123
122
  d="M13.85,7.65l-2.5-2.5a.5.5,0,0,0-.71,0,.48.48,0,0,0-.15.36V7h-3a.5.5,0,0,0-.5.5v1a.5.5,0,0,0,.5.5h3v1.5A.49.49,0,0,0,11,11a.48.48,0,0,0,.34-.14l2.51-2.5a.49.49,0,0,0,0-.68Z"
@@ -136,7 +135,6 @@
136
135
  focusable="false"
137
136
  aria-hidden="true"
138
137
  role="img"
139
- title="ons-icon-external-link"
140
138
  >
141
139
  <path
142
140
  d="M13.5,9H13a.5.5,0,0,0-.5.5v3h-9v-9h3A.5.5,0,0,0,7,3V2.5A.5.5,0,0,0,6.5,2h-4a.5.5,0,0,0-.5.5v11a.5.5,0,0,0,.5.5h11a.5.5,0,0,0,.5-.5v-4A.5.5,0,0,0,13.5,9Z"
@@ -155,7 +153,7 @@
155
153
  focusable="false"
156
154
  fill="currentColor"
157
155
  role="img"
158
- title="ons-icon-lock"
156
+ aria-hidden="true"
159
157
  >
160
158
  <path
161
159
  d="M12.25,6h-.72V4.49a3.5,3.5,0,0,0-7,0V6H3.75A.77.77,0,0,0,3,6.75v6.5a.77.77,0,0,0,.75.75h8.5a.77.77,0,0,0,.75-.75V6.75A.77.77,0,0,0,12.25,6ZM5.54,4.49a2.5,2.5,0,1,1,5,0V6h-5ZM9,11.66a.3.3,0,0,1-.26.34H7.29A.29.29,0,0,1,7,11.69v0l.39-1.82a1,1,0,1,1,1.4-.18.77.77,0,0,1-.18.18Z"
@@ -170,7 +168,7 @@
170
168
  focusable="false"
171
169
  fill="currentColor"
172
170
  role="img"
173
- title="ons-icon-person"
171
+ aria-hidden="true"
174
172
  >
175
173
  <path d="M7,9H9a5,5,0,0,1,5,5H2A5,5,0,0,1,7,9Z" transform="translate(-2 -2)" />
176
174
  <circle cx="6" cy="3" r="3" />
@@ -183,7 +181,7 @@
183
181
  focusable="false"
184
182
  fill="currentColor"
185
183
  role="img"
186
- title="ons-icon-print"
184
+ aria-hidden="true"
187
185
  >
188
186
  <path
189
187
  d="M17 4H3C1.3 4 0 5.2 0 6.8v5.5h4V16h12v-3.7h4V6.8C20 5.2 18.7 4 17 4zm-3 10H6V9h8v5zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-1-8H4v3h12V0z"
@@ -197,7 +195,7 @@
197
195
  focusable="false"
198
196
  fill="currentColor"
199
197
  role="img"
200
- title="ons-icon-quote"
198
+ aria-hidden="true"
201
199
  >
202
200
  <path d="M13.44.77h-3l-2 4v6h6v-6h-3l2-4zm-8 0h-3l-2 4v6h6v-6h-3l2-4z" transform="translate(-0.44 -0.77)" />
203
201
  </svg>
@@ -209,7 +207,6 @@
209
207
  aria-hidden="true"
210
208
  fill="currentColor"
211
209
  role="img"
212
- title="ons-icon-search"
213
210
  >
214
211
  <path
215
212
  d="M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z"
@@ -224,7 +221,7 @@
224
221
  focusable="false"
225
222
  fill="currentColor"
226
223
  role="img"
227
- title="ons-icon-sort-sprite"
224
+ aria-hidden="true"
228
225
  >
229
226
  <path class="ons-topTriangle" d="M6 0l6 7.2H0L6 0zm0 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
230
227
  <path class="ons-bottomTriangle" d="M6 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z" />
@@ -239,7 +236,6 @@
239
236
  focusable="false"
240
237
  aria-hidden="true"
241
238
  role="img"
242
- title="ons-icon-facebook"
243
239
  >
244
240
  <path
245
241
  d="M32,16.0986285 C32.0009185,7.5342974 25.337417,0.468462963 16.8372092,0.0203294753 C8.33700136,-0.427804013 0.97607758,5.89865855 0.0874346352,14.4161886 C-0.801208309,22.9337186 5.09355054,30.6602611 13.5009524,31.9979281 L13.5009524,20.7518951 L9.44,20.7518951 L9.44,16.0986285 L13.5009524,16.0986285 L13.5009524,12.549267 C13.5009524,8.5169471 15.8857143,6.28613892 19.5428571,6.28613892 C20.742535,6.30277426 21.9393895,6.40782423 23.1238095,6.60044523 L23.1238095,10.5637711 L21.1047619,10.5637711 C19.1161905,10.5637711 18.4990476,11.8056643 18.4990476,13.0782216 L18.4990476,16.0986285 L22.9409524,16.0986285 L22.2247619,20.7518951 L18.4990476,20.7518951 L18.4990476,31.9979281 C26.2735701,30.760956 31.9991507,24.0182672 32,16.0986285 L32,16.0986285 Z"
@@ -254,7 +250,6 @@
254
250
  focusable="false"
255
251
  aria-hidden="true"
256
252
  role="img"
257
- title="ons-icon-twitter"
258
253
  >
259
254
  <polygon points="24.89,23.01 57.79,66.99 65.24,66.99 32.34,23.01" />
260
255
  <path
@@ -270,7 +265,6 @@
270
265
  focusable="false"
271
266
  aria-hidden="true"
272
267
  role="img"
273
- title="ons-icon-title"
274
268
  >
275
269
  <path
276
270
  d="M21.34,9.46c0.66,0,1.2,0.54,1.2,1.2c0,0.66-0.54,1.2-1.2,1.2c-0.66,0-1.2-0.54-1.2-1.2C20.14,10,20.68,9.46,21.34,9.46z M16,12.67c-1.84,0-3.33,1.49-3.33,3.33c0,1.84,1.49,3.33,3.33,3.33c1.84,0,3.33-1.49,3.33-3.33C19.33,14.16,17.84,12.67,16,12.67z M16,10.86c2.84,0,5.14,2.3,5.14,5.14c0,2.84-2.3,5.14-5.14,5.14c-2.84,0-5.14-2.3-5.14-5.14C10.86,13.16,13.16,10.86,16,10.86z M16.56,7.8h-1.11c-2.17,0-2.51,0.01-3.49,0.06c-0.97,0.04-1.5,0.21-1.86,0.34C9.64,8.39,9.3,8.6,8.95,8.95C8.6,9.3,8.39,9.64,8.2,10.1c-0.14,0.35-0.3,0.88-0.34,1.86c-0.04,0.98-0.06,1.32-0.06,3.49v1.11c0,2.17,0.01,2.51,0.06,3.49c0.04,0.97,0.21,1.5,0.34,1.86c0.18,0.47,0.4,0.8,0.75,1.15c0.35,0.35,0.68,0.57,1.15,0.75c0.35,0.14,0.88,0.3,1.86,0.34c0.94,0.04,1.29,0.06,3.23,0.06h1.61c1.94,0,2.3-0.02,3.23-0.06c0.97-0.04,1.5-0.21,1.86-0.34c0.47-0.18,0.8-0.4,1.15-0.75c0.35-0.35,0.57-0.68,0.75-1.15c0.14-0.35,0.3-0.88,0.34-1.86c0.04-0.94,0.06-1.29,0.06-3.23v-1.61c0-1.94-0.02-2.3-0.06-3.23c-0.04-0.97-0.21-1.5-0.34-1.86c-0.18-0.47-0.4-0.8-0.75-1.15C22.7,8.6,22.36,8.39,21.9,8.2c-0.35-0.14-0.88-0.3-1.86-0.34C19.06,7.82,18.72,7.8,16.56,7.8z M17.03,6c1.8,0,2.18,0.02,3.1,0.06c1.06,0.05,1.79,0.22,2.43,0.46c0.66,0.26,1.22,0.6,1.77,1.15c0.56,0.56,0.9,1.11,1.15,1.77c0.25,0.64,0.42,1.36,0.46,2.43c0.05,0.99,0.06,1.35,0.06,3.58v1.09c0,2.22-0.01,2.59-0.06,3.58c-0.05,1.06-0.22,1.79-0.46,2.43c-0.26,0.66-0.6,1.22-1.15,1.77c-0.56,0.56-1.11,0.9-1.77,1.15c-0.64,0.25-1.36,0.42-2.43,0.46C19.13,25.99,18.77,26,16.55,26h-1.09c-2.22,0-2.59-0.01-3.58-0.06c-1.06-0.05-1.79-0.22-2.43-0.46c-0.66-0.26-1.22-0.6-1.77-1.15c-0.56-0.56-0.9-1.11-1.15-1.77c-0.25-0.64-0.42-1.36-0.46-2.43C6.02,19.21,6,18.83,6,17.03v-2.06c0-1.8,0.02-2.18,0.06-3.1c0.05-1.06,0.22-1.79,0.46-2.43c0.26-0.66,0.6-1.22,1.15-1.77c0.56-0.56,1.11-0.9,1.77-1.15c0.64-0.25,1.36-0.42,2.43-0.46C12.79,6.02,13.17,6,14.97,6H17.03z M16,0C7.16,0,0,7.16,0,16s7.16,16,16,16s16-7.16,16-16c0-4.24-1.69-8.31-4.69-11.31S20.24,0,16,0z"
@@ -285,7 +279,6 @@
285
279
  focusable="false"
286
280
  aria-hidden="true"
287
281
  role="img"
288
- title="ons-icon-linkedin"
289
282
  >
290
283
  <path
291
284
  d="M16,-3.41060513e-13 C20.2434638,-3.41060513e-13 24.3131264,1.68570945 27.3137085,4.6862915 C30.3142906,7.68687356 32,11.7565362 32,16 C32,24.836556 24.836556,32 16,32 C7.163444,32 0,24.836556 0,16 C0,7.163444 7.163444,-3.41060513e-13 16,-3.41060513e-13 Z M11.3505859,12.4641113 L7.45385744,12.4641113 L7.45385744,24.1875 L11.3505859,24.1875 L11.3505859,12.4641113 Z M20.9152832,12.1889649 C18.8479004,12.1889649 17.9213867,13.3251953 17.4035644,14.1240234 L17.4035644,14.1240234 L17.4035644,12.4641113 L13.5070801,12.4641113 C13.5212538,12.7696262 13.5275532,13.809993 13.5292593,15.1533871 L13.5293118,16.8832762 C13.5292156,16.9843911 13.5291048,17.0860852 13.5289803,17.1882303 L13.5280782,17.8054916 L13.5280782,17.8054916 L13.5268961,18.427439 C13.5216699,20.9164121 13.5108442,23.3704557 13.5078578,24.0208157 L13.5070801,24.1875 L17.4035644,24.1875 L17.4035644,17.640625 C17.4035644,17.2902832 17.4287109,16.9401856 17.5317382,16.6896972 C17.8134766,15.9897461 18.4545899,15.2646484 19.5310059,15.2646484 C20.940918,15.2646484 21.5051269,16.3395996 21.5051269,17.9157715 L21.5051269,17.9157715 L21.5051269,24.1875 L25.4013672,24.1875 L25.4013672,17.465332 C25.4013672,13.8645019 23.4790039,12.1889649 20.9152832,12.1889649 Z M9.42822263,6.8125 C8.09521488,6.8125 7.22363281,7.68774412 7.22363281,8.83813475 C7.22363281,9.96313475 8.06933594,10.8632812 9.37695313,10.8632812 L9.37695313,10.8632812 L9.40234375,10.8632812 C10.7612305,10.8632812 11.6069336,9.96313475 11.6069336,8.83813475 C11.581543,7.68774412 10.7612305,6.8125 9.42822263,6.8125 Z"
@@ -301,7 +294,7 @@
301
294
  preserveAspectRatio="xMidYMid"
302
295
  fill="currentcolor"
303
296
  role="img"
304
- title="ons-icon-loader"
297
+ aria-hidden="true"
305
298
  >
306
299
  <rect x="0" y="0" width="100" height="100" fill="none"></rect>
307
300
  <rect x="46.5" y="40" width="7" height="20" rx="5" ry="5" transform="rotate(0 50 50) translate(0 -30)">
@@ -656,7 +649,6 @@
656
649
  viewBox="0 0 115 96"
657
650
  focusable="false"
658
651
  role="img"
659
- title="ons-icon-crest"
660
652
  >
661
653
  <title id="{{ params.altTextId | default ('crest-alt') }}">
662
654
  {{ params.altText | default ('Royal coat of arms of the United Kingdom') }}
@@ -692,7 +684,6 @@
692
684
  focusable="false"
693
685
  aria-hidden="true"
694
686
  role="img"
695
- title="ons-icon-circle-lined"
696
687
  >
697
688
  <path
698
689
  d="M211.25,203.37c1.01-1.12,2.01-2.24,2.97-3.43l25-48.6c0.42-1.47,0.78-2.93,1.07-4.42L211.25,203.37zM192.12,220.32c0.66-0.44,1.25-0.85,1.89-1.34l48.96-95.17c0.03-0.8,0.04-1.57,0.01-2.36L192.12,220.32z M178.35,228.81c0.56-0.24,1.1-0.54,1.64-0.84l62.16-120.82c-0.09-0.58-0.16-1.19-0.27-1.82L178.35,228.81z M165.96,234.53c0.54-0.2,1.02-0.39,1.52-0.61L239.7,93.54c-0.11-0.54-0.22-1.07-0.38-1.6L165.96,234.53z M154.53,238.39c0.48-0.09,0.94-0.24,1.43-0.42l80.34-156.16c-0.13-0.5-0.28-0.96-0.48-1.41L154.53,238.39z M143.84,240.89c0.42-0.07,0.89-0.12,1.35-0.27l87-169.12c-0.14-0.46-0.37-0.87-0.56-1.25L143.84,240.89z M133.65,242.35c0.38,0,0.84-0.05,1.27-0.12l92.61-180.02c-0.19-0.38-0.41-0.79-0.64-1.1L133.65,242.35z M123.86,243c0.4-0.03,0.82-0.01,1.24-0.08l97.28-189.09c-0.19-0.38-0.45-0.72-0.66-1.06L123.86,243z M114.59,242.76c0.38,0.1,0.77,0.11,1.17,0.07L216.88,46.27c-0.2-0.35-0.43-0.66-0.74-0.91L114.59,242.76z M2.67,96.04l29.04-56.45c-1.04,1.1-2.02,2.24-2.97,3.43l-25,48.6C3.32,93.09,2.99,94.56,2.67,96.04z M106.68,242.11L211.02,39.3c-0.25-0.27-0.5-0.54-0.8-0.8L105.57,241.93C105.95,242.03,106.31,242.07,106.68,242.11z M97.97,240.67L204.78,33.05c-0.25-0.27-0.5-0.54-0.8-0.79L96.87,240.48C97.25,240.59,97.61,240.63,97.97,240.67z M89.58,238.71L198.31,27.36c-0.27-0.23-0.57-0.49-0.86-0.68L88.53,238.41C88.86,238.53,89.23,238.63,89.58,238.71z M0,119.49l49.26-95.75c-0.68,0.47-1.31,0.96-1.97,1.49L0.07,117.02C0.02,117.86-0.01,118.67,0,119.49z M81.45,236.17L191.51,22.22c-0.27-0.24-0.55-0.43-0.9-0.6L80.43,235.79C80.77,235.96,81.1,236.08,81.45,236.17z M73.58,233.08L184.43,17.62c-0.27-0.23-0.61-0.41-0.91-0.57L72.59,232.67C72.9,232.83,73.24,233,73.58,233.08z M0.88,136.14L63.32,14.78c-0.6,0.32-1.14,0.62-1.7,0.96L0.68,134.2C0.69,134.84,0.77,135.51,0.88,136.14z M65.99,229.56L177.16,13.49c-0.04-0.02-0.04-0.02-0.08-0.04c-0.3-0.16-0.57-0.29-0.9-0.41l-111.12,216c0.28,0.2,0.55,0.33,0.86,0.49C65.96,229.55,65.96,229.55,65.99,229.56z M58.68,225.41L169.53,9.95c-0.28-0.19-0.65-0.33-0.99-0.41L57.77,224.85C58.03,225.08,58.36,225.29,58.68,225.41z M3.33,149.74L75.79,8.91c-0.54,0.21-1.03,0.39-1.52,0.62L2.95,148.15C3.06,148.69,3.19,149.19,3.33,149.74z M51.64,220.83L161.71,6.89c-0.31-0.16-0.67-0.3-1.01-0.38L50.75,220.23C51.01,220.46,51.34,220.67,51.64,220.83z M6.75,161.38L87.24,4.92c-0.5,0.13-0.96,0.27-1.42,0.42L6.26,159.98C6.41,160.44,6.56,160.9,6.75,161.38z M44.82,215.73L153.59,4.3c-0.36-0.14-0.7-0.22-1.07-0.26L43.98,215.01C44.23,215.28,44.5,215.51,44.82,215.73z M10.88,171.7L98.04,2.28c-0.42,0.07-0.88,0.12-1.32,0.23L10.3,170.49C10.47,170.91,10.69,171.32,10.88,171.7z M38.29,210.05L145.18,2.29c-0.38-0.1-0.75-0.14-1.11-0.19L37.49,209.26C37.74,209.53,37.99,209.8,38.29,210.05z M32.09,203.83L136.51,0.86c-0.36-0.04-0.76-0.11-1.15-0.11L31.34,202.96C31.56,203.27,31.85,203.56,32.09,203.83z M15.57,180.96L108.29,0.71c-0.44,0.02-0.84,0.05-1.27,0.12l-92.1,179.03C15.12,180.24,15.32,180.59,15.57,180.96z M26.21,196.9L127.45,0.11c-0.4-0.06-0.78-0.07-1.19-0.03L25.5,195.95C25.7,196.29,25.93,196.6,26.21,196.9z M20.67,189.32L118.06,0c-0.41,0.03-0.83,0.01-1.25,0.08L20.01,188.26C20.19,188.64,20.46,188.97,20.67,189.32z"
@@ -705,13 +696,12 @@
705
696
  height="90"
706
697
  viewBox="-6 0 90 90"
707
698
  fill="none"
708
- title="ons-official-statistics-badge"
709
699
  focusable="false"
710
700
  role="img"
711
701
  class="ons-icon--logo{{ iconClasses }}"
712
702
  >
713
703
  <title id="{{ params.altTextId | default ('official-statistics-alt') }}">
714
- {{ params.altText | default ('Offical Statistics Badge') }}
704
+ {{ params.altText | default ('Official Statistics Badge') }}
715
705
  </title>
716
706
  <path
717
707
  d="M12.4999 92.59C7.18988 92.59 2.87988 88.27 2.87988 82.97C2.87988 77.67 7.19988 73.35 12.4999 73.35H64.4999C69.8099 73.35 74.1199 77.67 74.1199 82.97C74.1199 88.27 69.7999 92.59 64.4999 92.59H12.4999Z"
@@ -9,12 +9,15 @@
9
9
  href="{{ language.url }}"
10
10
  lang="{{ language.isoCode }}"
11
11
  {% if language.attributes %}{% for attribute, value in (language.attributes) %}{{ ' ' }}{{ attribute }}="{{ value }}"{% endfor %}{% endif %}
12
- >{% if language.abbrText %}
12
+ >
13
+ {# Visually hidden helper text for screenreaders #}
14
+ <span class="ons-u-vh">Change language to{{ ' ' }}</span>
15
+ {% if language.abbrText %}
13
16
  <span class="ons-u-d-no@s">{{ language.abbrText }}</span><span class="ons-u-d-no@2xs@s">{{- language.text -}}</span>
14
17
  {% else %}
15
18
  {{- language.text -}}
16
- {% endif %}</a
17
- >
19
+ {% endif %}
20
+ </a>
18
21
  </li>
19
22
  {% endfor %}
20
23
  </ul>
@@ -2,14 +2,21 @@ import onViewportChange from '../../js/utils/viewport-change';
2
2
 
3
3
  const attrExpanded = 'aria-expanded';
4
4
  const attrHidden = 'aria-hidden';
5
+ const attrDisabled = 'aria-disabled';
5
6
 
6
7
  export default class NavigationToggle {
7
8
  constructor(toggle, navigation, hideClass) {
8
9
  this.toggle = toggle;
9
10
  this.navigation = navigation;
10
11
  this.hideClass = hideClass;
12
+ this.searchToggleBtn = document.querySelector('.ons-js-toggle-header-search');
13
+ this.menuBtn = document.querySelector('.ons-js-toggle-nav-menu');
14
+ this.menuEl = document.querySelector('.ons-js-nav-menu');
15
+ this.searchBtn = document.querySelector('.ons-btn--search');
16
+ this.searchEl = document.querySelector('.ons-js-header-search');
11
17
 
12
18
  this.toggle.classList.remove('ons-u-d-no');
19
+ this.toggle.classList.remove('disabled');
13
20
  this.setAria();
14
21
  onViewportChange(this.setAria.bind(this));
15
22
  }
@@ -19,8 +26,11 @@ export default class NavigationToggle {
19
26
  }
20
27
 
21
28
  toggleNav() {
22
- const isHidden = this.navigation.getAttribute(attrHidden);
23
- isHidden === 'false' ? this.closeNav() : this.openNav();
29
+ if (this.navigation.getAttribute(attrHidden) === 'false') {
30
+ this.closeNav();
31
+ } else {
32
+ this.openNav();
33
+ }
24
34
  }
25
35
 
26
36
  openNav() {
@@ -34,8 +44,7 @@ export default class NavigationToggle {
34
44
  input.focus();
35
45
  }
36
46
 
37
- const searchToggleBtn = document.querySelector('.ons-js-toggle-header-search');
38
- if (this.toggle === searchToggleBtn) {
47
+ if (this.toggle === this.searchToggleBtn) {
39
48
  this.updateSearchIcon(true, this.toggle);
40
49
  }
41
50
 
@@ -49,8 +58,7 @@ export default class NavigationToggle {
49
58
  this.navigation.setAttribute(attrHidden, 'true');
50
59
  this.navigation.classList.add(this.hideClass);
51
60
 
52
- const searchToggleBtn = document.querySelector('.ons-js-toggle-header-search');
53
- if (this.toggle === searchToggleBtn) {
61
+ if (this.toggle === this.searchToggleBtn) {
54
62
  this.updateSearchIcon(false, this.toggle);
55
63
  }
56
64
 
@@ -58,32 +66,25 @@ export default class NavigationToggle {
58
66
  }
59
67
 
60
68
  updateSearchIcon(isOpen, toggle) {
61
- const searchIconSVG = `
62
- <span class="ons-btn__inner">
63
- <svg class="ons-icon ons-icon--search ons-u-mr-2xs" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" role="img" title="ons-icon-search">
64
- <path d="M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z"></path>
65
- </svg>
66
- <span class="ons-btn__text"></span>
67
- </span>`;
68
-
69
- const closeIconSVG = `
70
- <span class="ons-btn__inner">
71
- <svg class="ons-icon ons-icon--close" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" focusable="false" fill="currentColor" role="img" title="ons-icon-close">
72
- <path d="M12 0C5.4 0 0 5.4 0 12C0 18.6 5.4 24 12 24C18.6 24 24 18.6 24 12C24 5.4 18.6 0 12 0ZM17.3143 15.5143C17.6571 15.8571 17.6571 16.3714 17.3143 16.7143L16.7143 17.3143C16.3714 17.6571 15.8571 17.6571 15.5143 17.3143L12 13.8L8.48571 17.3143C8.14286 17.6571 7.62857 17.6571 7.28571 17.3143L6.68571 16.7143C6.34286 16.3714 6.34286 15.8571 6.68571 15.5143L10.2 12L6.68571 8.48571C6.34286 8.14286 6.34286 7.62857 6.68571 7.28571L7.28571 6.68571C7.62857 6.34286 8.14286 6.34286 8.48571 6.68571L12 10.2L15.5143 6.68571C15.8571 6.34286 16.3714 6.34286 16.7143 6.68571L17.3143 7.28571C17.6571 7.62857 17.6571 8.14286 17.3143 8.48571L13.8 12L17.3143 15.5143Z"/>
73
- </svg>
74
- <span class="ons-btn__text"></span>
75
- </span>`;
76
-
77
- if (isOpen) {
78
- toggle.classList.add('ons-btn--close');
79
- this.toggle.classList.remove('ons-btn--search-icon');
80
- this.toggle.innerHTML = closeIconSVG;
81
- }
82
- if (!isOpen) {
83
- toggle.classList.remove('ons-btn--close');
84
- toggle.classList.add('ons-btn--search-icon');
85
- toggle.innerHTML = searchIconSVG;
86
- }
69
+ const icons = {
70
+ open: `
71
+ <span class="ons-btn__inner">
72
+ <svg class="ons-icon ons-icon--close" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" focusable="false" fill="currentColor" role="img" title="ons-icon-close">
73
+ <path d="M12 0C5.4 0 0 5.4 0 12C0 18.6 5.4 24 12 24C18.6 24 24 18.6 24 12C24 5.4 18.6 0 12 0ZM17.3143 15.5143C17.6571 15.8571 17.6571 16.3714 17.3143 16.7143L16.7143 17.3143C16.3714 17.6571 15.8571 17.6571 15.5143 17.3143L12 13.8L8.48571 17.3143C8.14286 17.6571 7.62857 17.6571 7.28571 17.3143L6.68571 16.7143C6.34286 16.3714 6.34286 15.8571 6.68571 15.5143L10.2 12L6.68571 8.48571C6.34286 8.14286 6.34286 7.62857 6.68571 7.28571L7.28571 6.68571C7.62857 6.34286 8.14286 6.34286 8.48571 6.68571L12 10.2L15.5143 6.68571C15.8571 6.34286 16.3714 6.34286 16.7143 6.68571L17.3143 7.28571C17.6571 7.62857 17.6571 8.14286 17.3143 8.48571L13.8 12L17.3143 15.5143Z"/>
74
+ </svg>
75
+ <span class="ons-btn__text"></span>
76
+ </span>`,
77
+ close: `
78
+ <span class="ons-btn__inner">
79
+ <svg class="ons-icon ons-icon--search ons-u-mr-2xs" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" role="img" title="ons-icon-search">
80
+ <path d="M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z"></path>
81
+ </svg>
82
+ <span class="ons-btn__text"></span>
83
+ </span>`,
84
+ };
85
+ toggle.innerHTML = isOpen ? icons.open : icons.close;
86
+ toggle.classList.toggle('ons-btn--close', isOpen);
87
+ toggle.classList.toggle('ons-btn--search-icon', !isOpen);
87
88
  }
88
89
 
89
90
  isHidden(el) {
@@ -92,15 +93,19 @@ export default class NavigationToggle {
92
93
 
93
94
  setAria() {
94
95
  const isToggleHidden = this.isHidden(this.toggle);
95
- const hasAria = this.navigation.hasAttribute(attrHidden);
96
+ this.toggle.setAttribute(attrDisabled, 'false');
97
+
98
+ this.searchToggleBtn?.setAttribute(attrDisabled, 'false');
96
99
 
97
100
  if (!isToggleHidden) {
98
- if (!hasAria) {
99
- this.closeNav();
100
- }
101
- } else if (hasAria) {
102
- this.toggle.removeAttribute(attrExpanded);
101
+ // close nav by default if toggle button is visible
102
+ this.closeNav();
103
+ } else {
104
+ // if toggle is hidden, set nav to open (for visible desktop nav)
105
+ this.toggle.setAttribute(attrExpanded, 'false'); // set mobile menu expanded to false if hidden
103
106
  this.navigation.removeAttribute(attrHidden);
107
+ this.navigation.classList.remove(this.hideClass);
108
+
104
109
  if (this.hideClass !== 'ons-u-d-no') {
105
110
  this.navigation.classList.remove(this.hideClass);
106
111
  } else {
@@ -110,28 +115,22 @@ export default class NavigationToggle {
110
115
  }
111
116
 
112
117
  toggleMenuAndSearch() {
113
- const menuBtn = document.querySelector('.ons-js-toggle-nav-menu');
114
- const menuEl = document.querySelector('.ons-js-nav-menu');
115
- const searchToggle = document.querySelector('.ons-js-toggle-header-search');
116
- const searchBtn = document.querySelector('.ons-btn--search');
117
- const searchEl = document.querySelector('.ons-js-header-search');
118
-
119
- const isMenuOpen = menuBtn.getAttribute('aria-expanded') === 'true';
120
- const isSearchOpen = searchBtn.getAttribute('aria-expanded') === 'true';
121
-
122
- if (isMenuOpen && this.toggle == menuBtn) {
123
- this.updateSearchIcon(false, searchToggle);
124
- searchBtn.setAttribute('aria-expanded', 'false');
125
- searchEl.setAttribute('aria-hidden', 'true');
126
- searchEl.classList.add('ons-u-d-no');
127
- searchToggle.classList.remove('active');
118
+ const isMenuOpen = this.menuBtn.getAttribute('aria-expanded') === 'true';
119
+ const isSearchOpen = this.searchBtn.getAttribute('aria-expanded') === 'true';
120
+
121
+ if (isMenuOpen && this.toggle == this.menuBtn) {
122
+ this.updateSearchIcon(false, this.searchToggleBtn);
123
+ this.searchBtn.setAttribute('aria-expanded', 'false');
124
+ this.searchEl.setAttribute('aria-hidden', 'true');
125
+ this.searchEl.classList.add('ons-u-d-no');
126
+ this.searchToggleBtn.classList.remove('active');
128
127
  }
129
128
 
130
- if (isSearchOpen && this.toggle == searchToggle) {
131
- menuBtn.setAttribute('aria-expanded', 'false');
132
- menuEl.setAttribute('aria-hidden', 'true');
133
- menuEl.classList.add('ons-u-d-no');
134
- menuBtn.classList.remove('active');
129
+ if (isSearchOpen && this.toggle == this.searchToggleBtn) {
130
+ this.menuBtn.setAttribute('aria-expanded', 'false');
131
+ this.menuEl.setAttribute('aria-hidden', 'true');
132
+ this.menuEl.classList.add('ons-u-d-no');
133
+ this.menuBtn.classList.remove('active');
135
134
  }
136
135
  }
137
136
  }
@@ -180,10 +180,12 @@ describe('script: navigation', () => {
180
180
  });
181
181
 
182
182
  it('has aria-expanded set as `false` on the navigation toggle button', async () => {
183
+ // wait for progressive enhancement by checking that the toggle button is visible
184
+ await page.waitForSelector(buttonEl + ':not(.ons-u-d-no)');
183
185
  const button = await page.$(buttonEl);
184
- const ariaExpandedIsFalse = await button.evaluate((node) => node.getAttribute('aria-expanded') === 'false');
185
- expect(ariaExpandedIsFalse).toBe(true);
186
- });
186
+ const ariaExpanded = await button.evaluate((node) => node.getAttribute('aria-expanded'));
187
+ expect(ariaExpanded).toBe('false');
188
+ }, 10000);
187
189
  });
188
190
 
189
191
  describe('when the viewport is small', () => {
@@ -262,7 +264,7 @@ describe('script: navigation', () => {
262
264
 
263
265
  describe.each([['main', EXAMPLE_NAVIGATION, '.ons-navigation--main', '.ons-js-navigation-button']])(
264
266
  'level: %s navigation',
265
- (_, params, navEl, buttonEl) => {
267
+ (_, params, navEl) => {
266
268
  describe('when the viewport is small and manually made wider', () => {
267
269
  beforeEach(async () => {
268
270
  await setViewport(page, { width: 600, height: 1050 });
@@ -276,12 +278,6 @@ describe('script: navigation', () => {
276
278
  expect(hasAriaAttribute).toBe(true);
277
279
  });
278
280
 
279
- it('has aria-expanded removed from the navigation toggle button', async () => {
280
- const button = await page.$(buttonEl);
281
- const hasAriaExpanded = await button.evaluate((node) => node.getAttribute('aria-expanded') === null);
282
- expect(hasAriaExpanded).toBe(true);
283
- });
284
-
285
281
  it('has the hide class removed from the navigation list', async () => {
286
282
  const hasClass = await page.$eval(navEl, (node) =>
287
283
  node.classList.contains('ons-u-d-no@2xs@l' || 'ons-u-d-no' || 'ons-u-d-no@xs@l'),
@@ -104,7 +104,10 @@
104
104
  </dd>
105
105
  {% endif %}
106
106
  {% if item.actions %}
107
- <dd class="ons-summary__actions{{ ' ons-summary__column-size--2' if not item.valueList }}" {% if item.id %}id="{{ item.id }}"{% endif %}>
107
+ <dd
108
+ class="ons-summary__actions{{ ' ons-summary__column-size--2' if not item.valueList }}"
109
+ {% if item.id %}id="{{ item.id }}"{% endif %}
110
+ >
108
111
  {% for action in item.actions %}
109
112
  {% if loop.index > 1 %}<span class="ons-summary__spacer"></span>{% endif %}
110
113
  <a
@@ -3,7 +3,7 @@
3
3
  {% from "components/skip-to-content/_macro.njk" import onsSkipToContent %}
4
4
  {% from "components/button/_macro.njk" import onsButton %}
5
5
 
6
- <aside class="ons-table-of-contents-container" role="complementary">
6
+ <aside class="ons-table-of-contents-container">
7
7
  {% if params.skipLink %}
8
8
  {{
9
9
  onsSkipToContent({