@ons/design-system 53.1.1 → 55.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/components/accordion/_macro.njk +2 -3
  2. package/components/accordion/_macro.spec.js +3 -40
  3. package/components/accordion/accordion.dom.js +19 -0
  4. package/components/{collapsible/collapsible.group.js → accordion/accordion.js} +12 -5
  5. package/components/accordion/accordion.spec.js +56 -51
  6. package/components/autosuggest/_autosuggest.scss +4 -4
  7. package/components/autosuggest/autosuggest.spec.js +12 -2
  8. package/components/autosuggest/autosuggest.ui.js +4 -7
  9. package/components/button/_button.scss +28 -7
  10. package/components/button/_macro.njk +1 -1
  11. package/components/button/_macro.spec.js +2 -2
  12. package/components/checkboxes/_checkbox.scss +50 -17
  13. package/components/collapsible/_collapsible.scss +59 -85
  14. package/components/collapsible/_macro.njk +6 -39
  15. package/components/collapsible/_macro.spec.js +0 -53
  16. package/components/collapsible/collapsible.dom.js +3 -12
  17. package/components/collapsible/collapsible.js +3 -45
  18. package/components/collapsible/collapsible.spec.js +6 -139
  19. package/components/cookies-banner/_cookies-banner.scss +15 -7
  20. package/components/cookies-banner/_macro.njk +66 -22
  21. package/components/cookies-banner/_macro.spec.js +172 -114
  22. package/components/cookies-banner/cookies-banner.js +35 -13
  23. package/components/cookies-banner/cookies-banner.spec.js +58 -54
  24. package/components/document-list/document-list.scss +2 -0
  25. package/components/download-resources/download-resources.js +19 -0
  26. package/components/download-resources/download-resources.spec.js +95 -0
  27. package/components/external-link/_macro.njk +1 -1
  28. package/components/external-link/_macro.spec.js +2 -2
  29. package/components/fieldset/_fieldset.scss +11 -1
  30. package/components/fieldset/_macro.njk +9 -8
  31. package/components/fieldset/_macro.spec.js +27 -5
  32. package/components/footer/_footer.scss +1 -0
  33. package/components/header/_macro.njk +2 -5
  34. package/components/header/_macro.spec.js +0 -16
  35. package/components/hero/_macro.njk +1 -1
  36. package/components/hero/_macro.spec.js +1 -1
  37. package/components/icons/_macro.njk +1 -1
  38. package/components/input/_input-type.scss +37 -3
  39. package/components/input/_input.scss +28 -9
  40. package/components/input/_macro.njk +17 -14
  41. package/components/input/_macro.spec.js +56 -0
  42. package/components/label/_label.scss +1 -1
  43. package/components/label/_macro.njk +27 -15
  44. package/components/label/_macro.spec.js +31 -0
  45. package/components/lists/_macro.njk +1 -1
  46. package/components/lists/_macro.spec.js +2 -2
  47. package/components/message/_message.scss +1 -0
  48. package/components/modal/_macro.njk +2 -2
  49. package/components/modal/_modal.scss +10 -9
  50. package/components/mutually-exclusive/mutually-exclusive.duration.spec.js +2 -0
  51. package/components/mutually-exclusive/mutually-exclusive.number.spec.js +1 -0
  52. package/components/navigation/_macro.njk +0 -1
  53. package/components/navigation/_macro.spec.js +0 -1
  54. package/components/pagination/_pagination.scss +1 -0
  55. package/components/panel/_macro.njk +6 -7
  56. package/components/panel/_macro.spec.js +23 -20
  57. package/components/panel/_panel.scss +13 -5
  58. package/components/phase-banner/_phase-banner.scss +1 -0
  59. package/components/radios/_radio.scss +16 -4
  60. package/components/relationships/_relationships.scss +2 -2
  61. package/components/reply/_macro.njk +2 -2
  62. package/components/skip-to-content/_skip.scss +2 -1
  63. package/components/table/_macro.njk +3 -2
  64. package/components/table/_macro.spec.js +0 -27
  65. package/components/table/_table.scss +15 -7
  66. package/components/table/sortable-table.js +1 -0
  67. package/components/tabs/_tabs.scss +57 -34
  68. package/components/tabs/tabs.js +4 -2
  69. package/components/upload/_upload.scss +2 -2
  70. package/css/census.css +1 -1
  71. package/css/ids.css +1 -1
  72. package/css/main.css +1 -1
  73. package/js/main.js +1 -0
  74. package/layout/_template.njk +8 -8
  75. package/package.json +1 -1
  76. package/scripts/main.es5.js +1 -1
  77. package/scripts/main.js +2 -2
  78. package/scss/base/_global.scss +1 -0
  79. package/scss/objects/_page.scss +1 -1
  80. package/scss/overrides/hcm.scss +205 -46
  81. package/scss/patternlib.scss +1 -56
  82. package/scss/vars/_colors.scss +2 -1
