@ons/design-system 50.0.1 → 53.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 (182) hide show
  1. package/README.md +35 -15
  2. package/components/access-code/_macro.njk +1 -1
  3. package/components/access-code/_macro.spec.js +162 -0
  4. package/components/access-code/uac.spec.js +26 -0
  5. package/components/accordion/_macro.spec.js +224 -0
  6. package/components/accordion/accordion.spec.js +134 -0
  7. package/components/address-input/_macro.njk +1 -1
  8. package/components/address-input/_macro.spec.js +465 -0
  9. package/components/address-input/autosuggest.address.js +5 -4
  10. package/components/address-input/autosuggest.address.setter.js +9 -3
  11. package/components/address-input/autosuggest.address.spec.js +733 -0
  12. package/components/address-output/_macro.njk +6 -6
  13. package/components/address-output/_macro.spec.js +122 -0
  14. package/components/autosuggest/_macro.njk +1 -1
  15. package/components/autosuggest/_macro.spec.js +229 -0
  16. package/components/autosuggest/autosuggest.helpers.js +2 -3
  17. package/components/autosuggest/autosuggest.helpers.spec.js +85 -0
  18. package/components/autosuggest/autosuggest.js +4 -2
  19. package/components/autosuggest/autosuggest.spec.js +625 -0
  20. package/components/autosuggest/autosuggest.ui.js +6 -2
  21. package/components/breadcrumbs/_macro.spec.js +129 -0
  22. package/components/button/_button.scss +75 -33
  23. package/components/button/_macro.njk +6 -6
  24. package/components/button/_macro.spec.js +446 -0
  25. package/components/button/button.spec.js +290 -0
  26. package/components/call-to-action/_macro.njk +3 -1
  27. package/components/call-to-action/_macro.spec.js +52 -0
  28. package/components/card/_macro.njk +26 -19
  29. package/components/card/_macro.spec.js +261 -0
  30. package/components/char-check-limit/_macro.spec.js +73 -0
  31. package/components/char-check-limit/character-check.spec.js +196 -0
  32. package/components/char-check-limit/character-limit.js +1 -1
  33. package/components/checkboxes/_checkbox-macro.spec.js +419 -0
  34. package/components/checkboxes/_macro.njk +1 -3
  35. package/components/checkboxes/_macro.spec.js +306 -0
  36. package/components/checkboxes/checkbox-with-autoselect.js +2 -1
  37. package/components/checkboxes/checkboxes.spec.js +208 -0
  38. package/components/code-highlight/_macro.spec.js +56 -0
  39. package/components/code-highlight/code-highlight.spec.js +18 -0
  40. package/components/collapsible/_macro.spec.js +204 -0
  41. package/components/collapsible/collapsible.js +2 -1
  42. package/components/collapsible/collapsible.spec.js +236 -0
  43. package/components/content-pagination/_macro.spec.js +199 -0
  44. package/components/cookies-banner/_macro.njk +1 -1
  45. package/components/cookies-banner/_macro.spec.js +171 -0
  46. package/components/cookies-banner/cookies-banner.spec.js +90 -0
  47. package/components/date-input/_macro.njk +6 -3
  48. package/components/date-input/_macro.spec.js +286 -0
  49. package/components/document-list/_macro.njk +3 -5
  50. package/components/document-list/_macro.spec.js +491 -0
  51. package/components/download-resources/download-resources.spec.js +540 -0
  52. package/components/duration/_macro.njk +7 -6
  53. package/components/duration/_macro.spec.js +251 -0
  54. package/components/error/_macro.spec.js +97 -0
  55. package/components/external-link/_macro.njk +5 -2
  56. package/components/external-link/_macro.spec.js +77 -0
  57. package/components/feedback/_macro.njk +5 -3
  58. package/components/feedback/_macro.spec.js +122 -0
  59. package/components/field/_macro.njk +2 -2
  60. package/components/field/_macro.spec.js +97 -0
  61. package/components/fieldset/_macro.njk +3 -3
  62. package/components/fieldset/_macro.spec.js +173 -0
  63. package/components/footer/_footer.scss +19 -4
  64. package/components/footer/_macro.njk +106 -137
  65. package/components/footer/_macro.spec.js +678 -0
  66. package/components/header/_header.scss +65 -46
  67. package/components/header/_macro.njk +173 -121
  68. package/components/header/_macro.spec.js +618 -0
  69. package/components/hero/_hero.scss +30 -143
  70. package/components/hero/_macro.njk +12 -23
  71. package/components/hero/_macro.spec.js +218 -0
  72. package/components/icons/_macro.njk +258 -30
  73. package/components/icons/_macro.spec.js +140 -0
  74. package/components/images/_macro.njk +1 -1
  75. package/components/images/_macro.spec.js +121 -0
  76. package/components/input/_input-type.scss +12 -5
  77. package/components/input/_input.scss +8 -0
  78. package/components/input/_macro.njk +4 -5
  79. package/components/input/_macro.spec.js +658 -0
  80. package/components/label/_macro.spec.js +189 -0
  81. package/components/language-selector/_macro.njk +1 -1
  82. package/components/language-selector/_macro.spec.js +137 -0
  83. package/components/lists/_list.scss +4 -0
  84. package/components/lists/_macro.njk +4 -7
  85. package/components/lists/_macro.spec.js +618 -0
  86. package/components/message/_macro.spec.js +137 -0
  87. package/components/message-list/_macro.njk +7 -7
  88. package/components/message-list/_macro.spec.js +159 -0
  89. package/components/metadata/_macro.spec.js +167 -0
  90. package/components/modal/_macro.njk +6 -6
  91. package/components/modal/_macro.spec.js +87 -0
  92. package/components/modal/modal.spec.js +59 -0
  93. package/components/mutually-exclusive/_macro.njk +2 -2
  94. package/components/mutually-exclusive/_macro.spec.js +184 -0
  95. package/components/mutually-exclusive/mutually-exclusive.checkboxes.spec.js +203 -0
  96. package/components/mutually-exclusive/mutually-exclusive.date.spec.js +142 -0
  97. package/components/mutually-exclusive/mutually-exclusive.duration.spec.js +141 -0
  98. package/components/mutually-exclusive/mutually-exclusive.email.spec.js +117 -0
  99. package/components/mutually-exclusive/mutually-exclusive.multiple-options.checkboxes.spec.js +213 -0
  100. package/components/mutually-exclusive/mutually-exclusive.number.spec.js +125 -0
  101. package/components/mutually-exclusive/mutually-exclusive.textarea.spec.js +131 -0
  102. package/components/navigation/_macro.njk +45 -38
  103. package/components/navigation/_macro.spec.js +329 -0
  104. package/components/navigation/_navigation.scss +20 -4
  105. package/components/navigation/navigation.dom.js +1 -1
  106. package/components/navigation/navigation.spec.js +232 -0
  107. package/components/pagination/_macro.njk +1 -1
  108. package/components/pagination/_macro.spec.js +411 -0
  109. package/components/panel/_macro.njk +6 -6
  110. package/components/panel/_macro.spec.js +423 -0
  111. package/components/password/_macro.spec.js +137 -0
  112. package/components/password/password.spec.js +40 -0
  113. package/components/phase-banner/_macro.spec.js +73 -0
  114. package/components/promotional-banner/_macro.spec.js +97 -0
  115. package/components/question/_macro.njk +16 -22
  116. package/components/question/_macro.spec.js +309 -0
  117. package/components/quote/_macro.spec.js +81 -0
  118. package/components/radios/_macro.njk +3 -6
  119. package/components/radios/_macro.spec.js +575 -0
  120. package/components/radios/radios.spec.js +180 -0
  121. package/components/related-content/_macro.njk +14 -21
  122. package/components/related-content/_macro.spec.js +133 -0
  123. package/components/related-content/_section-macro.njk +10 -0
  124. package/components/related-content/_section-macro.spec.js +43 -0
  125. package/components/relationships/_macro.spec.js +108 -0
  126. package/components/relationships/relationships.spec.js +84 -0
  127. package/components/reply/_macro.njk +2 -2
  128. package/components/reply/_macro.spec.js +69 -0
  129. package/components/reply/reply.spec.js +78 -0
  130. package/components/search/_macro.njk +14 -12
  131. package/components/search/_macro.spec.js +44 -0
  132. package/components/search/_search.scss +7 -7
  133. package/components/section-navigation/_macro.njk +7 -2
  134. package/components/section-navigation/_macro.spec.js +206 -0
  135. package/components/select/_macro.njk +3 -3
  136. package/components/select/_macro.spec.js +203 -0
  137. package/components/select/select.spec.js +56 -0
  138. package/components/share-page/_macro.njk +6 -4
  139. package/components/share-page/_macro.spec.js +110 -0
  140. package/components/skip-to-content/_macro.spec.js +57 -0
  141. package/components/skip-to-content/skip-to-content.spec.js +44 -0
  142. package/components/status/_macro.spec.js +77 -0
  143. package/components/summary/_macro.njk +5 -5
  144. package/components/summary/_macro.spec.js +472 -0
  145. package/components/table/_macro.njk +2 -2
  146. package/components/table/_macro.spec.js +557 -0
  147. package/components/table/table.spec.js +155 -0
  148. package/components/table-of-contents/_macro.njk +35 -35
  149. package/components/table-of-contents/_macro.spec.js +178 -0
  150. package/components/table-of-contents/toc.js +29 -25
  151. package/components/table-of-contents/toc.spec.js +61 -0
  152. package/components/tabs/_macro.njk +1 -1
  153. package/components/tabs/_macro.spec.js +79 -0
  154. package/components/tabs/tabs.spec.js +162 -0
  155. package/components/text-indent/_macro.spec.js +52 -0
  156. package/components/textarea/_macro.njk +5 -3
  157. package/components/textarea/_macro.spec.js +300 -0
  158. package/components/textarea/textarea.spec.js +98 -0
  159. package/components/timeline/_macro.njk +3 -3
  160. package/components/timeline/_macro.spec.js +81 -0
  161. package/components/timeout-modal/_macro.spec.js +68 -0
  162. package/components/timeout-modal/timeout-modal.spec.js +226 -0
  163. package/components/timeout-panel/_macro.njk +0 -1
  164. package/components/timeout-panel/_macro.spec.js +54 -0
  165. package/components/timeout-panel/timeout-panel.dom.js +1 -2
  166. package/components/timeout-panel/timeout-panel.spec.js +161 -0
  167. package/components/upload/_macro.spec.js +75 -0
  168. package/components/video/_macro.spec.js +34 -0
  169. package/css/census.css +3 -1
  170. package/css/ids.css +2 -0
  171. package/css/main.css +1 -1
  172. package/img/dummy-brand-logo.svg +1 -0
  173. package/js/cookies-settings.spec.js +154 -0
  174. package/layout/_template.njk +7 -4
  175. package/package.json +10 -23
  176. package/scripts/main.es5.js +2 -2
  177. package/scripts/main.js +1 -1
  178. package/scss/ids.scss +2 -0
  179. package/scss/settings/_census.scss +141 -0
  180. package/scss/settings/_ids.scss +48 -0
  181. package/scss/utilities/_margin.scss +1 -0
  182. package/scss/vars/_colors.scss +5 -2
