@ons/design-system 54.0.1 → 55.2.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 (108) hide show
  1. package/components/access-code/_macro.njk +3 -3
  2. package/components/accordion/_macro.njk +1 -1
  3. package/components/address-input/_macro.njk +7 -7
  4. package/components/address-output/_macro.njk +7 -7
  5. package/components/autosuggest/_macro.njk +19 -19
  6. package/components/breadcrumbs/_macro.njk +3 -3
  7. package/components/browser-banner/_macro.njk +1 -1
  8. package/components/browser-banner/_macro.spec.js +31 -0
  9. package/components/button/_button.scss +21 -7
  10. package/components/button/_macro.njk +25 -25
  11. package/components/button/_macro.spec.js +2 -2
  12. package/components/call-to-action/_macro.njk +1 -1
  13. package/components/card/_macro.njk +10 -10
  14. package/components/checkboxes/_checkbox-macro.njk +9 -9
  15. package/components/checkboxes/_checkbox.scss +43 -10
  16. package/components/checkboxes/_macro.njk +10 -10
  17. package/components/checkboxes/checkbox-with-fieldset.js +6 -5
  18. package/components/checkboxes/checkboxes.dom.js +6 -9
  19. package/components/code-highlight/_macro.njk +1 -1
  20. package/components/collapsible/_collapsible.scss +2 -1
  21. package/components/collapsible/_macro.njk +7 -7
  22. package/components/date-input/_macro.njk +5 -5
  23. package/components/document-list/_macro.njk +29 -29
  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/duration/_macro.njk +9 -9
  28. package/components/duration/_macro.spec.js +0 -3
  29. package/components/error/_macro.njk +1 -1
  30. package/components/external-link/_macro.njk +2 -2
  31. package/components/external-link/_macro.spec.js +2 -2
  32. package/components/feedback/_macro.njk +2 -2
  33. package/components/field/_macro.njk +5 -5
  34. package/components/fieldset/_fieldset.scss +11 -1
  35. package/components/fieldset/_macro.njk +15 -14
  36. package/components/fieldset/_macro.spec.js +27 -5
  37. package/components/footer/_footer.scss +1 -0
  38. package/components/footer/_macro.njk +18 -18
  39. package/components/footer/_macro.spec.js +18 -0
  40. package/components/header/_header.scss +7 -2
  41. package/components/header/_macro.njk +63 -37
  42. package/components/header/_macro.spec.js +82 -14
  43. package/components/hero/_macro.njk +16 -16
  44. package/components/hero/_macro.spec.js +1 -1
  45. package/components/icons/_macro.njk +2 -2
  46. package/components/images/_macro.njk +3 -3
  47. package/components/input/_input-type.scss +7 -0
  48. package/components/input/_input.scss +13 -2
  49. package/components/input/_macro.njk +40 -34
  50. package/components/input/_macro.spec.js +64 -59
  51. package/components/label/_label.scss +1 -1
  52. package/components/label/_macro.njk +27 -15
  53. package/components/label/_macro.spec.js +31 -0
  54. package/components/language-selector/_macro.njk +1 -1
  55. package/components/lists/_macro.njk +22 -22
  56. package/components/lists/_macro.spec.js +2 -2
  57. package/components/message/_macro.njk +6 -6
  58. package/components/message/_message.scss +1 -0
  59. package/components/message-list/_macro.njk +1 -1
  60. package/components/metadata/_macro.njk +2 -2
  61. package/components/modal/_macro.njk +6 -6
  62. package/components/modal/_modal.scss +10 -9
  63. package/components/mutually-exclusive/_macro.njk +1 -1
  64. package/components/mutually-exclusive/mutually-exclusive.js +3 -1
  65. package/components/navigation/_macro.njk +9 -10
  66. package/components/navigation/_macro.spec.js +0 -1
  67. package/components/pagination/_macro.njk +3 -3
  68. package/components/pagination/_pagination.scss +1 -0
  69. package/components/panel/_macro.njk +38 -43
  70. package/components/panel/_macro.spec.js +24 -33
  71. package/components/panel/_panel.scss +13 -5
  72. package/components/phase-banner/_macro.njk +1 -1
  73. package/components/phase-banner/_macro.spec.js +31 -0
  74. package/components/phase-banner/_phase-banner.scss +1 -0
  75. package/components/promotional-banner/_macro.njk +2 -2
  76. package/components/question/_macro.njk +18 -18
  77. package/components/quote/_macro.njk +2 -2
  78. package/components/radios/_macro.njk +8 -8
  79. package/components/radios/_radio.scss +15 -3
  80. package/components/radios/check-radios.js +5 -1
  81. package/components/related-content/_macro.njk +2 -2
  82. package/components/relationships/_macro.njk +2 -2
  83. package/components/reply/_macro.njk +2 -2
  84. package/components/section-navigation/_macro.njk +2 -2
  85. package/components/select/_macro.njk +8 -8
  86. package/components/share-page/_macro.njk +2 -2
  87. package/components/skip-to-content/_skip.scss +2 -1
  88. package/components/status/_macro.njk +2 -2
  89. package/components/summary/_macro.njk +25 -25
  90. package/components/table/_macro.njk +13 -12
  91. package/components/table/_macro.spec.js +0 -27
  92. package/components/table/_table.scss +13 -6
  93. package/components/table/sortable-table.js +1 -0
  94. package/components/table-of-contents/_macro.njk +4 -4
  95. package/components/tabs/_tabs.scss +5 -3
  96. package/components/textarea/_macro.njk +8 -8
  97. package/components/timeline/_macro.njk +1 -1
  98. package/components/video/_macro.njk +1 -1
  99. package/css/census.css +1 -1
  100. package/css/ids.css +1 -1
  101. package/css/main.css +1 -1
  102. package/layout/_template.njk +57 -44
  103. package/package.json +1 -1
  104. package/scripts/main.es5.js +1 -1
  105. package/scripts/main.js +1 -1
  106. package/scss/base/_global.scss +1 -0
  107. package/scss/overrides/hcm.scss +205 -46
  108. package/scss/patternlib.scss +1 -0