@@ -4,6 +4,20 @@ import { setViewport } from '../../tests/helpers/puppeteer';
4
4
  import { renderComponent, renderTemplate, setTestPage } from '../../tests/helpers/rendering';
5
5
 
6
6
  const EXAMPLE_PAGE = `
7
+ ${renderComponent('header', {})}
8
+ ${renderComponent('breadcrumbs', {
9
+ ariaLabel: 'Breadcrumbs',
10
+ itemsList: [
11
+ {
12
+ url: '/',
13
+ text: 'Home',
14
+ },
15
+ {
16
+ url: '/components',
17
+ text: 'Components',
18
+ },
19
+ ],
20
+ })}
7
21
  <div class="ons-js-adv-filter">
8
22
  ${renderComponent('button', {
9
23
  type: 'button',
@@ -175,6 +189,7 @@ const EXAMPLE_PAGE = `
175
189
  <p>Try selecting different filters to get results.</p>
176
190
  </div>
177
191
  </div>
192
+ ${renderComponent('footer', {})}
178
193
  `;
179
194
 
180
195
  const RENDERED_EXAMPLE_PAGE = renderTemplate(EXAMPLE_PAGE);
@@ -505,6 +520,32 @@ describe('script: download-resources', () => {
505
520
  expect(displayStyle).not.toBe('none');
506
521
  });
507
522
 
523
+ it('hides the underlying page elements when the "Show filters" button is pressed', async () => {
524
+ await page.click('.ons-js-adv-filter__trigger');
525
+
526
+ const isListHiddenClass = await page.$eval('.ons-js-adv-filter__gallery', node => node.classList.contains('ons-u-d-no'));
527
+ const isListAriaHidden = await page.$eval('.ons-js-adv-filter__gallery', node => node.getAttribute('aria-hidden'));
528
+ const isOptionsHiddenClass = await page.$eval('.ons-adv-filter__results-options', node => node.classList.contains('ons-u-d-no'));
529
+ const isOptionsAriaHidden = await page.$eval('.ons-adv-filter__results-options', node => node.getAttribute('aria-hidden'));
530
+ const isHeaderHiddenClass = await page.$eval('.ons-header', node => node.classList.contains('ons-u-d-no'));
531
+ const isHeaderAriaHidden = await page.$eval('.ons-header', node => node.getAttribute('aria-hidden'));
532
+ const isFooterHiddenClass = await page.$eval('.ons-footer', node => node.classList.contains('ons-u-d-no'));
533
+ const isFooterAriaHidden = await page.$eval('.ons-footer', node => node.getAttribute('aria-hidden'));
534
+ const isBreadcrumbsHiddenClass = await page.$eval('.ons-breadcrumb', node => node.classList.contains('ons-u-d-no'));
535
+ const isBreadcrumbsAriaHidden = await page.$eval('.ons-breadcrumb', node => node.getAttribute('aria-hidden'));
536
+
537
+ expect(isListHiddenClass).toBe(true);
538
+ expect(isListAriaHidden).toBe('true');
539
+ expect(isOptionsHiddenClass).toBe(true);
540
+ expect(isOptionsAriaHidden).toBe('true');
541
+ expect(isHeaderHiddenClass).toBe(true);
542
+ expect(isHeaderAriaHidden).toBe('true');
543
+ expect(isFooterHiddenClass).toBe(true);
544
+ expect(isFooterAriaHidden).toBe('true');
545
+ expect(isBreadcrumbsHiddenClass).toBe(true);
546
+ expect(isBreadcrumbsAriaHidden).toBe('true');
547
+ });
548
+
508
549
  it('hides filter elements when the "Show (n results)" button is pressed', async () => {
509
550
  await page.click('.ons-js-adv-filter__trigger');
510
551
  await page.click('.ons-js-adv-filter__show');
@@ -513,6 +554,33 @@ describe('script: download-resources', () => {
513
554
  expect(displayStyle).toBe('none');
514
555
  });
515
556
 
557
+ it('shows the underlying page elements when the "Show (n results)" button is pressed', async () => {
558
+ await page.click('.ons-js-adv-filter__trigger');
559
+ await page.click('.ons-js-adv-filter__show');
560
+
561
+ const isListHiddenClass = await page.$eval('.ons-js-adv-filter__gallery', node => node.classList.contains('ons-u-d-no'));
562
+ const isListAriaHidden = await page.$eval('.ons-js-adv-filter__gallery', node => node.getAttribute('aria-hidden'));
563
+ const isOptionsHiddenClass = await page.$eval('.ons-adv-filter__results-options', node => node.classList.contains('ons-u-d-no'));
564
+ const isOptionsAriaHidden = await page.$eval('.ons-adv-filter__results-options', node => node.getAttribute('aria-hidden'));
565
+ const isHeaderHiddenClass = await page.$eval('.ons-header', node => node.classList.contains('ons-u-d-no'));
566
+ const isHeaderAriaHidden = await page.$eval('.ons-header', node => node.getAttribute('aria-hidden'));
567
+ const isFooterHiddenClass = await page.$eval('.ons-footer', node => node.classList.contains('ons-u-d-no'));
568
+ const isFooterAriaHidden = await page.$eval('.ons-footer', node => node.getAttribute('aria-hidden'));
569
+ const isBreadcrumbsHiddenClass = await page.$eval('.ons-breadcrumb', node => node.classList.contains('ons-u-d-no'));
570
+ const isBreadcrumbsAriaHidden = await page.$eval('.ons-breadcrumb', node => node.getAttribute('aria-hidden'));
571
+
572
+ expect(isListHiddenClass).toBe(false);
573
+ expect(isListAriaHidden).toBe('false');
574
+ expect(isOptionsHiddenClass).toBe(false);
575
+ expect(isOptionsAriaHidden).toBe('false');
576
+ expect(isHeaderHiddenClass).toBe(false);
577
+ expect(isHeaderAriaHidden).toBe('false');
578
+ expect(isFooterHiddenClass).toBe(false);
579
+ expect(isFooterAriaHidden).toBe('false');
580
+ expect(isBreadcrumbsHiddenClass).toBe(false);
581
+ expect(isBreadcrumbsAriaHidden).toBe('false');
582
+ });
583
+
516
584
  it('hides filter elements when the "Close" button is pressed', async () => {
517
585
  await page.click('.ons-js-adv-filter__trigger');
518
586
  await page.click('.ons-js-adv-filter__close');
@@ -520,6 +588,33 @@ describe('script: download-resources', () => {
520
588
  const displayStyle = await page.$eval('.ons-adv-filter__panel', node => getComputedStyle(node).display);
521
589
  expect(displayStyle).toBe('none');
522
590
  });
591
+
592
+ it('shows the underlying page elements when the "Close" button is pressed', async () => {
593
+ await page.click('.ons-js-adv-filter__trigger');
594
+ await page.click('.ons-js-adv-filter__close');
595
+
596
+ const isListHiddenClass = await page.$eval('.ons-js-adv-filter__gallery', node => node.classList.contains('ons-u-d-no'));
597
+ const isListAriaHidden = await page.$eval('.ons-js-adv-filter__gallery', node => node.getAttribute('aria-hidden'));
598
+ const isOptionsHiddenClass = await page.$eval('.ons-adv-filter__results-options', node => node.classList.contains('ons-u-d-no'));
599
+ const isOptionsAriaHidden = await page.$eval('.ons-adv-filter__results-options', node => node.getAttribute('aria-hidden'));
600
+ const isHeaderHiddenClass = await page.$eval('.ons-header', node => node.classList.contains('ons-u-d-no'));
601
+ const isHeaderAriaHidden = await page.$eval('.ons-header', node => node.getAttribute('aria-hidden'));
602
+ const isFooterHiddenClass = await page.$eval('.ons-footer', node => node.classList.contains('ons-u-d-no'));
603
+ const isFooterAriaHidden = await page.$eval('.ons-footer', node => node.getAttribute('aria-hidden'));
604
+ const isBreadcrumbsHiddenClass = await page.$eval('.ons-breadcrumb', node => node.classList.contains('ons-u-d-no'));
605
+ const isBreadcrumbsAriaHidden = await page.$eval('.ons-breadcrumb', node => node.getAttribute('aria-hidden'));
606
+
607
+ expect(isListHiddenClass).toBe(false);
608
+ expect(isListAriaHidden).toBe('false');
609
+ expect(isOptionsHiddenClass).toBe(false);
610
+ expect(isOptionsAriaHidden).toBe('false');
611
+ expect(isHeaderHiddenClass).toBe(false);
612
+ expect(isHeaderAriaHidden).toBe('false');
613
+ expect(isFooterHiddenClass).toBe(false);
614
+ expect(isFooterAriaHidden).toBe('false');
615
+ expect(isBreadcrumbsHiddenClass).toBe(false);
616
+ expect(isBreadcrumbsAriaHidden).toBe('false');
617
+ });
523
618
  });
524
619
  });
525
620
 
@@ -6,5 +6,5 @@
6
6
  </span><span class="ons-external-link__icon">&nbsp;<svg class="ons-svg-icon" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" focusable="false" aria-hidden="true">
7
7
  <path 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" transform="translate(-2 -1.99)"/>
8
8
  <path d="M8.83,7.88a.51.51,0,0,0,.71,0l2.31-2.32,1.28,1.28A.51.51,0,0,0,14,6.49v-4a.52.52,0,0,0-.5-.5h-4A.51.51,0,0,0,9,2.52a.58.58,0,0,0,.14.33l1.28,1.28L8.12,6.46a.51.51,0,0,0,0,.71Z" transform="translate(-2 -1.99)"/>
9
- </svg></span><span class="ons-external-link__new-window-description ons-u-vh">({{- params.newWindowDescription | default("opens in a new tab") -}})</span></a>
9
+ </svg></span><span class="ons-external-link__new-window-description ons-u-vh"> ({{- params.newWindowDescription | default("opens in a new tab") -}})</span></a>
10
10
  {% endmacro %}
@@ -54,7 +54,7 @@ describe('macro: external-link', () => {
54
54
  it('has a default new window description', async () => {
55
55
  const $ = cheerio.load(renderComponent('external-link', EXAMPLE_EXTERNAL_LINK));
56
56
 
57
- expect($('.ons-external-link__new-window-description').text()).toBe('(opens in a new tab)');
57
+ expect($('.ons-external-link__new-window-description').text()).toBe(' (opens in a new tab)');
58
58
  });
59
59
 
60
60
  it('has a custom new window description when `newWindowDescription` is provided', () => {
@@ -65,7 +65,7 @@ describe('macro: external-link', () => {
65
65
  }),
66
66
  );
67
67
 
68
- expect($('.ons-external-link__new-window-description').text()).toBe('(custom opens in a new tab text)');
68
+ expect($('.ons-external-link__new-window-description').text()).toBe(' (custom opens in a new tab text)');
69
69
  });
70
70
 
71
71
  it('has an "external-link" icon', async () => {
@@ -1,11 +1,21 @@
1
1
  .ons-fieldset {
2
2
  &__legend {
3
3
  font-weight: $font-weight-bold;
4
- margin: 0 0 0.6rem;
4
+ margin: 0;
5
+ &:not(&--with-description) {
6
+ margin-bottom: 0.55rem;
7
+ }
8
+ &-title {
9
+ display: block;
10
+ margin: 0;
11
+ padding: 0 0 1.5rem;
12
+ }
5
13
  }
6
14
 
7
15
  &__description:not(&__description--title) {
8
16
  @extend .ons-label__description;
17
+
18
+ margin-bottom: 0.55rem;
9
19
  }
10
20
 
11
21
  &__description--title {
@@ -1,6 +1,7 @@
1
1
  {% from "components/error/_macro.njk" import onsError %}
2
2
 
3
3
  {% macro onsFieldset(params) %}
4
+ {% set descriptionID = (params.id ~ "-" if params.id else "") ~ "legend-description" %}
4
5
  {% set fieldset -%}
5
6
  {% if params.dontWrap is defined and params.dontWrap -%}
6
7
  <div class="ons-input-items">
@@ -12,20 +13,20 @@
12
13
  class="ons-fieldset{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}"
13
14
  {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
14
15
  >
15
- <legend class="ons-fieldset__legend{% if params.legendIsQuestionTitle is defined and params.legendIsQuestionTitle %} ons-u-mb-no{% endif %}{% if params.legendClasses is defined and params.legendClasses %} {{ params.legendClasses }}{% endif %}">
16
+ <legend{% if params.description %} aria-describedBy="{{ descriptionID }}"{% endif %} class="ons-fieldset__legend{% if params.legendIsQuestionTitle %} ons-u-mb-no{% endif %}{% if params.legendClasses is defined and params.legendClasses %} {{ params.legendClasses }}{% endif %}{% if params.description %} ons-fieldset__legend--with-description{% endif %}">
16
17
  {%- if params.legendIsQuestionTitle -%}
17
- <h1 id="fieldset-legend-title" class="ons-fieldset__legend-title ons-u-mb-m{% if params.legendTitleClasses is defined and params.legendTitleClasses %} {{ params.legendTitleClasses }}{% endif %}">
18
+ <h1 id="fieldset-legend-title" class="ons-fieldset__legend-title{% if params.legendTitleClasses is defined and params.legendTitleClasses %} {{ params.legendTitleClasses }}{% endif %}">
18
19
  {{- params.legend | safe -}}
19
20
  </h1>
20
21
  {%- else -%}
21
- <span class="ons-fieldset__legend-title">{{- params.legend | safe -}}</span>
22
- {%- endif -%}
23
- {%- if params.description is defined and params.description -%}
24
- <div class="ons-fieldset__description{% if params.legendIsQuestionTitle %} ons-fieldset__description--title ons-u-mb-m{% endif %}">
25
- {{- params.description | safe -}}
26
- </div>
22
+ <span class="ons-fieldset__legend-title ons-u-pb-no">{{- params.legend | safe -}}</span>
27
23
  {%- endif -%}
28
24
  </legend>
25
+ {%- if params.description -%}
26
+ <div id="{{ descriptionID }}" class="ons-fieldset__description{% if params.legendIsQuestionTitle %} ons-fieldset__description--title ons-u-mb-m{% endif %}">
27
+ {{- params.description | safe -}}
28
+ </div>
29
+ {%- endif -%}
29
30
  {{- caller() if caller -}}
30
31
  </fieldset>
31
32
  {%- endif %}
@@ -32,6 +32,13 @@ describe('macro: fieldset', () => {
32
32
  expect(title).toBe('Fieldset legend');
33
33
  });
34
34
 
35
+ it('has the correct `aria-decribedby` value', () => {
36
+ const $ = cheerio.load(renderComponent('fieldset', EXAMPLE_FIELDSET_BASIC));
37
+
38
+ const ariaDescByVal = $('.ons-fieldset__legend').attr('aria-describedby');
39
+ expect(ariaDescByVal).toBe('example-fieldset-legend-description');
40
+ });
41
+
35
42
  it('has the `description` text', () => {
36
43
  const $ = cheerio.load(renderComponent('fieldset', EXAMPLE_FIELDSET_BASIC));
37
44
 
@@ -41,6 +48,26 @@ describe('macro: fieldset', () => {
41
48
  expect(title).toBe('A fieldset description');
42
49
  });
43
50
 
51
+ it('has the correct `description` `id` when no `fieldset `id` is provided', () => {
52
+ const $ = cheerio.load(renderComponent('fieldset', { ...EXAMPLE_FIELDSET_BASIC, id: undefined }));
53
+
54
+ const id = $('.ons-fieldset__description').attr('id');
55
+ expect(id).toBe('legend-description');
56
+ });
57
+
58
+ it('has the correct `description` `id` when `fieldset `id` is provided', () => {
59
+ const $ = cheerio.load(renderComponent('fieldset', EXAMPLE_FIELDSET_BASIC));
60
+
61
+ const id = $('.ons-fieldset__description').attr('id');
62
+ expect(id).toBe('example-fieldset-legend-description');
63
+ });
64
+
65
+ it('has the correct `legend` class when `description` is provided', () => {
66
+ const $ = cheerio.load(renderComponent('fieldset', EXAMPLE_FIELDSET_BASIC));
67
+
68
+ expect($('.ons-fieldset__legend').hasClass('ons-fieldset__legend--with-description')).toBe(true);
69
+ });
70
+
44
71
  it('has additionally provided style classes', () => {
45
72
  const $ = cheerio.load(
46
73
  renderComponent('fieldset', {
@@ -123,11 +150,6 @@ describe('macro: fieldset', () => {
123
150
  expect(results).toHaveNoViolations();
124
151
  });
125
152
 
126
- it('has the correct class', () => {
127
- const $ = cheerio.load(renderComponent('fieldset', { ...EXAMPLE_FIELDSET_BASIC, legendIsQuestionTitle: true }));
128
- expect($('.ons-fieldset__legend').hasClass('ons-u-mb-no')).toBe(true);
129
- });
130
-
131
153
  it('renders the legend in a H1', () => {
132
154
  const $ = cheerio.load(renderComponent('fieldset', { ...EXAMPLE_FIELDSET_BASIC, legendIsQuestionTitle: true }));
133
155
  const titleTag = $('.ons-fieldset__legend-title')[0].tagName;
@@ -17,6 +17,7 @@
17
17
 
18
18
  &__warning {
19
19
  background-color: $color-banner-bg-dark;
20
+ outline: 2px solid transparent; // Add transparent outline because Windows High Contrast Mode doesn't show background
20
21
  }
21
22
 
22
23
  &__license {
@@ -72,7 +72,7 @@
72
72
  {% endif %}
73
73
  <div class="ons-grid__col ons-col-auto{% if controlVisibility == true %} ons-u-d-no@xxs@{{ breakpoint }}{% endif %}">
74
74
  <nav class="ons-header-service-nav ons-header-service-nav--main {{ params.serviceLinks.classes }}" aria-label="{{ params.serviceLinks.ariaLabel | default("Service links navigation") }}">
75
- <ul class="ons-header-service-nav__list" aria-label="{{ params.serviceLinks.ariaListLabel | default("Service Links") }}">
75
+ <ul class="ons-header-service-nav__list">
76
76
  {% for item in (params.serviceLinks.itemsList if params.serviceLinks.itemsList is iterable else params.serviceLinks.itemsList.items()) %}
77
77
  <li class="ons-header-service-nav__item">
78
78
  <a
@@ -101,7 +101,6 @@
101
101
  "attributes": {
102
102
  "aria-label": params.serviceLinks.toggleServicesButton.ariaLabel | default("Toggle menu"),
103
103
  "aria-controls": params.serviceLinks.id,
104
- "aria-haspopup": "true",
105
104
  "aria-expanded": "false"
106
105
  }
107
106
  }) }}
@@ -114,7 +113,7 @@
114
113
  </div>
115
114
  {% if params.serviceLinks is defined and params.serviceLinks %}
116
115
  <nav class="ons-header-service-nav ons-header-service-nav--mobile ons-u-d-no ons-js-services-mobile-nav" id="{{ params.serviceLinks.id }}" aria-label="{{ params.serviceLinks.ariaLabel | default("Service links navigation") }}">
117
- <ul class="ons-header-service-nav__list" aria-label="{{ params.serviceLinks.ariaListLabel | default("Service Links") }}">
116
+ <ul class="ons-header-service-nav__list">
118
117
  {% for item in (params.serviceLinks.itemsList if params.serviceLinks.itemsList is iterable else params.serviceLinks.itemsList.items()) %}
119
118
  <li class="ons-header-service-nav__item ons-header-service-nav__item--mobile">
120
119
  <a
@@ -225,7 +224,6 @@
225
224
  "attributes": {
226
225
  "aria-label": "Toggle search" ,
227
226
  "aria-controls": "ons-site-search",
228
- "aria-haspopup": "true",
229
227
  "aria-expanded": "false"
230
228
  }
231
229
  }) }}
