@ons/design-system 50.0.0 → 52.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 (166) 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/_macro.njk +5 -5
  23. package/components/button/_macro.spec.js +446 -0
  24. package/components/button/button.spec.js +290 -0
  25. package/components/call-to-action/_macro.njk +3 -1
  26. package/components/call-to-action/_macro.spec.js +52 -0
  27. package/components/card/_macro.njk +26 -19
  28. package/components/card/_macro.spec.js +261 -0
  29. package/components/char-check-limit/_macro.spec.js +73 -0
  30. package/components/char-check-limit/character-check.spec.js +196 -0
  31. package/components/char-check-limit/character-limit.js +1 -1
  32. package/components/checkboxes/_checkbox-macro.spec.js +419 -0
  33. package/components/checkboxes/_macro.njk +1 -3
  34. package/components/checkboxes/_macro.spec.js +306 -0
  35. package/components/checkboxes/checkbox-with-autoselect.js +2 -1
  36. package/components/checkboxes/checkboxes.spec.js +208 -0
  37. package/components/code-highlight/_macro.spec.js +56 -0
  38. package/components/code-highlight/code-highlight.spec.js +18 -0
  39. package/components/collapsible/_macro.spec.js +204 -0
  40. package/components/collapsible/collapsible.js +2 -1
  41. package/components/collapsible/collapsible.spec.js +236 -0
  42. package/components/content-pagination/_macro.spec.js +199 -0
  43. package/components/cookies-banner/_macro.njk +1 -1
  44. package/components/cookies-banner/_macro.spec.js +171 -0
  45. package/components/cookies-banner/cookies-banner.spec.js +90 -0
  46. package/components/date-input/_macro.njk +6 -3
  47. package/components/date-input/_macro.spec.js +286 -0
  48. package/components/document-list/_macro.njk +3 -5
  49. package/components/document-list/_macro.spec.js +491 -0
  50. package/components/download-resources/download-resources.spec.js +540 -0
  51. package/components/duration/_macro.njk +7 -6
  52. package/components/duration/_macro.spec.js +251 -0
  53. package/components/error/_macro.spec.js +97 -0
  54. package/components/external-link/_macro.spec.js +60 -0
  55. package/components/feedback/_macro.njk +5 -3
  56. package/components/feedback/_macro.spec.js +122 -0
  57. package/components/field/_macro.njk +2 -2
  58. package/components/field/_macro.spec.js +97 -0
  59. package/components/fieldset/_macro.njk +3 -3
  60. package/components/fieldset/_macro.spec.js +173 -0
  61. package/components/footer/_macro.njk +12 -49
  62. package/components/footer/_macro.spec.js +549 -0
  63. package/components/header/_macro.njk +3 -3
  64. package/components/header/_macro.spec.js +562 -0
  65. package/components/hero/_hero.scss +0 -3
  66. package/components/hero/_macro.njk +4 -4
  67. package/components/hero/_macro.spec.js +224 -0
  68. package/components/icons/_macro.njk +15 -15
  69. package/components/icons/_macro.spec.js +140 -0
  70. package/components/images/_macro.njk +1 -1
  71. package/components/images/_macro.spec.js +121 -0
  72. package/components/input/_input-type.scss +12 -5
  73. package/components/input/_macro.njk +4 -5
  74. package/components/input/_macro.spec.js +658 -0
  75. package/components/label/_macro.spec.js +189 -0
  76. package/components/language-selector/_macro.spec.js +129 -0
  77. package/components/lists/_list.scss +4 -0
  78. package/components/lists/_macro.njk +4 -7
  79. package/components/lists/_macro.spec.js +618 -0
  80. package/components/message/_macro.spec.js +137 -0
  81. package/components/message-list/_macro.njk +7 -7
  82. package/components/message-list/_macro.spec.js +159 -0
  83. package/components/metadata/_macro.spec.js +167 -0
  84. package/components/modal/_macro.njk +6 -6
  85. package/components/modal/_macro.spec.js +87 -0
  86. package/components/modal/modal.js +0 -16
  87. package/components/modal/modal.spec.js +59 -0
  88. package/components/mutually-exclusive/_macro.njk +2 -2
  89. package/components/mutually-exclusive/_macro.spec.js +184 -0
  90. package/components/mutually-exclusive/mutually-exclusive.checkboxes.spec.js +203 -0
  91. package/components/mutually-exclusive/mutually-exclusive.date.spec.js +142 -0
  92. package/components/mutually-exclusive/mutually-exclusive.duration.spec.js +141 -0
  93. package/components/mutually-exclusive/mutually-exclusive.email.spec.js +117 -0
  94. package/components/mutually-exclusive/mutually-exclusive.multiple-options.checkboxes.spec.js +213 -0
  95. package/components/mutually-exclusive/mutually-exclusive.number.spec.js +125 -0
  96. package/components/mutually-exclusive/mutually-exclusive.textarea.spec.js +131 -0
  97. package/components/navigation/_macro.njk +6 -6
  98. package/components/navigation/_macro.spec.js +327 -0
  99. package/components/navigation/navigation.dom.js +1 -1
  100. package/components/navigation/navigation.spec.js +232 -0
  101. package/components/pagination/_macro.njk +1 -1
  102. package/components/pagination/_macro.spec.js +411 -0
  103. package/components/panel/_macro.njk +6 -6
  104. package/components/panel/_macro.spec.js +423 -0
  105. package/components/password/_macro.spec.js +137 -0
  106. package/components/password/password.spec.js +40 -0
  107. package/components/phase-banner/_macro.spec.js +73 -0
  108. package/components/promotional-banner/_macro.spec.js +97 -0
  109. package/components/question/_macro.njk +16 -22
  110. package/components/question/_macro.spec.js +309 -0
  111. package/components/quote/_macro.spec.js +81 -0
  112. package/components/radios/_macro.njk +3 -6
  113. package/components/radios/_macro.spec.js +575 -0
  114. package/components/radios/radios.spec.js +180 -0
  115. package/components/related-content/_macro.njk +1 -0
  116. package/components/related-content/_macro.spec.js +142 -0
  117. package/components/relationships/_macro.spec.js +108 -0
  118. package/components/relationships/relationships.spec.js +84 -0
  119. package/components/reply/_macro.njk +2 -2
  120. package/components/reply/_macro.spec.js +69 -0
  121. package/components/reply/reply.spec.js +78 -0
  122. package/components/search/_macro.njk +14 -12
  123. package/components/search/_macro.spec.js +44 -0
  124. package/components/search/_search.scss +7 -7
  125. package/components/section-navigation/_macro.njk +7 -2
  126. package/components/section-navigation/_macro.spec.js +206 -0
  127. package/components/select/_macro.njk +3 -3
  128. package/components/select/_macro.spec.js +203 -0
  129. package/components/select/select.spec.js +56 -0
  130. package/components/share-page/_macro.njk +2 -2
  131. package/components/share-page/_macro.spec.js +110 -0
  132. package/components/skip-to-content/_macro.spec.js +57 -0
  133. package/components/skip-to-content/skip-to-content.spec.js +44 -0
  134. package/components/status/_macro.spec.js +77 -0
  135. package/components/summary/_macro.njk +5 -5
  136. package/components/summary/_macro.spec.js +472 -0
  137. package/components/table/_macro.njk +2 -2
  138. package/components/table/_macro.spec.js +557 -0
  139. package/components/table/table.spec.js +155 -0
  140. package/components/table-of-contents/_macro.njk +35 -35
  141. package/components/table-of-contents/_macro.spec.js +178 -0
  142. package/components/table-of-contents/toc.js +29 -25
  143. package/components/table-of-contents/toc.spec.js +61 -0
  144. package/components/tabs/_macro.njk +1 -1
  145. package/components/tabs/_macro.spec.js +79 -0
  146. package/components/tabs/tabs.spec.js +162 -0
  147. package/components/text-indent/_macro.spec.js +52 -0
  148. package/components/textarea/_macro.njk +5 -3
  149. package/components/textarea/_macro.spec.js +300 -0
  150. package/components/textarea/textarea.spec.js +98 -0
  151. package/components/timeline/_macro.njk +3 -3
  152. package/components/timeline/_macro.spec.js +81 -0
  153. package/components/timeout-modal/_macro.spec.js +68 -0
  154. package/components/timeout-modal/timeout-modal.spec.js +226 -0
  155. package/components/timeout-panel/_macro.njk +0 -1
  156. package/components/timeout-panel/_macro.spec.js +54 -0
  157. package/components/timeout-panel/timeout-panel.dom.js +1 -2
  158. package/components/timeout-panel/timeout-panel.spec.js +161 -0
  159. package/components/upload/_macro.spec.js +75 -0
  160. package/components/video/_macro.spec.js +34 -0
  161. package/css/census.css +1 -1
  162. package/css/main.css +1 -1
  163. package/js/cookies-settings.spec.js +154 -0
  164. package/package.json +10 -23
  165. package/scripts/main.es5.js +1 -1
  166. package/scripts/main.js +2 -2
