@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.
- package/README.md +35 -15
- package/components/access-code/_macro.njk +1 -1
- package/components/access-code/_macro.spec.js +162 -0
- package/components/access-code/uac.spec.js +26 -0
- package/components/accordion/_macro.spec.js +224 -0
- package/components/accordion/accordion.spec.js +134 -0
- package/components/address-input/_macro.njk +1 -1
- package/components/address-input/_macro.spec.js +465 -0
- package/components/address-input/autosuggest.address.js +5 -4
- package/components/address-input/autosuggest.address.setter.js +9 -3
- package/components/address-input/autosuggest.address.spec.js +733 -0
- package/components/address-output/_macro.njk +6 -6
- package/components/address-output/_macro.spec.js +122 -0
- package/components/autosuggest/_macro.njk +1 -1
- package/components/autosuggest/_macro.spec.js +229 -0
- package/components/autosuggest/autosuggest.helpers.js +2 -3
- package/components/autosuggest/autosuggest.helpers.spec.js +85 -0
- package/components/autosuggest/autosuggest.js +4 -2
- package/components/autosuggest/autosuggest.spec.js +625 -0
- package/components/autosuggest/autosuggest.ui.js +6 -2
- package/components/breadcrumbs/_macro.spec.js +129 -0
- package/components/button/_macro.njk +5 -5
- package/components/button/_macro.spec.js +446 -0
- package/components/button/button.spec.js +290 -0
- package/components/call-to-action/_macro.njk +3 -1
- package/components/call-to-action/_macro.spec.js +52 -0
- package/components/card/_macro.njk +26 -19
- package/components/card/_macro.spec.js +261 -0
- package/components/char-check-limit/_macro.spec.js +73 -0
- package/components/char-check-limit/character-check.spec.js +196 -0
- package/components/char-check-limit/character-limit.js +1 -1
- package/components/checkboxes/_checkbox-macro.spec.js +419 -0
- package/components/checkboxes/_macro.njk +1 -3
- package/components/checkboxes/_macro.spec.js +306 -0
- package/components/checkboxes/checkbox-with-autoselect.js +2 -1
- package/components/checkboxes/checkboxes.spec.js +208 -0
- package/components/code-highlight/_macro.spec.js +56 -0
- package/components/code-highlight/code-highlight.spec.js +18 -0
- package/components/collapsible/_macro.spec.js +204 -0
- package/components/collapsible/collapsible.js +2 -1
- package/components/collapsible/collapsible.spec.js +236 -0
- package/components/content-pagination/_macro.spec.js +199 -0
- package/components/cookies-banner/_macro.njk +1 -1
- package/components/cookies-banner/_macro.spec.js +171 -0
- package/components/cookies-banner/cookies-banner.spec.js +90 -0
- package/components/date-input/_macro.njk +6 -3
- package/components/date-input/_macro.spec.js +286 -0
- package/components/document-list/_macro.njk +3 -5
- package/components/document-list/_macro.spec.js +491 -0
- package/components/download-resources/download-resources.spec.js +540 -0
- package/components/duration/_macro.njk +7 -6
- package/components/duration/_macro.spec.js +251 -0
- package/components/error/_macro.spec.js +97 -0
- package/components/external-link/_macro.spec.js +60 -0
- package/components/feedback/_macro.njk +5 -3
- package/components/feedback/_macro.spec.js +122 -0
- package/components/field/_macro.njk +2 -2
- package/components/field/_macro.spec.js +97 -0
- package/components/fieldset/_macro.njk +3 -3
- package/components/fieldset/_macro.spec.js +173 -0
- package/components/footer/_macro.njk +12 -49
- package/components/footer/_macro.spec.js +549 -0
- package/components/header/_macro.njk +3 -3
- package/components/header/_macro.spec.js +562 -0
- package/components/hero/_hero.scss +0 -3
- package/components/hero/_macro.njk +4 -4
- package/components/hero/_macro.spec.js +224 -0
- package/components/icons/_macro.njk +15 -15
- package/components/icons/_macro.spec.js +140 -0
- package/components/images/_macro.njk +1 -1
- package/components/images/_macro.spec.js +121 -0
- package/components/input/_input-type.scss +12 -5
- package/components/input/_macro.njk +4 -5
- package/components/input/_macro.spec.js +658 -0
- package/components/label/_macro.spec.js +189 -0
- package/components/language-selector/_macro.spec.js +129 -0
- package/components/lists/_list.scss +4 -0
- package/components/lists/_macro.njk +4 -7
- package/components/lists/_macro.spec.js +618 -0
- package/components/message/_macro.spec.js +137 -0
- package/components/message-list/_macro.njk +7 -7
- package/components/message-list/_macro.spec.js +159 -0
- package/components/metadata/_macro.spec.js +167 -0
- package/components/modal/_macro.njk +6 -6
- package/components/modal/_macro.spec.js +87 -0
- package/components/modal/modal.js +0 -16
- package/components/modal/modal.spec.js +59 -0
- package/components/mutually-exclusive/_macro.njk +2 -2
- package/components/mutually-exclusive/_macro.spec.js +184 -0
- package/components/mutually-exclusive/mutually-exclusive.checkboxes.spec.js +203 -0
- package/components/mutually-exclusive/mutually-exclusive.date.spec.js +142 -0
- package/components/mutually-exclusive/mutually-exclusive.duration.spec.js +141 -0
- package/components/mutually-exclusive/mutually-exclusive.email.spec.js +117 -0
- package/components/mutually-exclusive/mutually-exclusive.multiple-options.checkboxes.spec.js +213 -0
- package/components/mutually-exclusive/mutually-exclusive.number.spec.js +125 -0
- package/components/mutually-exclusive/mutually-exclusive.textarea.spec.js +131 -0
- package/components/navigation/_macro.njk +6 -6
- package/components/navigation/_macro.spec.js +327 -0
- package/components/navigation/navigation.dom.js +1 -1
- package/components/navigation/navigation.spec.js +232 -0
- package/components/pagination/_macro.njk +1 -1
- package/components/pagination/_macro.spec.js +411 -0
- package/components/panel/_macro.njk +6 -6
- package/components/panel/_macro.spec.js +423 -0
- package/components/password/_macro.spec.js +137 -0
- package/components/password/password.spec.js +40 -0
- package/components/phase-banner/_macro.spec.js +73 -0
- package/components/promotional-banner/_macro.spec.js +97 -0
- package/components/question/_macro.njk +16 -22
- package/components/question/_macro.spec.js +309 -0
- package/components/quote/_macro.spec.js +81 -0
- package/components/radios/_macro.njk +3 -6
- package/components/radios/_macro.spec.js +575 -0
- package/components/radios/radios.spec.js +180 -0
- package/components/related-content/_macro.njk +1 -0
- package/components/related-content/_macro.spec.js +142 -0
- package/components/relationships/_macro.spec.js +108 -0
- package/components/relationships/relationships.spec.js +84 -0
- package/components/reply/_macro.njk +2 -2
- package/components/reply/_macro.spec.js +69 -0
- package/components/reply/reply.spec.js +78 -0
- package/components/search/_macro.njk +14 -12
- package/components/search/_macro.spec.js +44 -0
- package/components/search/_search.scss +7 -7
- package/components/section-navigation/_macro.njk +7 -2
- package/components/section-navigation/_macro.spec.js +206 -0
- package/components/select/_macro.njk +3 -3
- package/components/select/_macro.spec.js +203 -0
- package/components/select/select.spec.js +56 -0
- package/components/share-page/_macro.njk +2 -2
- package/components/share-page/_macro.spec.js +110 -0
- package/components/skip-to-content/_macro.spec.js +57 -0
- package/components/skip-to-content/skip-to-content.spec.js +44 -0
- package/components/status/_macro.spec.js +77 -0
- package/components/summary/_macro.njk +5 -5
- package/components/summary/_macro.spec.js +472 -0
- package/components/table/_macro.njk +2 -2
- package/components/table/_macro.spec.js +557 -0
- package/components/table/table.spec.js +155 -0
- package/components/table-of-contents/_macro.njk +35 -35
- package/components/table-of-contents/_macro.spec.js +178 -0
- package/components/table-of-contents/toc.js +29 -25
- package/components/table-of-contents/toc.spec.js +61 -0
- package/components/tabs/_macro.njk +1 -1
- package/components/tabs/_macro.spec.js +79 -0
- package/components/tabs/tabs.spec.js +162 -0
- package/components/text-indent/_macro.spec.js +52 -0
- package/components/textarea/_macro.njk +5 -3
- package/components/textarea/_macro.spec.js +300 -0
- package/components/textarea/textarea.spec.js +98 -0
- package/components/timeline/_macro.njk +3 -3
- package/components/timeline/_macro.spec.js +81 -0
- package/components/timeout-modal/_macro.spec.js +68 -0
- package/components/timeout-modal/timeout-modal.spec.js +226 -0
- package/components/timeout-panel/_macro.njk +0 -1
- package/components/timeout-panel/_macro.spec.js +54 -0
- package/components/timeout-panel/timeout-panel.dom.js +1 -2
- package/components/timeout-panel/timeout-panel.spec.js +161 -0
- package/components/upload/_macro.spec.js +75 -0
- package/components/video/_macro.spec.js +34 -0
- package/css/census.css +1 -1
- package/css/main.css +1 -1
- package/js/cookies-settings.spec.js +154 -0
- package/package.json +10 -23
- package/scripts/main.es5.js +1 -1
- package/scripts/main.js +2 -2
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { renderComponent, setTestPage } from '../../tests/helpers/rendering';
|
|
2
|
+
|
|
3
|
+
const EXAMPLE_RADIOS_PARAMS = {
|
|
4
|
+
name: 'contact-preference',
|
|
5
|
+
clearRadios: {
|
|
6
|
+
text: 'Clear selection',
|
|
7
|
+
url: '/',
|
|
8
|
+
ariaClearText: 'You can clear your answer by clicking the clear selection button under the radio buttons',
|
|
9
|
+
ariaClearedText: 'You have cleared your answer',
|
|
10
|
+
},
|
|
11
|
+
legend: 'How would you like us to contact you?',
|
|
12
|
+
legendClasses: 'ons-u-vh',
|
|
13
|
+
radios: [
|
|
14
|
+
{
|
|
15
|
+
id: 'email',
|
|
16
|
+
value: 'email',
|
|
17
|
+
label: {
|
|
18
|
+
text: 'Email',
|
|
19
|
+
},
|
|
20
|
+
other: {
|
|
21
|
+
id: 'email-other',
|
|
22
|
+
type: 'email',
|
|
23
|
+
label: {
|
|
24
|
+
text: 'Enter your email address',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'phone',
|
|
30
|
+
value: 'phone',
|
|
31
|
+
label: {
|
|
32
|
+
text: 'Phone',
|
|
33
|
+
},
|
|
34
|
+
other: {
|
|
35
|
+
id: 'tel-other',
|
|
36
|
+
type: 'tel',
|
|
37
|
+
label: {
|
|
38
|
+
text: 'Enter your phone number',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'text',
|
|
44
|
+
value: 'Open input test',
|
|
45
|
+
label: {
|
|
46
|
+
text: 'Text',
|
|
47
|
+
},
|
|
48
|
+
other: {
|
|
49
|
+
id: 'text-other',
|
|
50
|
+
type: 'text',
|
|
51
|
+
open: true,
|
|
52
|
+
label: {
|
|
53
|
+
text: 'Enter something else',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
describe('script: radios', () => {
|
|
61
|
+
beforeEach(async () => {
|
|
62
|
+
await setTestPage('/test', renderComponent('radios', EXAMPLE_RADIOS_PARAMS));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('radios with other options should be given aria-expanded attributes', async () => {
|
|
66
|
+
const ariaExpandedOption1 = await page.$eval('#email', node => node.getAttribute('aria-expanded'));
|
|
67
|
+
expect(ariaExpandedOption1).toBe('false');
|
|
68
|
+
const ariaExpandedOption2 = await page.$eval('#phone', node => node.getAttribute('aria-expanded'));
|
|
69
|
+
expect(ariaExpandedOption2).toBe('false');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('radios with "open" other options should not be given aria-expanded attributes', async () => {
|
|
73
|
+
const hasAriaExpandedOption3 = await page.$eval('#text', node => node.hasAttribute('aria-expanded'));
|
|
74
|
+
expect(hasAriaExpandedOption3).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('a radio checked', () => {
|
|
78
|
+
beforeEach(async () => {
|
|
79
|
+
await page.click('#email');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('then the checked radio aria-expanded attribute should be set to true', async () => {
|
|
83
|
+
const ariaExpandedOption1 = await page.$eval('#email', node => node.getAttribute('aria-expanded'));
|
|
84
|
+
expect(ariaExpandedOption1).toBe('true');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('then the unchecked radio aria-expanded attribute should be set to false', async () => {
|
|
88
|
+
const ariaExpandedOption2 = await page.$eval('#phone', node => node.getAttribute('aria-expanded'));
|
|
89
|
+
expect(ariaExpandedOption2).toBe('false');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('then the unchecked radio aria-expanded attribute of "open" radio should not be set', async () => {
|
|
93
|
+
const hasAriaExpanded = await page.$eval('#text', node => node.hasAttribute('aria-expanded'));
|
|
94
|
+
expect(hasAriaExpanded).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('then the clear button should be visible', async () => {
|
|
98
|
+
const isHidden = await page.$eval('.ons-js-clear-btn', node => node.classList.contains('ons-u-db-no-js_enabled'));
|
|
99
|
+
expect(isHidden).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('then the aria live message should announce that the answer can be cleared', async () => {
|
|
103
|
+
const alertText = await page.$eval('.ons-js-clear-radio-alert', node => node.innerHTML);
|
|
104
|
+
expect(alertText).toBe('You can clear your answer by clicking the clear selection button under the radio buttons');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('and the radio selection is changed', () => {
|
|
108
|
+
beforeEach(async () => {
|
|
109
|
+
await page.click('#phone');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('then the checked radio aria-expanded attribute should be set to true', async () => {
|
|
113
|
+
const ariaExpandedOption2 = await page.$eval('#phone', node => node.getAttribute('aria-expanded'));
|
|
114
|
+
expect(ariaExpandedOption2).toBe('true');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('then the unchecked radio aria-expanded attribute should be set to false', async () => {
|
|
118
|
+
const ariaExpandedOption1 = await page.$eval('#email', node => node.getAttribute('aria-expanded'));
|
|
119
|
+
expect(ariaExpandedOption1).toBe('false');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('then the unchecked radio aria-expanded attribute of "open" radio should not be set', async () => {
|
|
123
|
+
const hasAriaExpanded = await page.$eval('#text', node => node.hasAttribute('aria-expanded'));
|
|
124
|
+
expect(hasAriaExpanded).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('the clear button is clicked', () => {
|
|
130
|
+
beforeEach(async () => {
|
|
131
|
+
await page.$eval('.ons-js-clear-btn', node => node.click());
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('then the clear button should not be visible', async () => {
|
|
135
|
+
const isHidden = await page.$eval('.ons-js-clear-btn', node => node.classList.contains('ons-u-db-no-js_enabled'));
|
|
136
|
+
expect(isHidden).toBe(true);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('then the aria live message should announce that the answer has been cleared', async () => {
|
|
140
|
+
const alertText = await page.$eval('.ons-js-clear-radio-alert', node => node.innerHTML);
|
|
141
|
+
expect(alertText).toBe('You have cleared your answer');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('then all radios should not be checked', async () => {
|
|
145
|
+
const checkedRadios = await page.$$eval('.ons-js-radio', nodes => nodes.map(node => node.checked));
|
|
146
|
+
expect(checkedRadios).not.toContain(true);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('then all other input fields should be empty', async () => {
|
|
150
|
+
const emailOtherValue = await page.$eval('#email-other', node => node.value);
|
|
151
|
+
expect(emailOtherValue).toBe('');
|
|
152
|
+
const telOtherValue = await page.$eval('#tel-other', node => node.value);
|
|
153
|
+
expect(telOtherValue).toBe('');
|
|
154
|
+
const textOtherValue = await page.$eval('#text-other', node => node.value);
|
|
155
|
+
expect(textOtherValue).toBe('');
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('there is a visible input which is focused', () => {
|
|
160
|
+
beforeEach(async () => {
|
|
161
|
+
await page.focus('#text-other');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('then the radio button should be checked', async () => {
|
|
165
|
+
const isRadioChecked = await page.$eval('#text', node => node.checked);
|
|
166
|
+
expect(isRadioChecked).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('there is a visible input and the radio is checked', () => {
|
|
171
|
+
beforeEach(async () => {
|
|
172
|
+
await page.click('#text');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('then the input should have a tab index of 0', async () => {
|
|
176
|
+
const tabIndex = await page.$eval('#text-other', node => node.getAttribute('tabindex'));
|
|
177
|
+
expect(tabIndex).toBe('0');
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
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, templateFaker } from '../../tests/helpers/rendering';
|
|
8
|
+
|
|
9
|
+
const EXAMPLE_RELATED_CONTENT_BODY = {
|
|
10
|
+
title: 'Related information',
|
|
11
|
+
body: 'Example body text...',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const EXAMPLE_RELATED_CONTENT_LINKS = {
|
|
15
|
+
rows: [
|
|
16
|
+
{
|
|
17
|
+
id: 'related-articles',
|
|
18
|
+
title: 'Related articles',
|
|
19
|
+
iconPosition: 'before',
|
|
20
|
+
iconSize: 'xl',
|
|
21
|
+
itemsList: [
|
|
22
|
+
{ text: 'First', url: '/article/first' },
|
|
23
|
+
{ text: 'Second', url: '/article/second' },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'related-links',
|
|
28
|
+
title: 'Related links',
|
|
29
|
+
iconPosition: 'after',
|
|
30
|
+
iconSize: 'xxl',
|
|
31
|
+
itemsList: [
|
|
32
|
+
{ text: 'A', url: '/article/a' },
|
|
33
|
+
{ text: 'B', url: '/article/b' },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
describe('macro: related-content', () => {
|
|
40
|
+
describe.each([
|
|
41
|
+
['content', EXAMPLE_RELATED_CONTENT_BODY],
|
|
42
|
+
['list of links', EXAMPLE_RELATED_CONTENT_LINKS],
|
|
43
|
+
])('mode: %s', (_, params) => {
|
|
44
|
+
it('passes jest-axe checks', async () => {
|
|
45
|
+
const $ = cheerio.load(renderComponent('related-content', params));
|
|
46
|
+
|
|
47
|
+
const results = await axe($.html());
|
|
48
|
+
expect(results).toHaveNoViolations();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('has a default `aria-label` of "Related content"', () => {
|
|
52
|
+
const $ = cheerio.load(renderComponent('related-content', params));
|
|
53
|
+
|
|
54
|
+
expect($('.ons-related-content').attr('aria-label')).toBe('Related content');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('has the provided `aria-label`', () => {
|
|
58
|
+
const $ = cheerio.load(
|
|
59
|
+
renderComponent('related-content', {
|
|
60
|
+
...params,
|
|
61
|
+
ariaLabel: 'Related articles',
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
expect($('.ons-related-content').attr('aria-label')).toBe('Related articles');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('has additionally provided style classes', () => {
|
|
69
|
+
const $ = cheerio.load(
|
|
70
|
+
renderComponent('related-content', {
|
|
71
|
+
...params,
|
|
72
|
+
classes: 'extra-class another-extra-class',
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect($('.ons-related-content').hasClass('extra-class')).toBe(true);
|
|
77
|
+
expect($('.ons-related-content').hasClass('another-extra-class')).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('mode: content', () => {
|
|
82
|
+
it('has provided body text', () => {
|
|
83
|
+
const $ = cheerio.load(renderComponent('related-content', EXAMPLE_RELATED_CONTENT_BODY));
|
|
84
|
+
|
|
85
|
+
expect(
|
|
86
|
+
$('.ons-related-content__body .ons-related-content__body')
|
|
87
|
+
.text()
|
|
88
|
+
.trim(),
|
|
89
|
+
).toBe('Example body text...');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('has inner content when the macro is called', () => {
|
|
93
|
+
const $ = cheerio.load(renderComponent('related-content', EXAMPLE_RELATED_CONTENT_BODY, ['<strong>Content</strong>']));
|
|
94
|
+
|
|
95
|
+
expect($('.ons-related-content__body .ons-related-content__body').html()).toContain('<strong>Content</strong>');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('mode: list of links', () => {
|
|
100
|
+
it('has a title heading for each section of links', () => {
|
|
101
|
+
const $ = cheerio.load(renderComponent('related-content', EXAMPLE_RELATED_CONTENT_LINKS));
|
|
102
|
+
|
|
103
|
+
const values = mapAll($('.ons-related-content__title'), node => node.text().trim());
|
|
104
|
+
expect(values).toEqual(['Related articles', 'Related links']);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('has the `id` attribute for each section heading', () => {
|
|
108
|
+
const $ = cheerio.load(renderComponent('related-content', EXAMPLE_RELATED_CONTENT_LINKS));
|
|
109
|
+
|
|
110
|
+
const values = mapAll($('.ons-related-content__title'), node => node.attr('id'));
|
|
111
|
+
expect(values).toEqual(['related-articles', 'related-links']);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('has the `aria-labelledby` attribute for each section of links', () => {
|
|
115
|
+
const $ = cheerio.load(renderComponent('related-content', EXAMPLE_RELATED_CONTENT_LINKS));
|
|
116
|
+
|
|
117
|
+
const values = mapAll($('.ons-related-content__navigation'), node => node.attr('aria-labelledby'));
|
|
118
|
+
expect(values).toEqual(['related-articles', 'related-links']);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('renders the expected list items using the list macro', () => {
|
|
122
|
+
const faker = templateFaker();
|
|
123
|
+
const listsSpy = faker.spy('lists');
|
|
124
|
+
|
|
125
|
+
faker.renderComponent('related-content', EXAMPLE_RELATED_CONTENT_LINKS);
|
|
126
|
+
|
|
127
|
+
expect(listsSpy.occurrences[0]).toHaveProperty('iconPosition', 'before');
|
|
128
|
+
expect(listsSpy.occurrences[0]).toHaveProperty('iconSize', 'xl');
|
|
129
|
+
expect(listsSpy.occurrences[0]).toHaveProperty('itemsList', [
|
|
130
|
+
{ text: 'First', url: '/article/first' },
|
|
131
|
+
{ text: 'Second', url: '/article/second' },
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
expect(listsSpy.occurrences[1]).toHaveProperty('iconPosition', 'after');
|
|
135
|
+
expect(listsSpy.occurrences[1]).toHaveProperty('iconSize', 'xxl');
|
|
136
|
+
expect(listsSpy.occurrences[1]).toHaveProperty('itemsList', [
|
|
137
|
+
{ text: 'A', url: '/article/a' },
|
|
138
|
+
{ text: 'B', url: '/article/b' },
|
|
139
|
+
]);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
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_RELATIONSHIPS = {
|
|
9
|
+
playback: "Amanda Bloggs is Joe Bloggs' <em>…</em>",
|
|
10
|
+
name: 'relationship',
|
|
11
|
+
dontWrap: true,
|
|
12
|
+
legendIsQuestionTitle: true,
|
|
13
|
+
radios: [
|
|
14
|
+
{
|
|
15
|
+
id: 'grandparent',
|
|
16
|
+
value: 'grandparent',
|
|
17
|
+
label: {
|
|
18
|
+
text: 'Grandparent',
|
|
19
|
+
},
|
|
20
|
+
attributes: {
|
|
21
|
+
'data-title': 'Thinking of Joe Bloggs, Amanda Bloggs is their <em>grandparents</em>',
|
|
22
|
+
'data-playback': "Amanda Bloggs is Joe Bloggs' <em>grandparents</em>",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'other-relation',
|
|
27
|
+
value: 'other-relation',
|
|
28
|
+
label: {
|
|
29
|
+
text: 'Other relation',
|
|
30
|
+
},
|
|
31
|
+
attributes: {
|
|
32
|
+
'data-title': 'Thinking of Joe Bloggs, Amanda Bloggs is their <em>other relation</em>',
|
|
33
|
+
'data-playback': "Amanda Bloggs is Joe Bloggs' <em>other relation</em>",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'unrelated',
|
|
38
|
+
value: 'unrelated',
|
|
39
|
+
label: {
|
|
40
|
+
text: 'Unrelated',
|
|
41
|
+
description: 'Including foster child',
|
|
42
|
+
},
|
|
43
|
+
attributes: {
|
|
44
|
+
'data-title': 'Thinking of Joe Bloggs, Amanda Bloggs is <em>unrelated</em> to Joe Bloggs',
|
|
45
|
+
'data-playback': 'Amanda Bloggs is <em>unrelated</em> to Joe Bloggs',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
describe('macro: relationships', () => {
|
|
52
|
+
it('passes jest-axe checks', async () => {
|
|
53
|
+
const $ = cheerio.load(renderComponent('relationships', EXAMPLE_RELATIONSHIPS));
|
|
54
|
+
|
|
55
|
+
const results = await axe($.html());
|
|
56
|
+
expect(results).toHaveNoViolations();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('has the expected `id` attribute', () => {
|
|
60
|
+
const $ = cheerio.load(
|
|
61
|
+
renderComponent('relationships', {
|
|
62
|
+
...EXAMPLE_RELATIONSHIPS,
|
|
63
|
+
id: 'example-relationships',
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect($('.ons-relationships').attr('id')).toBe('example-relationships');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('has additionally provided style classes', () => {
|
|
71
|
+
const $ = cheerio.load(
|
|
72
|
+
renderComponent('relationships', {
|
|
73
|
+
...EXAMPLE_RELATIONSHIPS,
|
|
74
|
+
classes: 'extra-class another-extra-class',
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
expect($('.ons-relationships').hasClass('extra-class')).toBe(true);
|
|
79
|
+
expect($('.ons-relationships').hasClass('another-extra-class')).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('has the provided `playback` text', () => {
|
|
83
|
+
const $ = cheerio.load(renderComponent('relationships', EXAMPLE_RELATIONSHIPS));
|
|
84
|
+
|
|
85
|
+
const playbackContent = $('.ons-relationships__playback')
|
|
86
|
+
.html()
|
|
87
|
+
.trim();
|
|
88
|
+
expect(playbackContent).toBe("Amanda Bloggs is Joe Bloggs' <em>…</em>");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('has playback paragraph hidden initially', async () => {
|
|
92
|
+
const $ = cheerio.load(renderComponent('relationships', EXAMPLE_RELATIONSHIPS));
|
|
93
|
+
|
|
94
|
+
expect($('.ons-relationships__playback').hasClass('ons-u-d-no')).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('renders the expected radio items the radios macro', () => {
|
|
98
|
+
const faker = templateFaker();
|
|
99
|
+
const radiosSpy = faker.spy('radios');
|
|
100
|
+
|
|
101
|
+
faker.renderComponent('relationships', EXAMPLE_RELATIONSHIPS);
|
|
102
|
+
|
|
103
|
+
expect(radiosSpy.occurrences[0]).toHaveProperty('name', 'relationship');
|
|
104
|
+
expect(radiosSpy.occurrences[0]).toHaveProperty('dontWrap', true);
|
|
105
|
+
expect(radiosSpy.occurrences[0]).toHaveProperty('legendIsQuestionTitle', true);
|
|
106
|
+
expect(radiosSpy.occurrences[0]).toHaveProperty('radios', EXAMPLE_RELATIONSHIPS.radios);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { renderComponent, setTestPage } from '../../tests/helpers/rendering';
|
|
2
|
+
|
|
3
|
+
const EXAMPLE_RELATIONSHIPS = {
|
|
4
|
+
dontWrap: true,
|
|
5
|
+
playback: "Amanda Bloggs is Joe Bloggs' <em>…</em>",
|
|
6
|
+
name: 'relationship',
|
|
7
|
+
radios: [
|
|
8
|
+
{
|
|
9
|
+
id: 'grandparent',
|
|
10
|
+
value: 'grandparent',
|
|
11
|
+
label: {
|
|
12
|
+
text: 'Grandparent',
|
|
13
|
+
},
|
|
14
|
+
attributes: {
|
|
15
|
+
'data-title': 'Thinking of Joe Bloggs, Amanda Bloggs is their <em>grandparents</em>',
|
|
16
|
+
'data-playback': "Amanda Bloggs is Joe Bloggs' <em>grandparents</em>",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'other-relation',
|
|
21
|
+
value: 'other-relation',
|
|
22
|
+
label: {
|
|
23
|
+
text: 'Other relation',
|
|
24
|
+
},
|
|
25
|
+
attributes: {
|
|
26
|
+
'data-title': 'Thinking of Joe Bloggs, Amanda Bloggs is their <em>other relation</em>',
|
|
27
|
+
'data-playback': "Amanda Bloggs is Joe Bloggs' <em>other relation</em>",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'unrelated',
|
|
32
|
+
value: 'unrelated',
|
|
33
|
+
label: {
|
|
34
|
+
text: 'Unrelated',
|
|
35
|
+
description: 'Including foster child',
|
|
36
|
+
},
|
|
37
|
+
attributes: {
|
|
38
|
+
'data-title': 'Thinking of Joe Bloggs, Amanda Bloggs is <em>unrelated</em> to Joe Bloggs',
|
|
39
|
+
'data-playback': 'Amanda Bloggs is <em>unrelated</em> to Joe Bloggs',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
describe('script: relationships', () => {
|
|
46
|
+
beforeEach(async () => {
|
|
47
|
+
await setTestPage(
|
|
48
|
+
'/test',
|
|
49
|
+
renderComponent(
|
|
50
|
+
'question',
|
|
51
|
+
{
|
|
52
|
+
title: 'Thinking of Joe Bloggs, Amanda Bloggs is their <em>…</em>',
|
|
53
|
+
readDescriptionFirst: true,
|
|
54
|
+
legendIsQuestionTitle: true,
|
|
55
|
+
legendTitleClasses: 'ons-js-relationships-legend',
|
|
56
|
+
},
|
|
57
|
+
[renderComponent('relationships', EXAMPLE_RELATIONSHIPS)],
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('when the component initialises', () => {
|
|
63
|
+
it('then the playback paragraph should become visible', async () => {
|
|
64
|
+
const hasHideClass = await page.$eval('.ons-relationships__playback', node => node.classList.contains('ons-u-d-no'));
|
|
65
|
+
expect(hasHideClass).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('when the user selects a relationship', () => {
|
|
70
|
+
beforeEach(async () => {
|
|
71
|
+
await page.click('#other-relation');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('the question title should be changed to reflect the relationship', async () => {
|
|
75
|
+
const headingText = await page.$eval('h1', element => element.innerHTML);
|
|
76
|
+
expect(headingText.trim()).toBe('Thinking of Joe Bloggs, Amanda Bloggs is their <em>other relation</em>');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('the playback should be changed to reflect the relationship', async () => {
|
|
80
|
+
const playbackText = await page.$eval('.ons-relationships__playback', element => element.innerHTML);
|
|
81
|
+
expect(playbackText.trim()).toBe("Amanda Bloggs is Joe Bloggs' <em>other relation</em>");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{% macro onsReply(params) %}
|
|
2
2
|
{% from "components/textarea/_macro.njk" import onsTextarea %}
|
|
3
3
|
{% from "components/button/_macro.njk" import onsButton %}
|
|
4
|
-
<div class="ons-js-reply">
|
|
4
|
+
<div class="ons-reply ons-js-reply">
|
|
5
5
|
{{
|
|
6
6
|
onsTextarea({
|
|
7
7
|
"id": params.textarea.id,
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
}}
|
|
24
24
|
</div>
|
|
25
25
|
<div class="ons-grid__col ons-u-ml-m">
|
|
26
|
-
<a href="{{ params.closeLinkUrl }}">{{ params.closeLinkText }}</a>
|
|
26
|
+
<a class="ons-reply__link" href="{{ params.closeLinkUrl }}">{{ params.closeLinkText }}</a>
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
29
29
|
</div>
|
|
@@ -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
|
+
});
|