@@ -245,7 +243,6 @@
245
243
  "attributes": {
246
244
  "aria-label": params.navigation.toggleNavigationButton.ariaLabel | default("Toggle main menu") ,
247
245
  "aria-controls": params.navigation.id,
248
- "aria-haspopup": "true",
249
246
  "aria-expanded": "false"
250
247
  }
251
248
  }) }}
@@ -13,7 +13,6 @@ const EXAMPLE_HEADER_BASIC = {
13
13
  const EXAMPLE_SERVICE_LINKS_CONFIG = {
14
14
  id: 'service-links',
15
15
  ariaLabel: 'Services menu',
16
- ariaListLabel: 'Menu',
17
16
  classes: 'custom-class',
18
17
  toggleServicesButton: {
19
18
  text: 'Menu',
@@ -378,12 +377,6 @@ describe('macro: header', () => {
378
377
  expect($('.ons-header-service-nav--main').attr('aria-label')).toBe('Services menu');
379
378
  });
380
379
 
381
- it('has the provided `aria-label` for the list', () => {
382
- const $ = cheerio.load(renderComponent('header', EXAMPLE_HEADER_SERVICE_LINKS_SINGLE));
383
-
384
- expect($('.ons-header-service-nav--main .ons-header-service-nav__list').attr('aria-label')).toBe('Menu');
385
- });
386
-
387
380
  it('has the link text for each list item', () => {
388
381
  const $ = cheerio.load(renderComponent('header', EXAMPLE_HEADER_SERVICE_LINKS_MULTIPLE));
389
382
 
@@ -410,12 +403,6 @@ describe('macro: header', () => {
410
403
  expect($('.ons-header-service-nav--mobile').attr('aria-label')).toBe('Services menu');
411
404
  });
412
405
 
413
- it('has the provided `aria-label` for the list for mobile', () => {
414
- const $ = cheerio.load(renderComponent('header', EXAMPLE_HEADER_SERVICE_LINKS_SINGLE));
415
-
416
- expect($('.ons-header-service-nav--mobile .ons-header-service-nav__list').attr('aria-label')).toBe('Menu');
417
- });
418
-
419
406
  it('has the link text for each list item for mobile', () => {
420
407
  const $ = cheerio.load(renderComponent('header', EXAMPLE_HEADER_SERVICE_LINKS_MULTIPLE));
421
408
 
@@ -445,7 +432,6 @@ describe('macro: header', () => {
445
432
  attributes: {
446
433
  'aria-label': 'Toggle services menu',
447
434
  'aria-controls': 'service-links',
448
- 'aria-haspopup': 'true',
449
435
  'aria-expanded': 'false',
450
436
  },
451
437
  });
@@ -558,7 +544,6 @@ describe('macro: header', () => {
558
544
  attributes: {
559
545
  'aria-label': 'Toggle main menu',
560
546
  'aria-controls': 'main-nav',
561
- 'aria-haspopup': 'true',
562
547
  'aria-expanded': 'false',
563
548
  },
564
549
  });
@@ -601,7 +586,6 @@ describe('macro: header', () => {
601
586
  attributes: {
602
587
  'aria-label': 'Toggle search',
603
588
  'aria-controls': 'ons-site-search',
604
- 'aria-haspopup': 'true',
605
589
  'aria-expanded': 'false',
606
590
  },
607
591
  });
@@ -25,7 +25,7 @@
25
25
 
26
26
  <header>
27
27
  {% if params.preTitleImage is defined and params.preTitleImage %}
28
- {% set preTitleImageWithModifier = params.preTitleImage.name|replace(r/(\.[^\.]+)$/, "--" + skinName + "$1") %}
28
+ {% set preTitleImageWithModifier = params.preTitleImage.name ~ "--" ~ skinName ~ ".svg" %}
29
29
  <img class="ons-hero__pre-title" src="{{ params.placeholderURL }}/img/{{ preTitleImageWithModifier }}" alt="{{ params.preTitleImage.alt }}">
30
30
  {% endif %}
31
31
  {% if params.title is defined and params.title %}
@@ -36,7 +36,7 @@ const EXAMPLE_HERO_WITH_BUTTON = {
36
36
  const EXAMPLE_HERO_WITH_PRETITLE_IMAGE = {
37
37
  ...EXAMPLE_HERO_MINIMAL,
38
38
  preTitleImage: {
39
- name: 'example.svg',
39
+ name: 'example',
40
40
  alt: 'svg alt text',
41
41
  },
42
42
  };
@@ -62,7 +62,7 @@
62
62
  <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"/>
63
63
  </svg>
64
64
  {% elif params.iconType == "sort-sprite" %}
65
- <svg id="sort-sprite{% if params.id is defined and params.id %}-{{ params.id | lower }}{% endif %}" class="ons-svg-icon {{ iconClasses }}" viewBox="0 0 12 19" xmlns="http://www.w3.org/2000/svg" focusable="false" fill="currentColor">
65
+ <svg id="sort-sprite{% if params.id is defined and params.id %}-{{ params.id | lower }}{% endif %}" class="ons-svg-icon{{ iconClasses }}" viewBox="0 0 12 19" xmlns="http://www.w3.org/2000/svg" focusable="false" fill="currentColor">
66
66
  <path class="ons-topTriangle" d="M6 0l6 7.2H0L6 0zm0 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z"/>
67
67
  <path class="ons-bottomTriangle" d="M6 18.6l6-7.2H0l6 7.2zm0 3.6l6 7.2H0l6-7.2z"/>
68
68
  </svg>
@@ -14,7 +14,16 @@
14
14
  z-index: 1;
15
15
 
16
16
  &:focus {
17
- box-shadow: inset 0 0 0 1px $color-input;
17
+ // Overide default input focus so it can wrap prefix/suffix too
18
+ box-shadow: none;
19
+ outline: none;
20
+ }
21
+
22
+ // Overide default input error style so it can wrap prefix/suffix too
23
+ &.ons-input--error:not(:focus) {
24
+ border-right: $input-border-width solid $color-input-border;
25
+ box-shadow: none;
26
+ outline: none;
18
27
  }
19
28
  }
20
29
 
@@ -36,13 +45,15 @@
36
45
 
37
46
  &__type,
38
47
  &__type[title] {
39
- border: 1px solid $color-input;
48
+ border: 1px solid $color-input-border;
40
49
  }
41
50
 
42
51
  &__input:focus + &__type::after {
52
+ // Style input + prefix/suffix on focus
53
+ @extend %ons-input-focus;
54
+
43
55
  border-radius: $input-radius;
44
56
  bottom: 0;
45
- box-shadow: 0 0 0 3px $color-focus;
46
57
  content: '';
47
58
  display: block;
48
59
  left: 0;
@@ -75,3 +86,26 @@
75
86
  }
76
87
  }
77
88
  }
89
+
90
+ // Errors
91
+ .ons-input--error:not(:focus) {
92
+ & + .ons-input-type__type,
93
+ & + .ons-input-type__type[title] {
94
+ border-color: $color-errors;
95
+ }
96
+
97
+ & + .ons-input-type__type::after {
98
+ border-radius: $input-radius;
99
+ bottom: 0;
100
+
101
+ // Style input + prefix/suffix for errors
102
+ box-shadow: 0 0 0 1px $color-errors;
103
+ content: '';
104
+ display: block;
105
+ left: 0;
106
+ outline: 1px solid transparent; // Add transparent outline because Windows High Contrast Mode doesn't show box-shadows
107
+ position: absolute;
108
+ right: 0;
109
+ top: 0;
110
+ }
111
+ }
@@ -1,10 +1,18 @@
1
1
  %ons-input-focus {
2
- box-shadow: 0 0 0 3px $color-focus, inset 0 0 0 1px $color-input;
3
- outline: none;
2
+ box-shadow: 0 0 0 $input-border-width $color-input-border, 0 0 0 4px $color-focus;
3
+
4
+ // Add transparent outline because Windows High Contrast Mode doesn't show box-shadows
5
+ outline: 3px solid transparent;
6
+ outline-offset: 1px;
7
+
8
+ @media screen and (forced-colors: active) {
9
+ // To better match the focus states of native controls
10
+ outline-color: Highlight;
11
+ }
4
12
  }
5
13
 
6
14
  .ons-input {
7
- border: $input-border-width solid $color-input;
15
+ border: $input-border-width solid $color-input-border;
8
16
  border-radius: $input-radius;
9
17
  color: inherit;
10
18
  display: block;
@@ -45,8 +53,9 @@
45
53
  }
46
54
 
47
55
  &--error:not(:focus) {
48
- border: 1px solid $color-errors;
49
- box-shadow: inset 0 0 0 1px $color-errors;
56
+ border: $input-border-width solid $color-errors;
57
+ box-shadow: 0 0 0 $input-border-width $color-errors;
58
+ outline: 1px solid transparent; // Add transparent outline because Windows High Contrast Mode doesn't show box-shadows
50
59
  }
51
60
 
52
61
  &--with-description {
@@ -73,7 +82,9 @@
73
82
 
74
83
  .ons-input--select {
75
84
  appearance: none;
76
- background: $color-white url('#{$static}/img/icons--chevron-down.svg') no-repeat center right 10px;
85
+ background: $color-input-bg
86
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 11.75 7.7'%3E%3Cpath fill='currentColor' d='m1.37.15 4.5 5.1 4.5-5.1a.37.37 0 0 1 .6 0l.7.7a.45.45 0 0 1 0 .5l-5.5 6.2a.37.37 0 0 1-.6 0l-5.5-6.1a.64.64 0 0 1 0-.6l.7-.7a.64.64 0 0 1 .6 0Z'/%3E%3C/svg%3E")
87
+ no-repeat center right 10px;
77
88
  background-size: 1rem;
78
89
  line-height: 1.3rem;
79
90
  padding: 0.39rem 2rem 0.39rem $input-padding-horizontal;
@@ -100,10 +111,10 @@
100
111
  color: transparent;
101
112
  }
102
113
  &:valid:not(:placeholder-shown) {
103
- background-color: $color-white;
114
+ background-color: $color-input-bg;
104
115
  }
105
116
  &:focus {
106
- background-color: $color-white;
117
+ background-color: $color-input-bg;
107
118
  }
108
119
  }
109
120
 
@@ -122,7 +133,7 @@
122
133
  .ons-input--ghost {
123
134
  border: 2px solid rgba(255, 255, 255, 0.6);
124
135
  &:focus {
125
- border: 2px solid $color-input;
136
+ border: 2px solid $color-input-border;
126
137
  }
127
138
  }
128
139
 
@@ -156,3 +167,11 @@
156
167
  }
157
168
  }
158
169
  }
170
+
171
+ // Search type inputs - removes the 'X' clear button from webkit browsers
172
+ input[type='search']::-webkit-search-decoration,
173
+ input[type='search']::-webkit-search-cancel-button,
174
+ input[type='search']::-webkit-search-results-button,
175
+ input[type='search']::-webkit-search-results-decoration {
176
+ display: none;
177
+ }
@@ -27,7 +27,7 @@
27
27
  type="{{ type }}"
28
28
  id="{{ params.id }}"
29
29
  class="ons-input ons-input--text ons-input-type__input{% if params.error is defined and params.error %} ons-input--error{% endif %}{% if params.searchButton is defined and params.searchButton %} ons-search__input{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.width is defined and params.width %} ons-input{% if params.type is defined and (params.type == 'number' or params.type == 'tel') %}-number{% endif %}--w-{{ params.width }}{% endif %}{{ exclusiveClass }}{{ inputPlaceholder }}"
30
- {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}aria-labelledby="{{ params.prefix.id if params.prefix }}{{ params.suffix.id if params.suffix }}"{% endif %}
30
+ {% if params.prefix is defined and params.prefix and params.prefix.id is defined and params.prefix.id %}aria-labelledby="{{ params.prefix.id }}"{% elif params.suffix is defined and params.suffix and params.suffix.id is defined and params.suffix.id %}aria-labelledby="{{ params.suffix.id }}"{% endif %}
31
31
  {% if params.name is defined and params.name %}name="{{ params.name }}"{% endif %}
32
32
  {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
33
33
  {% if params.accept is defined and params.accept %}accept="{{ params.accept }}"{% endif %}
@@ -71,21 +71,24 @@
71
71
  {% endif %}
72
72
 
73
73
  {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}
74
- {% if params.prefix is defined and params.prefix %}
75
- {% set prefixClass = " ons-input-type--prefix" %}
76
- {% endif %}
74
+ {% if (params.prefix is defined and params.prefix and params.prefix.id is defined and params.prefix.id and params.prefix.title is defined and params.prefix.title) or (params.suffix is defined and params.suffix and params.suffix.id is defined and params.suffix.id and params.suffix.title is defined and params.suffix.title) %}
75
+ {% if params.prefix is defined and params.prefix %}
76
+ {% set prefixClass = " ons-input-type--prefix" %}
77
+ {% endif %}
77
78
 
78
- <span class="ons-input-type{{ prefixClass }}">
79
- <span class="ons-input-type__inner">
80
- {{ input | safe }}
81
- {% set abbr = params.prefix or params.suffix %}
82
- <abbr
83
- {% if abbr.id is defined and abbr.id %}id="{{ abbr.id }}" {% endif %}
84
- class="ons-input-type__type ons-js-input-abbr"
85
- title="{{ abbr.title }}"
86
- >{{ abbr.text or abbr.title }}</abbr>
79
+ <span class="ons-input-type{{ prefixClass }}">
80
+ <span class="ons-input-type__inner">
81
+ {{ input | safe }}
82
+
83
+ {% set abbr = params.prefix or params.suffix %}
84
+ <abbr
85
+ id="{{ abbr.id }}"
86
+ class="ons-input-type__type ons-js-input-abbr"
87
+ title="{{ abbr.title }}"
88
+ >{{ abbr.text or abbr.title }}</abbr>
89
+ </span>
87
90
  </span>
88
- </span>
91
+ {% endif %}
89
92
  {% elif params.searchButton is defined and params.searchButton %}
90
93
  <span class="ons-grid--flex ons-search">
91
94
  {% call onsSearch({