@@ -2,22 +2,22 @@
2
2
  <div class="ons-address-output{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}">
3
3
  <p class="ons-address-output__lines">
4
4
  {% if params.unit is defined and params.unit %}
5
- {{ params.unit }}<br>
5
+ <span class="ons-address-output__unit">{{ params.unit }}</span><br>
6
6
  {% endif %}
7
7
  {% if params.organisation is defined and params.organisation %}
8
- {{ params.organisation }}<br>
8
+ <span class="ons-address-output__organisation">{{ params.organisation }}</span><br>
9
9
  {% endif %}
10
10
  {% if params.line1 is defined and params.line1 %}
11
- {{ params.line1 }}<br>
11
+ <span class="ons-address-output__line1">{{ params.line1 }}</span><br>
12
12
  {% endif %}
13
13
  {% if params.line2 is defined and params.line2 %}
14
- {{ params.line2 }}<br>
14
+ <span class="ons-address-output__line2">{{ params.line2 }}</span><br>
15
15
  {% endif %}
16
16
  {% if params.town is defined and params.town %}
17
- {{ params.town }}<br>
17
+ <span class="ons-address-output__town">{{ params.town }}</span><br>
18
18
  {% endif %}
19
19
  {% if params.postcode is defined and params.postcode %}
20
- {{ params.postcode }}
20
+ <span class="ons-address-output__postcode">{{ params.postcode }}</span>
21
21
  {% endif %}