@@ -0,0 +1,69 @@
1
+ /** @jest-environment jsdom */
2
+
3
+ import * as cheerio from 'cheerio';
4
+
5
+ import axe from '../../tests/helpers/axe';
6
+ import { renderComponent, templateFaker } from '../../tests/helpers/rendering';
7
+
8
+ const EXAMPLE_TEXTAREA = {
9
+ id: 'reply-textarea',
10
+ name: 'reply',
11
+ label: {
12
+ text: 'Reply',
13
+ },
14
+ };
15
+
16
+ const EXAMPLE_BUTTON = {
17
+ id: 'reply-button',
18
+ type: 'button',
19
+ text: 'Send message',
20
+ classes: 'u-mb-xs',
21
+ };
22
+
23
+ const EXAMPLE_REPLY = {
24
+ textarea: EXAMPLE_TEXTAREA,
25
+ button: EXAMPLE_BUTTON,
26
+ closeLinkText: 'Close conversation',
27
+ closeLinkUrl: '/close-conversation',
28
+ };
29
+
30
+ describe('macro: reply', () => {
31
+ it('passes jest-axe checks', async () => {
32
+ const $ = cheerio.load(renderComponent('reply', EXAMPLE_REPLY));
33
+
34
+ const results = await axe($.html());
35
+ expect(results).toHaveNoViolations();
36
+ });
37
+
38
+ it('renders the provided `textarea` using the `textarea` component', () => {
39
+ const faker = templateFaker();
40
+ const textareaSpy = faker.spy('textarea');
41
+
42
+ cheerio.load(faker.renderComponent('reply', EXAMPLE_REPLY));
43
+
44
+ expect(textareaSpy.occurrences[0]).toEqual(EXAMPLE_TEXTAREA);
45
+ });
46
+
47
+ it('renders the provided `button` using the `button` component', () => {
48
+ const faker = templateFaker();
49
+ const buttonSpy = faker.spy('button');
50
+
51
+ cheerio.load(faker.renderComponent('reply', EXAMPLE_REPLY));
52
+
53
+ expect(buttonSpy.occurrences[0]).toEqual(EXAMPLE_BUTTON);
54
+ });
55
+
56
+ it('has the expected hyperlink URL', async () => {
57
+ const $ = cheerio.load(renderComponent('reply', EXAMPLE_REPLY));
58
+
59
+ const $el = $('.ons-reply__link');
60
+ expect($el.attr('href')).toBe(EXAMPLE_REPLY.closeLinkUrl);
61
+ });
62
+
63
+ it('has the expected link text', async () => {
64
+ const $ = cheerio.load(renderComponent('reply', EXAMPLE_REPLY));
65
+
66
+ const $el = $('.ons-reply__link');
67
+ expect($el.text()).toBe(EXAMPLE_REPLY.closeLinkText);
68
+ });
69
+ });
@@ -0,0 +1,78 @@
1
+ import { renderComponent, setTestPage } from '../../tests/helpers/rendering';
2
+
3
+ const EXAMPLE_REPLY = {
4
+ textarea: {
5
+ id: 'reply-textarea',
6
+ name: 'reply',
7
+ label: {
8
+ text: 'Reply',
9
+ },
10
+ },
11
+ button: {
12
+ id: 'reply-button',
13
+ type: 'button',
14
+ text: 'Send message',
15
+ classes: 'u-mb-xs',
16
+ },
17
+ closeLinkText: 'Close conversation',
18
+ closeLinkUrl: '/close-conversation',
19
+ };
20
+
21
+ describe('script: reply', () => {
22
+ describe('scenario: Empty textarea', () => {
23
+ it('the button is disabled', async () => {
24
+ await setTestPage('/test', renderComponent('reply', EXAMPLE_REPLY));
25
+
26
+ const disabledButton = await page.evaluate(() => document.querySelector('#reply-button').disabled);
27
+ expect(disabledButton).toBe(true);
28
+ });
29
+
30
+ it('the button has classes applied', async () => {
31
+ await setTestPage('/test', renderComponent('reply', EXAMPLE_REPLY));
32
+ const disabledButton = await page.$eval('#reply-button', element => element.classList.contains('ons-btn--disabled'));
33
+ expect(disabledButton).toBe(true);
34
+ });
35
+ });
36
+
37
+ describe('scenario: Filled textarea', () => {
38
+ it('the button is enabled', async () => {
39
+ await setTestPage('/test', renderComponent('reply', EXAMPLE_REPLY));
40
+ await page.focus('#reply-textarea');
41
+ await page.keyboard.type('Sausages');
42
+
43
+ const disabledButton = await page.evaluate(() => document.querySelector('#reply-button').disabled);
44
+ expect(disabledButton).toBe(false);
45
+ });
46
+
47
+ it('the button has classes removed', async () => {
48
+ await setTestPage('/test', renderComponent('reply', EXAMPLE_REPLY));
49
+ await page.focus('#reply-textarea');
50
+ await page.keyboard.type('Sausages');
51
+
52
+ const disabledButton = await page.$eval('#reply-button', element => element.classList.contains('ons-btn--disabled'));
53
+ expect(disabledButton).toBe(false);
54
+ });
55
+ });
56
+
57
+ describe('scenario: Filled then emptied textarea', () => {
58
+ it('the button is disabled', async () => {
59
+ await setTestPage('/test', renderComponent('reply', EXAMPLE_REPLY));
60
+ await page.focus('#reply-textarea');
61
+ await page.keyboard.type('s');
62
+ await page.keyboard.press('Backspace');
63
+
64
+ const disabledButton = await page.evaluate(() => document.querySelector('#reply-button').disabled);
65
+ expect(disabledButton).toBe(true);
66
+ });
67
+
68
+ it('the button has classes applied', async () => {
69
+ await setTestPage('/test', renderComponent('reply', EXAMPLE_REPLY));
70
+ await page.focus('#reply-textarea');
71
+ await page.keyboard.type('s');
72
+ await page.keyboard.press('Backspace');
73
+
74
+ const disabledButton = await page.$eval('#reply-button', element => element.classList.contains('ons-btn--disabled'));
75
+ expect(disabledButton).toBe(true);
76
+ });
77
+ });
78
+ });
@@ -3,16 +3,18 @@
3
3
 