@@ -11,9 +11,9 @@
11
11
  {% set type = "text" %}
12
12
  {% set pattern = "[0-9]*" %}
13
13
  {% set inputmode = "numeric" %}
14
- {% elif params.type is defined and params.type %}
14
+ {% elif params.type %}
15
15
  {% set type = params.type %}
16
- {% elif params.searchButton is defined and params.searchButton %}
16
+ {% elif params.searchButton %}
17
17
  {% set type = "search" %}
18
18
  {% else %}
19
19
  {% set type = "text" %}
@@ -26,24 +26,24 @@
26
26
  <input
27
27
  type="{{ type }}"
28
28
  id="{{ params.id }}"
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 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
- {% if params.name is defined and params.name %}name="{{ params.name }}"{% endif %}
32
- {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
33
- {% if params.accept is defined and params.accept %}accept="{{ params.accept }}"{% endif %}
34
- {% if params.min is defined and params.min %}min="{{ params.min }}"{% endif %}
35
- {% if params.max is defined and params.max %}max="{{ params.max }}"{% endif %}
36
- {% if params.minLength is defined and params.minLength %}minlength="{{ params.minLength }}"{% endif %}
37
- {% if params.maxLength is defined and params.maxLength %}maxlength="{{ params.maxLength }}"{% endif %}
38
- {% if pattern is defined and pattern %}pattern="{{ pattern }}"{% endif %}
39
- {% if inputmode is defined and inputmode %}inputmode="{{ inputmode }}"{% endif %}
40
- {% if params.autocomplete is defined and params.autocomplete %}autocomplete="{{ params.autocomplete }}"{% endif %}
41
- {% if params.accessiblePlaceholder is defined and params.accessiblePlaceholder %}placeholder="{{ params.label.text }}"{% endif %}
42
- {% if params.charCheckLimit is defined and params.charCheckLimit %}data-char-check-ref="{{ params.id }}-check-remaining" data-char-check-num="{{ params.charCheckLimit.limit }}" aria-describedby="{{ params.id }}-check-remaining"{% endif %}
43
- {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.charcheckCountdown is defined and params.charCheckLimit.charcheckCountdown %}data-char-check-countdown="true"{% endif %}
44
- {% 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 %}
45
- {% if params.label is defined and params.label and params.label.description is defined and params.label.description %}{% if params.label.id is defined and params.label.id %}aria-describedby="{{ params.label.id }}-description-hint"{% else %}aria-describedby="description-hint"{% endif %}{% endif %}
46
- {% if params.required is defined and params.required == true %}required="required"{% endif %}
29
+ class="ons-input ons-input--text ons-input-type__input{% if params.error %} ons-input--error{% endif %}{% if params.searchButton %} ons-search__input{% endif %}{% if params.classes %} {{ params.classes }}{% endif %}{% if params.width %} ons-input{% if params.type == 'number' or params.type == 'tel' %}-number{% endif %}--w-{{ params.width }}{% endif %}{{ exclusiveClass }}{{ inputPlaceholder }}"
30
+ {% if params.prefix and params.prefix.id %}aria-labelledby="{{ params.prefix.id }}"{% elif params.suffix and params.suffix.id %}aria-labelledby="{{ params.suffix.id }}"{% endif %}
31
+ {% if params.name %}name="{{ params.name }}"{% endif %}
32
+ {% if params.value %}value="{{ params.value }}"{% endif %}
33
+ {% if params.accept %}accept="{{ params.accept }}"{% endif %}
34
+ {% if params.min %}min="{{ params.min }}"{% endif %}
35
+ {% if params.max %}max="{{ params.max }}"{% endif %}
36
+ {% if params.minLength %}minlength="{{ params.minLength }}"{% endif %}
37
+ {% if params.maxLength %}maxlength="{{ params.maxLength }}"{% endif %}
38
+ {% if pattern %}pattern="{{ pattern }}"{% endif %}
39
+ {% if inputmode %}inputmode="{{ inputmode }}"{% endif %}
40
+ {% if params.autocomplete %}autocomplete="{{ params.autocomplete }}"{% endif %}
41
+ {% if params.accessiblePlaceholder %}placeholder="{{ params.label.text }}"{% endif %}
42
+ {% if params.charCheckLimit %}data-char-check-ref="{{ params.id }}-check-remaining" data-char-check-num="{{ params.charCheckLimit.limit }}" aria-describedby="{{ params.id }}-check-remaining"{% endif %}
43
+ {% if params.charCheckLimit and params.charCheckLimit.charcheckCountdown %}data-char-check-countdown="true"{% endif %}
44
+ {% if params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{ attribute }}{% if value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
45
+ {% if params.label and params.label.description %}{% if params.label.id %}aria-describedby="{{ params.label.id }}-description-hint"{% else %}aria-describedby="description-hint"{% endif %}{% endif %}
46
+ {% if params.required == true %}required="required"{% endif %}
47
47
  />
48
48
  {% if params.listeners %}
49
49
  <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
@@ -52,12 +52,12 @@
52
52
  {% endfor %}
53
53
  </script>
54
54
  {% endif %}
55
- {% if params.postTextboxLinkText is defined and params.postTextboxLinkText %}
55
+ {% if params.postTextboxLinkText %}
56
56
  <a class="ons-u-fs-s ons-input__post-link" href="{{ params.postTextboxLinkUrl }}">{{ params.postTextboxLinkText }}</a>
57
57
  {% endif %}
58
58
  {% endset %}
59
59
  {% set field %}
60
- {% if params.label is defined and params.label and params.label.text is defined and params.label.text %}
60
+ {% if params.label and params.label.text %}
61
61
  {{ onsLabel({
62
62
  "for": params.id,
63
63
  "id": params.label.id,
@@ -70,9 +70,9 @@
70
70
  }) }}
71
71
  {% endif %}
72
72
 
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 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 %}
73
+ {% if params.prefix or params.suffix %}
74
+ {% if (params.prefix and params.prefix.id) or (params.suffix and params.suffix.id) %}
75
+ {% if params.prefix %}
76
76
  {% set prefixClass = " ons-input-type--prefix" %}
77
77
  {% endif %}
78
78
 
@@ -81,15 +81,21 @@
81
81
  {{ input | safe }}
82
82
 
83
83
  {% set abbr = params.prefix or params.suffix %}
84
- <abbr
84
+ {% if params.prefix.title or params.suffix.title %}
85
+ {% set tag = 'abbr' %}
86
+ {% else %}
87
+ {% set tag = 'span' %}
88
+ {% endif %}
89
+
90
+ <{{ tag }}
85
91
  id="{{ abbr.id }}"
86
92
  class="ons-input-type__type ons-js-input-abbr"
87
- title="{{ abbr.title }}"
88
- >{{ abbr.text or abbr.title }}</abbr>
93
+ {% if abbr.title %}title="{{ abbr.title }}"{% endif %}
94
+ >{{ abbr.text }}</{{ tag }}>
89
95
  </span>
90
96
  </span>
91
97
  {% endif %}
92
- {% elif params.searchButton is defined and params.searchButton %}
98
+ {% elif params.searchButton %}
93
99
  <span class="ons-grid--flex ons-search">
94
100
  {% call onsSearch({
95
101
  "accessiblePlaceholder": params.accessiblePlaceholder,
@@ -110,7 +116,7 @@
110
116
  {% endif %}
111
117
  {% endset %}
112
118
 
113
- {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
119
+ {% if params.charCheckLimit and params.charCheckLimit.limit %}
114
120
  {% set charCheckField %}
115
121
  {% call onsCharLimit({
116
122
  "id": params.id ~ "-check",
@@ -126,7 +132,7 @@
126
132
  {% endset %}
127
133
  {% endif %}
128
134
 
129
- {% if params.mutuallyExclusive is defined and params.mutuallyExclusive %}
135
+ {% if params.mutuallyExclusive %}
130
136
  {% call onsMutuallyExclusive({
131
137
  "id": params.fieldId,
132
138
  "legend": params.legend,
@@ -142,7 +148,7 @@
142
148
  "error": params.error,
143
149
  "autosuggestResults": params.autosuggestResults
144
150
  }) %}
145
- {% if charCheckField is defined and charCheckField %}
151
+ {% if charCheckField %}
146
152
  {{ charCheckField | safe }}
147
153
  {% else %}
148
154
  {{ field | safe }}
@@ -156,9 +162,9 @@
156
162
  "classes": params.fieldClasses,
157
163
  "dontWrap": params.dontWrap,
158
164
  "error": params.error,
159
- "inline": params.label.inline if params.label is defined and params.label else ''
165
+ "inline": params.label.inline if params.label else ''
160
166
  }) %}
161
- {% if charCheckField is defined and charCheckField %}
167
+ {% if charCheckField %}
162
168
  {{ charCheckField | safe }}
163
169
  {% else %}
164
170
  {{ field | safe }}
@@ -79,6 +79,13 @@ const EXAMPLE_INPUT_WITH_MUTUALLY_EXCLUSIVE_WITH_ERROR = {
79
79
  };
80
80
 
81
81
  describe('macro: input', () => {
82
+ it('passes jest-axe checks', async () => {
83
+ const $ = cheerio.load(renderComponent('input', EXAMPLE_INPUT_WITH_LABEL));
84
+
85
+ const results = await axe($.html());
86
+ expect(results).toHaveNoViolations();
87
+ });
88
+
82
89
  it('has the provided `id` attribute', () => {
83
90
  const $ = cheerio.load(renderComponent('input', EXAMPLE_INPUT_MINIMAL));
84
91
 
@@ -374,7 +381,7 @@ describe('macro: input', () => {
374
381
  });
375
382
 
376
383
  describe('prefix and suffix', () => {
377
- it('adds `aria-labelledby` attribute when `prefix` is provided', () => {
384
+ it('passes jest-axe checks', async () => {
378
385
  const $ = cheerio.load(
379
386
  renderComponent('input', {
380
387
  ...EXAMPLE_INPUT_MINIMAL,
@@ -385,10 +392,11 @@ describe('macro: input', () => {
385
392
  }),
386
393
  );
387
394
 
388
- expect($('.ons-input').attr('aria-labelledby')).toBe('example-prefix-id');
395
+ const results = await axe($.html());
396
+ expect(results).toHaveNoViolations();
389
397
  });
390
398
 
391
- it('renders prefix element from `prefix.title`', () => {
399
+ it('adds `aria-labelledby` attribute when `prefix` is provided', () => {
392
400
  const $ = cheerio.load(
393
401
  renderComponent('input', {
394
402
  ...EXAMPLE_INPUT_MINIMAL,
@@ -399,13 +407,7 @@ describe('macro: input', () => {
399
407
  }),
400
408
  );
401
409
 
402
- expect($('.ons-input-type--prefix .ons-js-input-abbr').attr('id')).toBe('example-prefix-id');
403
- expect($('.ons-input-type--prefix .ons-js-input-abbr').attr('title')).toBe('Example prefix title');
404
- expect(
405
- $('.ons-input-type--prefix .ons-js-input-abbr')
406
- .text()
407
- .trim(),
408
- ).toBe('Example prefix title');
410
+ expect($('.ons-input').attr('aria-labelledby')).toBe('example-prefix-id');
409
411
  });
410
412
 
411
413
  it('renders prefix element from `prefix.text`', () => {
@@ -443,20 +445,6 @@ describe('macro: input', () => {
443
445
  expect($('.ons-input-type--prefix').length).toBe(0);
444
446
  });
445
447
 
446
- it('does not render prefix element when `prefix.title` not set', () => {
447
- const $ = cheerio.load(
448
- renderComponent('input', {
449
- ...EXAMPLE_INPUT_MINIMAL,
450
- prefix: {
451
- text: 'Example prefix text',
452
- id: 'example-prefix-id',
453
- },
454
- }),
455
- );
456
-
457
- expect($('.ons-input-type--prefix').length).toBe(0);
458
- });
459
-
460
448
  it('adds `aria-labelledby` attribute when `suffix` is provided', () => {
461
449
  const $ = cheerio.load(
462
450
  renderComponent('input', {
@@ -471,13 +459,14 @@ describe('macro: input', () => {
471
459
  expect($('.ons-input').attr('aria-labelledby')).toBe('example-suffix-id');
472
460
  });
473
461
 
474
- it('renders suffix element from `suffix.title`', () => {
462
+ it('renders suffix element from `suffix.text`', () => {
475
463
  const $ = cheerio.load(
476
464
  renderComponent('input', {
477
465
  ...EXAMPLE_INPUT_MINIMAL,
478
466
  suffix: {
479
467
  id: 'example-suffix-id',
480
468
  title: 'Example suffix title',
469
+ text: 'Example suffix text',
481
470
  },
482
471
  }),
483
472
  );
@@ -488,57 +477,51 @@ describe('macro: input', () => {
488
477
  $('.ons-js-input-abbr')
489
478
  .text()
490
479
  .trim(),
491
- ).toBe('Example suffix title');
480
+ ).toBe('Example suffix text');
492
481
  });
493
482
 
494
- it('renders suffix element from `suffix.text`', () => {
483
+ it('does not render suffix element when `suffix.id` not set', () => {
495
484
  const $ = cheerio.load(
496
485
  renderComponent('input', {
497
486
  ...EXAMPLE_INPUT_MINIMAL,
498
487
  suffix: {
499
- id: 'example-suffix-id',
500
488
  title: 'Example suffix title',
501
489
  text: 'Example suffix text',
502
490
  },
503
491
  }),
504
492
  );
505
493
 
506
- expect($('.ons-js-input-abbr').attr('id')).toBe('example-suffix-id');
507
- expect($('.ons-js-input-abbr').attr('title')).toBe('Example suffix title');
508
- expect(
509
- $('.ons-js-input-abbr')
510
- .text()
511
- .trim(),
512
- ).toBe('Example suffix text');
494
+ expect($('.ons-input').length).toBe(0);
513
495
  });
514
- });
515
496
 
516
- it('does not render suffix element when `suffix.id` not set', () => {
517
- const $ = cheerio.load(
518
- renderComponent('input', {
519
- ...EXAMPLE_INPUT_MINIMAL,
520
- suffix: {
521
- title: 'Example suffix title',
522
- text: 'Example suffix text',
523
- },
524
- }),
525
- );
497
+ it('renders an `abbr` tag when `title` set', () => {
498
+ const $ = cheerio.load(
499
+ renderComponent('input', {
500
+ ...EXAMPLE_INPUT_MINIMAL,
501
+ suffix: {
502
+ text: 'Example suffix text',
503
+ title: 'Example suffix title',
504
+ id: 'example-suffix-id',
505
+ },
506
+ }),
507
+ );
526
508
 
527
- expect($('.ons-input').length).toBe(0);
528
- });
509
+ expect($('.ons-input + abbr').length).toBe(1);
510
+ });
529
511
 
530
- it('does not render suffix element when `suffix.title` not set', () => {
531
- const $ = cheerio.load(
532
- renderComponent('input', {
533
- ...EXAMPLE_INPUT_MINIMAL,
534
- suffix: {
535
- text: 'Example suffix text',
536
- id: 'example-suffix-id',
537
- },
538
- }),
539
- );
512
+ it('renders a `span` tag when `title` not set', () => {
513
+ const $ = cheerio.load(
514
+ renderComponent('input', {
515
+ ...EXAMPLE_INPUT_MINIMAL,
516
+ suffix: {
517
+ text: 'Example suffix text',
518
+ id: 'example-suffix-id',
519
+ },
520
+ }),
521
+ );
540
522
 
541
- expect($('.ons-input').length).toBe(0);
523
+ expect($('.ons-input + span').length).toBe(1);
524
+ });
542
525
  });
543
526
 
544
527
  describe('search', () => {
@@ -577,6 +560,21 @@ describe('macro: input', () => {
577
560
  });
578
561
 
579
562
  describe('with character limit', () => {
563
+ it('passes jest-axe checks', async () => {
564
+ const $ = cheerio.load(
565
+ renderComponent('input', {
566
+ ...EXAMPLE_INPUT_WITH_CHARACTER_LIMIT,
567
+ label: {
568
+ id: 'example-input-label',
569
+ text: 'Example input label',
570
+ },
571
+ }),
572
+ );
573
+
574
+ const results = await axe($.html());
575
+ expect(results).toHaveNoViolations();
576
+ });
577
+
580
578
  it('has the provided minimum length', () => {
581
579
  const $ = cheerio.load(renderComponent('input', EXAMPLE_INPUT_WITH_CHARACTER_LIMIT));
582
580
 
@@ -673,6 +671,13 @@ describe('macro: input', () => {
673
671
  });
674
672
 
675
673
  describe('mutually exclusive', () => {
674
+ it('passes jest-axe checks', async () => {
675
+ const $ = cheerio.load(renderComponent('input', EXAMPLE_INPUT_WITH_MUTUALLY_EXCLUSIVE_WITH_ERROR));
676
+
677
+ const results = await axe($.html());
678
+ expect(results).toHaveNoViolations();
679
+ });
680
+
676
681
  it('has the `ons-js-exclusive-group-item` class', () => {
677
682
  const $ = cheerio.load(renderComponent('input', EXAMPLE_INPUT_WITH_MUTUALLY_EXCLUSIVE_WITH_ERROR));
678
683
 
@@ -19,7 +19,7 @@
19
19
  &--placeholder {
20
20
  font-size: 1rem;
21
21
  font-weight: $font-weight-regular;
22
- left: 8px;
22
+ left: 10px;
23
23
  position: absolute;
24
24
  top: 6px;
25
25
  }
@@ -1,32 +1,44 @@
1
1
  {% macro onsLabel(params) %}
2
- {% set field -%}
3
- <span
4
- {% if params.id is defined and params.id %} id="{{ params.id }}-description-hint"{% else %}id="description-hint"{% endif %}
5
- class="ons-label__description {% if params.inputType is defined and params.inputType %}ons-{{ params.inputType }}__label--with-description{% else %} ons-input--with-description{% endif %}">
2
+ {% if params.id %}
3
+ {% set descriptionID = params.id ~ "-description-hint" %}
4
+ {% else %}
5
+ {% set descriptionID = "description-hint" %}
6
+ {% endif %}
7
+
8
+ {% if params.inputType == "checkbox" or params.inputType == "radio" %}
9
+ {% set isCheckboxOrRadio = true %}
10
+ {% else %}
11
+ {% set isCheckboxOrRadio = false %}
12
+ {% endif %}
13
+
14
+ {% set description -%}
15
+ <span{% if isCheckboxOrRadio == false %} id="{{ descriptionID }}"{% endif %} class="ons-label__description {% if params.inputType %}ons-{{ params.inputType }}__label--with-description{% else %} ons-input--with-description{% endif %}">
6
16
  {{- params.description -}}
7
17
  </span>
8
18
  {%- endset %}
9
19
 
10
20
  <label
11
- class="{% if params.inputType is not defined -%}ons-label{%- endif %}{{- ' ' + params.classes if params.classes else "" -}}{%- if params.description is defined and params.description %} ons-label--with-description{%- endif %} {{- ' ons-label--placeholder' if params.accessiblePlaceholder else "" -}}"
12
- {% if params.for is defined and params.for %} for="{{ params.for }}"{% endif %}
13
- {% if params.id is defined and params.id %} id="{{ params.id }}"{% endif %}
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 %}
21
+ class="{% if params.inputType is not defined -%}ons-label{%- endif %}{{- ' ' + params.classes if params.classes else "" -}}{%- if params.description %} ons-label--with-description{%- endif %} {{- ' ons-label--placeholder' if params.accessiblePlaceholder else "" -}}"
22
+ {% if params.description %}aria-describedby="{{ descriptionID }}"{% endif %}
23
+ {% if params.for %} for="{{ params.for }}"{% endif %}
24
+ {% if params.id %} id="{{ params.id }}"{% endif %}
25
+ {% if params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{ attribute }}{% if value %}="{{ value }}"{% endif %}{% endfor %}{% endif %}
15
26
  >
16
27
 
17
28
  {{- params.text | safe -}}
18
29
 
19
- {%- if params.inputType is defined and params.inputType -%}
20
- {% if params.inputType == "checkbox" or params.inputType == "radio" -%}
21
- {%- if params.description is defined and params.description -%}
22
- {{- field | safe -}}
23
- {%- endif -%}
30
+ {%- if isCheckboxOrRadio == true -%}
31
+ {%- if params.description -%}
32
+ <span class="ons-label__aria-hidden-description" aria-hidden="true">{{- description | safe -}}</span>
24
33
  {%- endif -%}
25
34
  </label>
35
+ {% if isCheckboxOrRadio == true and params.description -%}
36
+ <span class="ons-label__visually-hidden-description ons-u-vh" id="{{ descriptionID }}">{{- params.description -}}</span>
37
+ {%- endif -%}
26
38
  {%- else -%}
27
39
  </label>
28
- {%- if params.description is defined and params.description -%}
29
- {{- field | safe -}}
40
+ {%- if params.description -%}
41
+ {{- description | safe -}}
30
42
  {%- endif -%}
31
43
  {%- endif %}
32
44
  {% endmacro %}
@@ -53,6 +53,12 @@ describe('macro: label', () => {
53
53
  expect($('.ons-label').attr('for')).toBe('some-input');
54
54
  });
55
55
 
56
+ it('has `aria-describedby` and correct value when description is provided', () => {
57
+ const $ = cheerio.load(renderComponent('label', EXAMPLE_LABEL_WITH_DESCRIPTION));
58
+
59
+ expect($('.ons-label').attr('aria-describedby')).toBe('example-label-description-hint');
60
+ });
61
+
56
62
  it.each([
57
63
  ['`inputType` parameter is not provided', EXAMPLE_LABEL, true],
58
64
  ['`inputType` parameter is provided', EXAMPLE_LABEL_WITH_INPUT_TYPE, false],
@@ -185,5 +191,30 @@ describe('macro: label', () => {
185
191
  expect($('.ons-label__description').hasClass(expectedInputSpecificModifier)).toBe(true);
186
192
  expect($('.ons-label__description').hasClass('ons-input--with-description')).toBe(false);
187
193
  });
194
+
195
+ it.each([['checkbox'], ['radio']])('has the description in an `aria-hidden` element when "%s" `inputType` is provided', inputType => {
196
+ const $ = cheerio.load(
197
+ renderComponent('label', {
198
+ ...EXAMPLE_LABEL_WITH_DESCRIPTION,
199
+ inputType,
200
+ }),
201
+ );
202
+
203
+ expect($('.ons-label__aria-hidden-description').attr('aria-hidden')).toBe('true');
204
+ });
205
+
206
+ it.each([['checkbox'], ['radio']])(
207
+ 'has a duplicate description in a visually hidden element when "%s" `inputType` is provided',
208
+ inputType => {
209
+ const $ = cheerio.load(
210
+ renderComponent('label', {
211
+ ...EXAMPLE_LABEL_WITH_DESCRIPTION,
212
+ inputType,
213
+ }),
214
+ );
215
+
216
+ expect($('.ons-label__visually-hidden-description').hasClass('ons-u-vh')).toBe(true);
217
+ },
218
+ );
188
219
  });
189
220
  });
@@ -5,7 +5,7 @@
5
5
  <ul class="ons-language-links{% if languageNumber and languageNumber != 2 %} ons-u-d-no@xxs@m{% endif %}">
6
6
  {% for language in alternativeLanguages %}
7
7
  <li class="ons-language-links__item">
8
- <a href="{{ language.url }}" lang="{{ language.ISOCode }}" {% if language.attributes is defined and language.attributes %}{% for attribute, value in (language.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}>{% if language.abbrText %}<span class="ons-u-d-no@s">{{ language.abbrText }}</span><span class="ons-u-d-no@xxs@s">{{ language.text }}</span>{% else %}{{ language.text }}{% endif %}</a>
8
+ <a href="{{ language.url }}" lang="{{ language.ISOCode }}" {% if language.attributes %}{% for attribute, value in (language.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}>{% if language.abbrText %}<span class="ons-u-d-no@s">{{ language.abbrText }}</span><span class="ons-u-d-no@xxs@s">{{ language.text }}</span>{% else %}{{ language.text }}{% endif %}</a>
9
9
  </li>
10
10
  {% endfor %}
11
11
  </ul>
@@ -2,44 +2,44 @@
2
2
  {% set variants = params.variants if params.variants else '' %}
3
3
  {% set listLength = params.itemsList | length %}
4
4
 
5
- {% if params.itemsList[0] is defined and params.itemsList[0].prefix %}
5
+ {% if params.itemsList[0].prefix %}
6
6
  {% set otherClasses = 'ons-list--bare ons-list--prefix' %}
7
- {% elif params.itemsList[0] is defined and params.itemsList[0].suffix %}
7
+ {% elif params.itemsList[0].suffix %}
8
8
  {% set otherClasses = 'ons-list--bare ons-list--suffix' %}
9
- {% elif params.itemsList[0] is defined and params.iconPosition is defined and params.iconPosition %}
9
+ {% elif params.iconPosition %}
10
10
  {% set otherClasses = 'ons-list--bare ons-list--icons' %}
11
11
  {% set iconType = params.iconType if params.iconType else '' %}
12
12
  {% else %}
13
13
  {% set otherClasses = '' %}
14
14
  {% endif %}
15
15
 
16
- {% if params.element is defined and params.element and listLength > 1 %}
16
+ {% if params.element and listLength > 1 %}
17
17
  {% set listEl = params.element | lower %}
18
- {% elif (params.element is defined and params.element == 'ol') and listLength < 2 %}
18
+ {% elif (params.element == 'ol') and listLength < 2 %}
19
19
  {% set listEl = 'p' %}
20
20
  {% else %}
21
21
  {% set listEl = 'ul' %}
22
22
  {% endif %}
23
- <{{listEl}} {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %} class="ons-list{% if listEl == 'p' %} ons-list--p{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes -}}{% endif %}{% if params.variants is defined and params.variants %}{% if params.variants is not string %}{% for variant in params.variants %} ons-list--{{ variant }}{% endfor %}{% else %} ons-list--{{ params.variants }}{% endif %}{% endif %}{% if otherClasses %} {{ otherClasses -}}{% endif %}"{% 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 %}>
23
+ <{{listEl}} {% if params.id %}id="{{ params.id }}"{% endif %} class="ons-list{% if listEl == 'p' %} ons-list--p{% endif %}{% if params.classes %} {{ params.classes -}}{% endif %}{% if params.variants %}{% if params.variants is not string %}{% for variant in params.variants %} ons-list--{{ variant }}{% endfor %}{% else %} ons-list--{{ params.variants }}{% endif %}{% endif %}{% if otherClasses %} {{ otherClasses -}}{% endif %}"{% if params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %} {{ attribute }}{% if value %}="{{ value }}"{% endif %}{% endfor %}{% endif %}>
24
24
  {%- for item in (params.itemsList if params.itemsList is iterable else params.itemsList.items()) -%}
25
25
  {% if listLength > 1 or listEl == 'ul' %}
26
- <li class="ons-list__item{% if item.listClasses is defined and item.listClasses %} {{ item.listClasses }}{% endif %}"{% if item.current is defined and item.current %} aria-current="true"{% endif %}>
26
+ <li class="ons-list__item{% if item.listClasses %} {{ item.listClasses }}{% endif %}"{% if item.current %} aria-current="true"{% endif %}>
27
27
  {% endif %}
28
28
  {% set itemText = item.text %}
29
- {% set itemIconType = item.iconType if item.iconType is defined and item.iconType else iconType %}
29
+ {% set itemIconType = item.iconType if item.iconType else iconType %}
30
30
 
31
31
  {# For Craft support we also support title and navigation title #}
32
- {% if item.navigationTitle is defined and item.navigationTitle %}
32
+ {% if item.navigationTitle %}
33
33
  {% set itemText = item.navigationTitle %}
34
- {% elif item.title is defined and item.title %}
34
+ {% elif item.title %}
35
35
  {% set itemText = item.title %}
36
36
  {% endif %}
37
37
 
38
- {%- if item.prefix is defined and item.prefix or (params.iconPosition is defined and params.iconPosition == 'before') -%}
38
+ {%- if item.prefix or (params.iconPosition == 'before') -%}
39
39
  <span class="ons-list__prefix"{% if listEl != 'ol' %} aria-hidden="true"{% endif %}>
40
- {%- if item.prefix is defined and item.prefix -%}
40
+ {%- if item.prefix -%}
41
41
  {{- item.prefix -}}.
42
- {% elif params.iconPosition is defined and params.iconPosition == 'before' %}
42
+ {% elif params.iconPosition == 'before' %}
43
43
  {% from "components/icons/_macro.njk" import onsIcon %}
44
44
  {{
45
45
  onsIcon({
@@ -50,8 +50,8 @@
50
50
  {%- endif -%}
51
51
  </span>
52
52
  {%- endif -%}
53
- {%- if item.url is defined and item.url and item.current != true -%}
54
- {%- if item.external is defined and item.external -%}
53
+ {%- if item.url and item.current != true -%}
54
+ {%- if item.external -%}
55
55
  {% from "components/external-link/_macro.njk" import onsExternalLink %}
56
56
  {{
57
57
  onsExternalLink({
@@ -60,21 +60,21 @@
60
60
  })
61
61
  }}
62
62
  {%- else -%}
63
- <a href="{{ item.url }}" class="ons-list__link{% if item.variants == 'inPageLink' %} ons-js-inpagelink{% endif %}{% if item.classes is defined and item.classes %} {{ item.classes }}{% endif %}"{% if item.target is defined and item.target %} target="{{ item.target }}"{% endif %}{% if item.attributes is defined and item.attributes %}{% for attribute, value in (item.attributes.items() if item.attributes is mapping and item.attributes.items else item.attributes) %} {{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %}{% endfor %}{% endif %}>
64
- {%- if item.prefix is defined and item.prefix -%}<span class="ons-u-vh">{{- item.prefix -}}</span>{%- endif -%} {{- itemText | safe -}}
65
- {%- if item.target is defined and item.target == "_blank" -%}<span class="ons-u-vh">{{- item.screenreaderMessage | default("this link will open in a new tab") -}}</span>{%- endif -%}
63
+ <a href="{{ item.url }}" class="ons-list__link{% if item.variants == 'inPageLink' %} ons-js-inpagelink{% endif %}{% if item.classes %} {{ item.classes }}{% endif %}"{% if item.target %} target="{{ item.target }}"{% endif %}{% if item.attributes %}{% for attribute, value in (item.attributes.items() if item.attributes is mapping and item.attributes.items else item.attributes) %} {{ attribute }}{% if value %}="{{ value }}"{% endif %}{% endfor %}{% endif %}>
64
+ {%- if item.prefix -%}<span class="ons-u-vh">{{- item.prefix -}}</span>{%- endif -%} {{- itemText | safe -}}
65
+ {%- if item.target == "_blank" -%}<span class="ons-u-vh"> ({{- item.screenreaderMessage | default("opens in a new tab") -}})</span>{%- endif -%}
66
66
  </a>
67
67
  {%- endif -%}
68
68
  {%- else -%}
69
69
  {{- itemText | safe -}}
70
70
  {%- endif -%}
71
- {%- if item.suffix is defined and item.suffix or (params.iconPosition is defined and params.iconPosition == 'after') -%}
71
+ {%- if item.suffix or (params.iconPosition == 'after') -%}
72
72
  <span class="ons-list__suffix"{% if listEl != 'ol' %} aria-hidden="true"{% endif %}>
73
- {%- if item.suffix is defined and item.suffix -%}
73
+ {%- if item.suffix -%}
74
74
  {{- item.suffix -}}
75
- {%- elif (item.index is defined and item.index and listEl != 'ol') or (item.index is defined and item.index and listEl == 'ol' and 'bare' in variants) -%}
75
+ {%- elif (item.index and listEl != 'ol') or (item.index and listEl == 'ol' and 'bare' in variants) -%}
76
76
  {{- loop.index -}}
77
- {% elif params.iconPosition is defined and params.iconPosition == 'after' %}
77
+ {% elif params.iconPosition == 'after' %}
78
78
  {% from "components/icons/_macro.njk" import onsIcon %}
79
79
  {{
80
80
  onsIcon({
@@ -331,7 +331,7 @@ describe('macro: lists', () => {
331
331
  }),
332
332
  );
333
333
 
334
- expect($('.ons-list__link .ons-u-vh').text()).toBe('opens in a new tab');
334
+ expect($('.ons-list__link .ons-u-vh').text()).toBe(' (opens in a new tab)');
335
335
  });
336
336
 
337
337
  it('renders a default visually hidden screen reader message when target is "_blank"', () => {
@@ -347,7 +347,7 @@ describe('macro: lists', () => {
347
347
  }),
348
348
  );
349
349
 
350
- expect($('.ons-list__link .ons-u-vh').text()).toBe('this link will open in a new tab');
350
+ expect($('.ons-list__link .ons-u-vh').text()).toBe(' (opens in a new tab)');
351
351
  });
352
352
 
353
353
  it('has additionally provided `attributes`', () => {
@@ -1,21 +1,21 @@
1
1
  {%- macro onsMessage(params) -%}
2
2
  <div class="ons-message ons-message--{{ params.type }}">
3
3
  <div class="ons-message__head">
4
- <dl class="ons-message__metadata" {% if params.id is defined and params.id %} id="{{ params.id }}"{% endif %} {% if params.name is defined and params.name %} name="{{ params.name }}"{% endif %}>
4
+ <dl class="ons-message__metadata" {% if params.id %} id="{{ params.id }}"{% endif %} {% if params.name %} name="{{ params.name }}"{% endif %}>
5
5
  <div class="ons-message__sender">
6
6
  <dt class="ons-message__term ons-u-fw-b">{{ params.fromLabel }}:</dt>
7
- <dd class="ons-message__value ons-u-fw-b" {% if params.fromId is defined and params.fromId %} id="{{ params.fromId }}"{% endif %} {% if params.fromName is defined and params.fromName %} name="{{ params.fromName }}"{% endif %}>{{ params.fromValue }}</dd>
7
+ <dd class="ons-message__value ons-u-fw-b" {% if params.fromId %} id="{{ params.fromId }}"{% endif %} {% if params.fromName %} name="{{ params.fromName }}"{% endif %}>{{ params.fromValue }}</dd>
8
8
  </div>
9
9
  <div class="ons-message__timestamp">
10
10
  <dt class="ons-message__term ons-u-fs-s">{{ params.sentLabel }}:</dt>
11
- <dd class="ons-message__value ons-u-fs-s" {% if params.sentId is defined and params.sentId %} id="{{ params.sentId }}"{% endif %} {% if params.sentName is defined and params.sentName %} name="{{ params.sentName }}"{% endif %}>{{ params.sentValue }}</dd>
11
+ <dd class="ons-message__value ons-u-fs-s" {% if params.sentId %} id="{{ params.sentId }}"{% endif %} {% if params.sentName %} name="{{ params.sentName }}"{% endif %}>{{ params.sentValue }}</dd>
12
12
  </div>
13
13
  </dl>
14
- {% if params.unreadLink is defined and params.unreadLink %}
15
- <a class="ons-message__unread-link" {% if params.unreadLinkId is defined and params.unreadLinkId %}id="{{ params.unreadLinkId }}"{% endif %} href="{{ params.unreadLink }}">{{ params.unreadLinkText }}</a>
14
+ {% if params.unreadLink %}
15
+ <a class="ons-message__unread-link" {% if params.unreadLinkId %}id="{{ params.unreadLinkId }}"{% endif %} href="{{ params.unreadLink }}">{{ params.unreadLinkText }}</a>
16
16
  {% endif %}
17
17
  </div>
18
- <div class="ons-message__body" {% if params.messageID is defined and params.messageID %} id="{{ params.messageID }}"{% endif %} {% if params.messageName is defined and params.messageName %} name="{{ params.messageName }}"{% endif %}>
18
+ <div class="ons-message__body" {% if params.messageID %} id="{{ params.messageID }}"{% endif %} {% if params.messageName %} name="{{ params.messageName }}"{% endif %}>
19
19
  {{ caller() }}
20
20
  </div>
21
21
  </div>