22
22
  </p>
23
23
  </div>
@@ -0,0 +1,122 @@
1
+ /** @jest-environment jsdom */
2
+
3
+ import * as cheerio from 'cheerio';
4
+
5
+ import axe from '../../tests/helpers/axe';
6
+ import { renderComponent } from '../../tests/helpers/rendering';
7
+
8
+ const EXAMPLE_ADDRESS_OUTPUT_FULL = {
9
+ unit: 'Unit 5',
10
+ organisation: 'Trescos',
11
+ line1: 'Abingdon Road',
12
+ line2: 'Goathill',
13
+ town: 'Barry',
14
+ postcode: 'AB12 6UH',
15
+ };
16
+
17
+ const EXAMPLE_ADDRESS_OUTPUT_NONE = {};
18
+
19
+ describe('macro: address-output', () => {
20
+ it('passes jest-axe checks', async () => {
21
+ const $ = cheerio.load(renderComponent('address-output', EXAMPLE_ADDRESS_OUTPUT_FULL));
22
+
23
+ const results = await axe($.html());
24
+ expect(results).toHaveNoViolations();
25
+ });
26
+
27
+ it('has additionally provided container style classes', () => {
28
+ const $ = cheerio.load(
29
+ renderComponent('address-output', {
30
+ ...EXAMPLE_ADDRESS_OUTPUT_FULL,
31
+ classes: 'extra-class another-extra-class',
32
+ }),
33
+ );
34
+
35
+ expect($('.ons-address-output').hasClass('extra-class')).toBe(true);
36
+ expect($('.ons-address-output').hasClass('another-extra-class')).toBe(true);
37
+ });
38
+
39
+ it('renders no lines when no parameters are provided', () => {
40
+ const $ = cheerio.load(renderComponent('address-output', EXAMPLE_ADDRESS_OUTPUT_NONE));
41
+
42
+ expect($('.ons-address-output__lines *').length).toBe(0);
43
+ });
44
+
45
+ it.each([
46
+ ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL],
47
+ ['single line', { unit: 'Unit 5' }],
48
+ ])('renders `unit` with %s', (_, params) => {
49
+ const $ = cheerio.load(renderComponent('address-output', params));
50
+
51
+ expect(
52
+ $('.ons-address-output__unit')
53
+ .text()
54
+ .trim(),
55
+ ).toBe('Unit 5');
56
+ });
57
+
58
+ it.each([
59
+ ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL],
60
+ ['single line', { organisation: 'Trescos' }],
61
+ ])('renders `organisation` with %s', (_, params) => {
62
+ const $ = cheerio.load(renderComponent('address-output', params));
63
+
64
+ expect(
65
+ $('.ons-address-output__organisation')
66
+ .text()
67
+ .trim(),
68
+ ).toBe('Trescos');
69
+ });
70
+
71
+ it.each([
72
+ ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL],
73
+ ['single line', { line1: 'Abingdon Road' }],
74
+ ])('renders `line1` with %s', (_, params) => {
75
+ const $ = cheerio.load(renderComponent('address-output', params));
76
+
77
+ expect(
78
+ $('.ons-address-output__line1')
79
+ .text()
80
+ .trim(),
81
+ ).toBe('Abingdon Road');
82
+ });
83
+
84
+ it.each([
85
+ ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL],
86
+ ['single line', { line2: 'Goathill' }],
87
+ ])('renders `line2` with %s', (_, params) => {
88
+ const $ = cheerio.load(renderComponent('address-output', params));
89
+
90
+ expect(
91
+ $('.ons-address-output__line2')
92
+ .text()
93
+ .trim(),
94
+ ).toBe('Goathill');
95
+ });
96
+
97
+ it.each([
98
+ ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL],
99
+ ['single line', { town: 'Barry' }],
100
+ ])('renders `town` with %s', (_, params) => {
101
+ const $ = cheerio.load(renderComponent('address-output', params));
102
+
103
+ expect(
104
+ $('.ons-address-output__town')
105
+ .text()
106
+ .trim(),
107
+ ).toBe('Barry');
108
+ });
109
+
110
+ it.each([
111
+ ['all address lines', EXAMPLE_ADDRESS_OUTPUT_FULL],
112
+ ['single line', { postcode: 'AB12 6UH' }],
113
+ ])('renders `postcode` with %s', (_, params) => {
114
+ const $ = cheerio.load(renderComponent('address-output', params));
115
+
116
+ expect(
117
+ $('.ons-address-output__postcode')
118
+ .text()
119
+ .trim(),
120
+ ).toBe('AB12 6UH');
121
+ });
122
+ });
@@ -38,7 +38,7 @@
38
38
  {% set autosuggestResults %}