4
4
  {% set content = caller() %}
5
5
 
6
- {{ content | safe }}
7
- {{
8
- onsButton({
9
- "type": params.searchButton.type,
10
- "text": params.searchButton.text,
11
- "id": params.searchButton.id,
12
- "variants": 'small',
13
- "classes": 'ons-search__btn ons-u-mt-xs@xxs@s' + (" " + params.searchButton.classes if params.searchButton.classes else ""),
14
- "attributes": params.searchButton.attributes,
15
- "iconType": params.searchButton.iconType
16
- })
17
- }}
6
+ <div class="ons-search-component">
7
+ {{ content | safe }}
8
+ {{
9
+ onsButton({
10
+ "type": params.searchButton.type,
11
+ "text": params.searchButton.text,
12
+ "id": params.searchButton.id,
13
+ "variants": 'small',
14
+ "classes": 'ons-search__btn ons-u-mt-xs@xxs@s' + (" " + params.searchButton.classes if params.searchButton.classes else ""),
15
+ "attributes": params.searchButton.attributes,
16
+ "iconType": params.searchButton.iconType
17
+ })
18
+ }}
19
+ </div>
18
20
  {% endmacro %}
@@ -0,0 +1,44 @@
1
+ /** @jest-environment jsdom */
2
+
3
+ import * as cheerio from 'cheerio';
4
+
5
+ import axe from '../../tests/helpers/axe';
6
+ import { renderComponent, templateFaker } from '../../tests/helpers/rendering';
7
+
8
+ const EXAMPLE_SEARCH = {
9
+ searchButton: {
10
+ id: 'search-button-id',
11
+ type: 'button',
12
+ text: 'Search for address',
13
+ iconType: 'search',
14
+ classes: 'extra-search-button-class',
15
+ attributes: { a: 42 },
16
+ },
17
+ };
18
+
19
+ const FAKE_NESTED_CONTENT = '<span class="test--nested">Nested content...</span>';
20
+
21
+ describe('macro: search', () => {
22
+ it('renders expected nested content', () => {
23
+ const $ = cheerio.load(renderComponent('search', EXAMPLE_SEARCH, FAKE_NESTED_CONTENT));
24
+
25
+ expect($('.ons-search-component .test--nested').text()).toBe('Nested content...');
26
+ });
27
+
28
+ it('renders button component', () => {
29
+ const faker = templateFaker();
30
+ const buttonSpy = faker.spy('button');
31
+
32
+ faker.renderComponent('input', EXAMPLE_SEARCH);
33
+
34
+ expect(buttonSpy.occurrences[0]).toEqual({
35
+ id: 'search-button-id',
36
+ type: 'button',
37
+ text: 'Search for address',
38
+ variants: 'small',
39
+ classes: 'ons-search__btn ons-u-mt-xs@xxs@s extra-search-button-class',
40
+ attributes: EXAMPLE_SEARCH.searchButton.attributes,
41
+ iconType: 'search',
42
+ });
43
+ });
44
+ });
@@ -1,16 +1,16 @@
1
- .ons-search {
1
+ .ons-search-component {
2
+ display: flex;
2
3
  flex-flow: row wrap;
3
- margin-left: -0.5rem;
4
- margin-top: -0.5rem;
5
4
 
6
5
  @include mq(s) {
7
6
  flex-flow: row nowrap !important;
8
7
  }
9
- &__btn {
8
+ .ons-search__btn {
10
9
  margin-top: -3px;
11
10
  }
12
- &__btn,
13
- &__input {
14
- margin-left: 0.5rem;
11
+ .ons-search__btn {
12
+ @include mq(s) {
13
+ margin-left: 0.5rem;
14
+ }
15
15
  }
16
16
  }
@@ -2,8 +2,13 @@
2
2
  <nav class="ons-section-nav{% if params.variants is defined and params.variants == 'vertical' %} ons-section-nav--vertical{% endif %}" id="{{ params.id }}" aria-label="{{ params.ariaLabel | default("Section menu") }}">
3
3
  <ul class="ons-section-nav__list">
4
4
  {% for item in (params.itemsList if params.itemsList is iterable else params.itemsList.items()) %}
5
- <li class="ons-section-nav__item {{ item.classes }}{{ ' ons-section-nav__item--active' if (item.url == params.currentPath) or (item.url in params.currentPath) or (params.tabQuery == item.title|lower) }}">
6
- <a class="ons-section-nav__link" href="{{ item.url }}" {% if (item.url == params.currentPath) or (item.url in params.currentPath) or (params.tabQuery == item.title|lower) %} aria-current="location" {% endif %}>{{ item.title }}</a>
5
+ {% if (params.currentPath and (item.url == params.currentPath or item.url in params.currentPath)) or (params.tabQuery and params.tabQuery == item.title|lower) %}
6
+ {% set isCurrent = true %}
7
+ {% else %}
8
+ {% set isCurrent = false %}
9
+ {% endif %}
10
+ <li class="ons-section-nav__item{% if item.classes %} ' ' + {{ item.classes }}{% endif %}{% if isCurrent == true %} ons-section-nav__item--active{% endif %}">
11
+ <a class="ons-section-nav__link" href="{{ item.url }}"{% if isCurrent == true %} aria-current="location"{% endif %}>{{ item.title }}</a>
7
12
  {% if item.anchors is defined and item.anchors %}
8
13
  <ul class="ons-section-nav__sub-items ons-list ons-list--dashed ons-u-mt-xs ons-u-mb-xs">
9
14
  {% for anchor in item.anchors %}
@@ -0,0 +1,206 @@
1
+ /** @jest-environment jsdom */
2
+
3
+ import * as cheerio from 'cheerio';
4
+
5
+ import axe from '../../tests/helpers/axe';
6
+ import { mapAll } from '../../tests/helpers/cheerio';
7
+ import { renderComponent } from '../../tests/helpers/rendering';
8
+
9
+ const EXAMPLE_SECTION_NAVIGATION = {
10
+ id: 'section-menu',
11
+ currentPath: '/results',
12
+ itemsList: [
13
+ {
14
+ title: 'Results',
15
+ url: '/results',
16
+ },
17
+ {
18
+ title: 'Dashboard',
19
+ url: '/results/dashboard',
20
+ },
21
+ ],
22
+ };
23
+
24
+ const EXAMPLE_SECTION_NAVIGATION_VERTICAL = {
25
+ variants: 'vertical',
26
+ currentPath: '#section-2',
27
+ itemsList: [
28
+ {
29
+ title: 'Section 1',
30
+ url: '#section-1',
31
+ },
32
+ {
33
+ title: 'Section 2',
34
+ url: '#section-2',
35
+ anchors: [
36
+ {
37
+ title: 'Sub section 1',
38
+ url: '#sub-section-1',
39
+ },
40
+ {
41
+ title: 'Sub section 2',
42
+ url: '#sub-section-2',
43
+ },
44
+ {
45
+ title: 'Sub section 3',
46
+ url: '#sub-section-3',
47
+ },
48
+ ],
49
+ },
50
+ {
51
+ title: 'Section 3',
52
+ url: '#0',
53
+ },
54
+ ],
55
+ };
56
+
57
+ describe('macro: section-navigation', () => {
58
+ describe('variant: horizontal', () => {
59
+ it('passes jest-axe checks', async () => {
60
+ const $ = cheerio.load(renderComponent('section-navigation', EXAMPLE_SECTION_NAVIGATION));
61
+
62
+ const results = await axe($.html());
63
+ expect(results).toHaveNoViolations();
64
+ });
65
+
66
+ it('does not have the `vertical` modifier class', async () => {
67
+ const $ = cheerio.load(renderComponent('section-navigation', EXAMPLE_SECTION_NAVIGATION));
68
+
69
+ expect($('.ons-section-nav').hasClass('ons-section-nav--vertical')).toBe(false);
70
+ });
71
+ });
72
+
73
+ describe('variant: vertical', () => {
74
+ it('passes jest-axe checks', async () => {
75
+ const $ = cheerio.load(renderComponent('section-navigation', EXAMPLE_SECTION_NAVIGATION_VERTICAL));
76
+
77
+ const results = await axe($.html());
78
+ expect(results).toHaveNoViolations();
79
+ });
80
+
81
+ it('has the `vertical` modifier class', async () => {
82
+ const $ = cheerio.load(renderComponent('section-navigation', EXAMPLE_SECTION_NAVIGATION_VERTICAL));
83
+
84
+ expect($('.ons-section-nav').hasClass('ons-section-nav--vertical')).toBe(true);
85
+ });
86
+ });
87
+
88
+ it('has the provided `id` attribute', () => {
89
+ const $ = cheerio.load(renderComponent('section-navigation', EXAMPLE_SECTION_NAVIGATION));
90
+
91
+ expect($('.ons-section-nav').attr('id')).toBe('section-menu');
92
+ });
93
+
94
+ it('has the provided `ariaLabel` parameter', () => {
95
+ const $ = cheerio.load(
96
+ renderComponent('section-navigation', {
97
+ ...EXAMPLE_SECTION_NAVIGATION,
98
+ ariaLabel: 'Section navigation menu',
99
+ }),
100
+ );
101
+
102
+ expect($('.ons-section-nav').attr('aria-label')).toBe('Section navigation menu');
103
+ });
104
+
105
+ it('assumes a default `ariaLabel` of "Section menu"', () => {
106
+ const $ = cheerio.load(renderComponent('section-navigation', EXAMPLE_SECTION_NAVIGATION));
107
+
108
+ expect($('.ons-section-nav').attr('aria-label')).toBe('Section menu');
109
+ });
110
+
111
+ describe('navigation items', () => {
112
+ it('renders top level navigation items', () => {
113
+ const $ = cheerio.load(renderComponent('section-navigation', EXAMPLE_SECTION_NAVIGATION));
114
+
115
+ const itemLabels = mapAll($('.ons-section-nav__item .ons-section-nav__link'), node => node.text().trim());
116
+ expect(itemLabels).toEqual(['Results', 'Dashboard']);
117
+
118
+ const itemLinks = mapAll($('.ons-section-nav__item .ons-section-nav__link'), node => node.attr('href'));
119
+ expect(itemLinks).toEqual(['/results', '/results/dashboard']);
120
+ });
121
+
122
+ it('has additionally provided style classes', () => {
123
+ const $ = cheerio.load(
124
+ renderComponent('section-navigation', {
125
+ currentPath: '#section-1',
126
+ itemsList: [
127
+ {
128
+ classes: 'extra-class another-extra-class',
129
+ title: 'Section 1',
130
+ url: '#section-1',
131
+ },
132
+ ],
133
+ }),
134
+ );
135
+
136
+ expect($('.ons-section-nav__item').hasClass('extra-class')).toBe(true);
137
+ expect($('.ons-section-nav__item').hasClass('another-extra-class')).toBe(true);
138
+ });
139
+
140
+ it('marks the current item with a class when `currentPath` is provided', () => {
141
+ const $ = cheerio.load(renderComponent('section-navigation', EXAMPLE_SECTION_NAVIGATION));
142
+
143
+ expect(
144
+ $('.ons-section-nav__item--active')
145
+ .text()
146
+ .trim(),
147
+ ).toBe('Results');
148
+ });
149
+
150
+ it('marks the current item with a class when `tabQuery` is provided', () => {
151
+ const $ = cheerio.load(
152
+ renderComponent('section-navigation', {
153
+ ...EXAMPLE_SECTION_NAVIGATION,
154
+ currentPath: undefined,
155
+ tabQuery: 'dashboard',
156
+ }),
157
+ );
158
+
159
+ expect(
160
+ $('.ons-section-nav__item--active')
161
+ .text()
162
+ .trim(),
163
+ ).toBe('Dashboard');
164
+ });
165
+
166
+ it('marks the current item with `aria-current` when `currentPath` is provided', () => {
167
+ const $ = cheerio.load(renderComponent('section-navigation', EXAMPLE_SECTION_NAVIGATION));
168
+
169
+ expect($('.ons-section-nav__item--active .ons-section-nav__link').attr('aria-current')).toBe('location');
170
+ });
171
+
172
+ it('marks the current item with `aria-current` when `tabQuery` is provided', () => {
173
+ const $ = cheerio.load(
174
+ renderComponent('section-navigation', {
175
+ ...EXAMPLE_SECTION_NAVIGATION,
176
+ currentPath: undefined,
177
+ tabQuery: 'dashboard',
178
+ }),
179
+ );
180
+
181
+ expect($('.ons-section-nav__item--active .ons-section-nav__link').attr('aria-current')).toBe('location');
182
+ });
183
+
184
+ describe('nested anchor navigation items', () => {
185
+ it('renders anchor navigation list for top-level items that define `anchors`', () => {
186
+ const $ = cheerio.load(renderComponent('section-navigation', EXAMPLE_SECTION_NAVIGATION_VERTICAL));
187
+
188
+ expect($('.ons-section-nav__list > .ons-section-nav__item:nth-child(1) .ons-section-nav__sub-items').length).toBe(0);
189
+ expect($('.ons-section-nav__list > .ons-section-nav__item:nth-child(2) .ons-section-nav__sub-items').length).toBe(1);
190
+ expect($('.ons-section-nav__list > .ons-section-nav__item:nth-child(3) .ons-section-nav__sub-items').length).toBe(0);
191
+ });
192
+
193
+ it('renders the expected anchor navigation items', () => {
194
+ const $ = cheerio.load(renderComponent('section-navigation', EXAMPLE_SECTION_NAVIGATION_VERTICAL));
195
+
196
+ const itemLabels = mapAll($('.ons-section-nav__sub-items .ons-section-nav__item .ons-section-nav__link'), node =>
197
+ node.text().trim(),
198
+ );
199
+ expect(itemLabels).toEqual(['Sub section 1', 'Sub section 2', 'Sub section 3']);
200
+
201
+ const itemLinks = mapAll($('.ons-section-nav__sub-items .ons-section-nav__item .ons-section-nav__link'), node => node.attr('href'));
202
+ expect(itemLinks).toEqual(['#sub-section-1', '#sub-section-2', '#sub-section-3']);
203
+ });
204
+ });
205
+ });
206
+ });
@@ -4,7 +4,7 @@
4
4
 
5
5
  {% call onsField({
6
6
  "id": params.fieldId,
7
- "classes": params.fieldClases,
7
+ "classes": params.fieldClasses,
8
8
  "legendClasses": params.legendClasses,
9
9
  "dontWrap": params.dontWrap,
10
10
  "error": params.error,
@@ -21,13 +21,13 @@
21
21
  <select
22
22
  id="{{ params.id }}"
23
23
  name="{{ params.name }}"
24
- class="ons-input ons-input--select{% if params.classes %} {{ params.classes }}{% endif %}{% if params.select and params.select.error %} ons-input--error{% endif %}"
24
+ class="ons-input ons-input--select{% if params.classes %} {{ params.classes }}{% endif %}{% if params.error is defined and params.error %} ons-input--error{% endif %}"
25
25
  {% if params.value is defined and params.value %}value="{{ params.value}}" {% endif %}
26
26
  {% 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 %}
27
27
  >
28
28
  {% for option in params.options %}
29
29
  <option value="{{ option.value | default(option.text) }}"
30
- {% if option.id is defined and option.id %}"id"="{{ option.id }}" {% endif %}
30
+ {% if option.id is defined and option.id %}id="{{ option.id }}" {% endif %}
31
31
  {% if option.selected is defined and option.selected %}selected {% endif %}
32
32
  {% if option.disabled is defined and option.disabled %}disabled {% endif %}
33
33
  {% if params.attributes is defined and params.attributes %}{% for attribute, value in option.attributes %}{{ attribute }}="{{ value }}"{% endfor %}{% endif %}