39
39
  <div class="ons-autosuggest-input__results ons-js-autosuggest-results">
40
40
  <header id="{{ params.resultsTitleId }}" class="ons-autosuggest-input__results-title ons-u-fs-s">{{ params.resultsTitle }}</header>
41
- <ul class="ons-autosuggest-input__listbox ons-js-autosuggest-listbox" role="listbox" id="{{ params.id }}-listbox" aria-labelledby="{{ params.resultsTitleId }}" tabindex="-1"></ul>
41
+ <ul class="ons-autosuggest-input__listbox ons-js-autosuggest-listbox" title="{{ params.resultsTitle }}" role="listbox" id="{{ params.id }}-listbox" tabindex="-1"></ul>
42
42
  </div>
43
43
  <div class="ons-autosuggest-input__instructions ons-u-vh ons-js-autosuggest-instructions" id="{{ params.id }}-instructions" tabindex="-1">{{ params.instructions }}</div>
44
44
  <div class="ons-autosuggest-input__status ons-u-vh ons-js-autosuggest-aria-status" aria-live="assertive" role="status" tabindex="-1"></div>
@@ -0,0 +1,229 @@
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_AUTOSUGGEST = {
9
+ id: 'country-of-birth',
10
+ label: {
11
+ text: 'Current name of country',
12
+ description: 'Enter your own answer or select from suggestions',
13
+ id: 'country-of-birth-label',
14
+ classes: 'extra-label-class',
15
+ },
16
+ autocomplete: 'off',
17
+ instructions: 'Use up and down keys to navigate.',
18
+ ariaYouHaveSelected: 'You have selected',
19
+ ariaMinChars: 'Enter 3 or more characters for suggestions.',
20
+ minChars: 2,
21
+ ariaResultsLabel: 'Country suggestions',
22
+ ariaOneResult: 'There is one suggestion available.',
23
+ ariaNResults: 'There are {n} suggestions available.',
24
+ ariaLimitedResults: 'Type more characters to improve your search',
25
+ moreResults: 'Continue entering to improve suggestions',
26
+ resultsTitle: 'Suggestions',
27
+ resultsTitleId: 'country-of-birth-suggestions',
28
+ autosuggestData: '/examples/data/country-of-birth.json',
29
+ noResults: 'No suggestions found. You can enter your own answer',
30
+ typeMore: 'Continue entering to get suggestions',
31
+ };
32
+
33
+ describe('macro: autosuggest', () => {
34
+ it('passes jest-axe checks', async () => {
35
+ const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
36
+
37
+ const results = await axe($.html());
38
+ expect(results).toHaveNoViolations();
39
+ });
40
+
41
+ it('has expected id on container element', () => {
42
+ const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
43
+
44
+ expect($('.ons-autosuggest-input').attr('id')).toBe('country-of-birth-container');
45
+ });
46
+
47
+ it('has the provided data attributes', () => {
48
+ const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
49
+
50
+ const $element = $('.ons-autosuggest-input');
51
+ expect($element.attr('data-allow-multiple')).toBeUndefined();
52
+ expect($element.attr('data-min-chars')).toBe('2');
53
+ expect($element.attr('data-aria-limited-results')).toBe('Type more characters to improve your search');
54
+ expect($element.attr('data-aria-min-chars')).toBe('Enter 3 or more characters for suggestions.');
55
+ expect($element.attr('data-aria-n-results')).toBe('There are {n} suggestions available.');
56
+ expect($element.attr('data-aria-one-result')).toBe('There is one suggestion available.');
57
+ expect($element.attr('data-aria-you-have-selected')).toBe('You have selected');
58
+ expect($element.attr('data-autosuggest-data')).toBe('/examples/data/country-of-birth.json');
59
+ expect($element.attr('data-instructions')).toBe('Use up and down keys to navigate.');
60
+ expect($element.attr('data-more-results')).toBe('Continue entering to improve suggestions');
61
+ expect($element.attr('data-no-results')).toBe('No suggestions found. You can enter your own answer');
62
+ expect($element.attr('data-results-title')).toBe('Suggestions');
63
+ expect($element.attr('data-type-more')).toBe('Continue entering to get suggestions');
64
+ });
65
+
66
+ it('has the `data-allow-multiple` attribute when `allowMultiple` is `true`', () => {
67
+ const $ = cheerio.load(
68
+ renderComponent('autosuggest', {
69
+ ...EXAMPLE_AUTOSUGGEST,
70
+ allowMultiple: true,
71
+ }),
72
+ );
73
+
74
+ expect($('.ons-autosuggest-input').attr('data-allow-multiple')).toBe('true');
75
+ });
76
+
77
+ it('has a special class that indicates the component should initialise itself', () => {
78
+ const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
79
+
80
+ expect($('.ons-autosuggest-input').hasClass('ons-js-autosuggest')).toBe(true);
81
+ });
82
+
83
+ it('does not have a special class when the component has an external initialiser', () => {
84
+ const $ = cheerio.load(
85
+ renderComponent('autosuggest', {
86
+ ...EXAMPLE_AUTOSUGGEST,
87
+ externalInitialiser: true,
88
+ }),
89
+ );
90
+
91
+ expect($('.ons-autosuggest-input').hasClass('ons-js-autosuggest')).toBe(false);
92
+ });
93
+
94
+ it('has special class to indicate that component is not editable', () => {
95
+ const $ = cheerio.load(
96
+ renderComponent('autosuggest', {
97
+ ...EXAMPLE_AUTOSUGGEST,
98
+ isEditable: false,
99
+ }),
100
+ );
101
+
102
+ expect($('.ons-autosuggest-input').hasClass('ons-js-address-not-editable')).toBe(true);
103
+ });
104
+
105
+ it('has special class to indicate that component input is mandatory', () => {
106
+ const $ = cheerio.load(
107
+ renderComponent('autosuggest', {
108
+ ...EXAMPLE_AUTOSUGGEST,
109
+ mandatory: true,
110
+ }),
111
+ );
112
+
113
+ expect($('.ons-autosuggest-input').hasClass('ons-js-address-mandatory')).toBe(true);
114
+ });
115
+
116
+ it('has additionally provided container style classes', () => {
117
+ const $ = cheerio.load(
118
+ renderComponent('autosuggest', {
119
+ ...EXAMPLE_AUTOSUGGEST,
120
+ containerClasses: 'extra-class another-extra-class',
121
+ }),
122
+ );
123
+
124
+ expect($('.ons-autosuggest-input').hasClass('extra-class')).toBe(true);
125
+ expect($('.ons-autosuggest-input').hasClass('another-extra-class')).toBe(true);
126
+ });
127
+
128
+ describe('input', () => {
129
+ it('uses the `input` component with the expected parameters', () => {
130
+ const faker = templateFaker();
131
+ const inputSpy = faker.spy('input');
132
+
133
+ faker.renderComponent('autosuggest', {
134
+ ...EXAMPLE_AUTOSUGGEST,
135
+ classes: 'extra-class another-extra-class',
136
+ width: '7',
137
+ value: 'abc',
138
+ attributes: {
139
+ a: 42,
140
+ },
141
+ error: {
142
+ id: 'error-id',
143
+ text: 'An error occurred.',
144
+ },
145
+ mutuallyExclusive: null,
146
+ accessiblePlaceholder: true,
147
+ });
148
+
149
+ expect(inputSpy.occurrences[0]).toHaveProperty('id', 'country-of-birth');
150
+ expect(inputSpy.occurrences[0]).toHaveProperty('classes', 'ons-js-autosuggest-input extra-class another-extra-class');
151
+ expect(inputSpy.occurrences[0]).toHaveProperty('width', '7');
152
+ expect(inputSpy.occurrences[0]).toHaveProperty('label.text', 'Current name of country');
153
+ expect(inputSpy.occurrences[0]).toHaveProperty('label.description', 'Enter your own answer or select from suggestions');
154
+ expect(inputSpy.occurrences[0]).toHaveProperty('label.id', 'country-of-birth-label');
155
+ expect(inputSpy.occurrences[0]).toHaveProperty('label.classes', 'extra-label-class');
156
+ expect(inputSpy.occurrences[0]).toHaveProperty('autocomplete', 'off');
157
+ expect(inputSpy.occurrences[0]).toHaveProperty('value', 'abc');
158
+ expect(inputSpy.occurrences[0]).toHaveProperty('attributes.a', 42);
159
+ expect(inputSpy.occurrences[0]).toHaveProperty('error.id', 'error-id');
160
+ expect(inputSpy.occurrences[0]).toHaveProperty('error.text', 'An error occurred.');
161
+ expect(inputSpy.occurrences[0]).toHaveProperty('mutuallyExclusive', null);
162
+ expect(inputSpy.occurrences[0]).toHaveProperty('accessiblePlaceholder', true);
163
+ expect(typeof inputSpy.occurrences[0].autosuggestResults).toBe('string');
164
+ });
165
+ });
166
+
167
+ describe('autosuggest results', () => {
168
+ it('is rendered `mutallyExclusive` parameter is not defined', () => {
169
+ const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
170
+
171
+ expect($('.ons-autosuggest-input__results').length).toBe(1);
172
+ });
173
+
174
+ it('is not rendered when `mutallyExclusive` parameter is defined', () => {
175
+ const $ = cheerio.load(
176
+ renderComponent('autosuggest', {
177
+ ...EXAMPLE_AUTOSUGGEST,
178
+ mutuallyExclusive: { fakeParam: true },
179
+ }),
180
+ );
181
+
182
+ expect($('.ons-autosuggest-input__results').length).toBe(0);
183
+ });
184
+
185
+ it('renders header with the provided title identifier', () => {
186
+ const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
187
+
188
+ expect($('.ons-autosuggest-input__results-title').attr('id')).toBe('country-of-birth-suggestions');
189
+ });
190
+
191
+ it('renders header with the provided title text', () => {
192
+ const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
193
+
194
+ expect(
195
+ $('.ons-autosuggest-input__results-title')
196
+ .text()
197
+ .trim(),
198
+ ).toBe('Suggestions');
199
+ });
200
+
201
+ it('renders list with a generated identifier', () => {
202
+ const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
203
+
204
+ expect($('.ons-autosuggest-input__listbox').attr('id')).toBe('country-of-birth-listbox');
205
+ });
206
+
207
+ it('renders an accessible list', () => {
208
+ const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
209
+
210
+ expect($('.ons-autosuggest-input__listbox').attr('title')).toBe('Suggestions');
211
+ });
212
+
213
+ it('renders instructions with a generated identifier', () => {
214
+ const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
215
+
216
+ expect($('.ons-autosuggest-input__instructions').attr('id')).toBe('country-of-birth-instructions');
217
+ });
218
+
219
+ it('renders instructions text', () => {
220
+ const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
221
+
222
+ expect(
223
+ $('.ons-autosuggest-input__instructions')
224
+ .text()
225
+ .trim(),
226
+ ).toBe('Use up and down keys to navigate.');
227
+ });
228
+ });
229
+ });
@@ -5,14 +5,13 @@ export function sanitiseAutosuggestText(string, sanitisedQueryRemoveChars = [],
5
5
  sanitisedString = sanitisedString.replace(new RegExp(char.toLowerCase(), 'g'), '');
6
6
  });
7
7
 
8
+ sanitisedString = trimEnd ? sanitisedString.trim() : sanitisedString.trimStart();
8
9
  sanitisedString = sanitisedString.replace(/\s\s+/g, ' ');
9
- sanitisedString = sanitisedString.replace('&', '%26');
10
+ sanitisedString = sanitisedString.replace(/[&]/g, '%26');
10
11
 
11
12
  if (sanitisedQuerySplitNumsChars) {
12
13
  sanitisedString = sanitisedString.replace(/\d(?=[a-z]{3,})/gi, '$& ');
13
14
  }
14
15
 
15
- sanitisedString = trimEnd ? sanitisedString.trim() : sanitisedString.trimStart();
16
-
17
16
  return sanitisedString;
18
17
  }
@@ -0,0 +1,85 @@
1
+ import { sanitiseAutosuggestText } from './autosuggest.helpers';
2
+
3
+ describe('module: autosuggest.helpers', () => {
4
+ describe('function: sanitiseAutosuggestText', () => {
5
+ it.each([
6
+ ['ABC', 'abc'],
7
+ ['ABCdEF', 'abcdef'],
8
+ ['1ABC23', '1abc23'],
9
+ ])('transforms input characters into lower case (%s)', (input, expectedResult) => {
10
+ const result = sanitiseAutosuggestText(input);
11
+ expect(result).toBe(expectedResult);
12
+ });
13
+
14
+ it.each([
15
+ ['abccdefge', [], 'abccdefge'],
16
+ ['abccdefge', ['c'], 'abdefge'],
17
+ ['abccdefge', ['c', 'E'], 'abdfg'],
18
+ ])('removes unwanted characters (%s)', (input, removeChars, expectedResult) => {
19
+ const result = sanitiseAutosuggestText(input, removeChars);
20
+ expect(result).toBe(expectedResult);
21
+ });
22
+
23
+ it.each([
24
+ ['a b', 'a b'],
25
+ ['a b c', 'a b c'],
26
+ ['a \n\n\t b', 'a b'],
27
+ ])('replaces blocks of whitespace with a single space character', (input, expectedResult) => {
28
+ const result = sanitiseAutosuggestText(input);
29
+ expect(result).toBe(expectedResult);
30
+ });
31
+
32
+ it.each([
33
+ ['a&b', 'a%26b'],
34
+ ['a&&b', 'a%26%26b'],
35
+ ['a&b&c', 'a%26b%26c'],
36
+ ])('escapes the "&" character for use in a URL', (input, expectedResult) => {
37
+ const result = sanitiseAutosuggestText(input);
38
+ expect(result).toBe(expectedResult);
39
+ });
40
+
41
+ it.each([
42
+ ['1a', '1a'],
43
+ ['1aa', '1aa'],
44
+ ['1aaa', '1aaa'],
45
+ ['1aaaa', '1aaaa'],
46
+ ['11aaaa', '11aaaa'],
47
+ ['11aaaa22bbb', '11aaaa22bbb'],
48
+ ['11aaaa2b33ccc', '11aaaa2b33ccc'],
49
+ ])(
50
+ 'does not a space after a digit that is followed by at least 3 letters when `sanitisedQuerySplitNumsChars` is false',
51
+ (input, expectedResult) => {
52
+ const result = sanitiseAutosuggestText(input, [], false);
53
+ expect(result).toBe(expectedResult);
54
+ },
55
+ );
56
+
57
+ it.each([
58
+ ['1a', '1a'],
59
+ ['1aa', '1aa'],
60
+ ['1aaa', '1 aaa'],
61
+ ['1aaaa', '1 aaaa'],
62
+ ['11aaaa', '11 aaaa'],
63
+ ['11aaaa22bbb', '11 aaaa22 bbb'],
64
+ ['11aaaa2b33ccc', '11 aaaa2b33 ccc'],
65
+ ])(
66
+ 'adds a space after a digit that is followed by at least 3 letters when `sanitisedQuerySplitNumsChars` is true',
67
+ (input, expectedResult) => {
68
+ const result = sanitiseAutosuggestText(input, [], true);
69
+ expect(result).toBe(expectedResult);
70
+ },
71
+ );
72
+
73
+ it('trims whitespace from start and end of input when `trimEnd` is true', () => {
74
+ const result = sanitiseAutosuggestText(' 1a ', [], false, true);
75
+ expect(result).toBe('1a');
76
+ });
77
+
78
+ it('trims whitespace from only the start of input when `trimEnd` is false', () => {
79
+ const result = sanitiseAutosuggestText(' 1a ', [], false, false);
80
+ // The trailing space is consolidated into 1 whitespace due to a transformation that
81
+ // this implementation applies.
82
+ expect(result).toBe('1a ');
83
+ });
84
+ });
85
+ });
@@ -3,17 +3,19 @@ import AutosuggestUI from './autosuggest.ui';
3
3
  export default class Autosuggest {
4
4
  constructor(context) {
5
5
  this.context = context;
6
- this.lang = document.documentElement.getAttribute('lang').toLowerCase();
7
6
 
8
7
  this.autosuggest = new AutosuggestUI({
9
8
  context,
10
- lang: this.lang,
11
9
  onSelect: this.onSelect.bind(this),
12
10
  onUnsetResult: this.onUnsetResult.bind(this),
13
11
  onError: this.onError.bind(this),
14
12
  });
15
13
  }
16
14
 
15
+ get lang() {
16
+ return document.documentElement.getAttribute('lang').toLowerCase();
17
+ }
18
+
17
19
  async onSelect(result) {
18
20
  this.autosuggest.input.value = result.displayText;
19
21
  }