@ons/design-system 72.10.5 → 72.10.7

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 (57) hide show
  1. package/components/button/_button.scss +8 -6
  2. package/components/char-check-limit/_macro.njk +5 -4
  3. package/components/char-check-limit/_macro.spec.js +167 -7
  4. package/components/char-check-limit/character-check.js +23 -17
  5. package/components/char-check-limit/character-check.spec.js +106 -1
  6. package/components/chart/_chart.scss +33 -5
  7. package/components/chart/_macro.njk +172 -140
  8. package/components/chart/_macro.spec.js +378 -22
  9. package/components/chart/annotations-options.js +1 -1
  10. package/components/chart/bar-chart.js +1 -0
  11. package/components/chart/chart-iframe-resize.dom.js +8 -0
  12. package/components/chart/chart-iframe-resize.js +16 -0
  13. package/components/chart/chart.js +5 -0
  14. package/components/chart/example-area-chart-with-axis-min-and-max-values.njk +72 -0
  15. package/components/chart/example-bar-chart-with-axis-min-and-max-values.njk +59 -0
  16. package/components/chart/example-bar-chart.njk +4 -0
  17. package/components/chart/example-column-chart-with-axis-min-and-max-values.njk +59 -0
  18. package/components/chart/example-iframe-chart.njk +32 -0
  19. package/components/chart/example-line-chart-with-annotations.njk +3 -1
  20. package/components/chart/example-line-chart-with-axis-min-and-max-values.njk +227 -0
  21. package/components/chart/example-line-chart.njk +4 -0
  22. package/components/chart/example-scatter-chart-with-axis-min-and-max-values.njk +98 -0
  23. package/components/chart/example-scatter-chart.njk +4 -0
  24. package/components/chart/range-annotations-options.js +1 -1
  25. package/components/chart/reference-line-annotations-options.js +1 -1
  26. package/components/chart/specific-chart-options.js +19 -0
  27. package/components/checkboxes/_macro.spec.js +3 -3
  28. package/components/details-panel/_details-panel.scss +4 -0
  29. package/components/details-panel/_macro.njk +17 -13
  30. package/components/details-panel/_macro.spec.js +17 -0
  31. package/components/details-panel/example-details-panel-open.njk +2 -1
  32. package/components/details-panel/example-details-panel.njk +2 -1
  33. package/components/header/_header.scss +40 -24
  34. package/components/header/_macro.njk +103 -89
  35. package/components/header/_macro.spec.js +130 -2
  36. package/components/header/example-header-basic-with-search-and-language.njk +207 -0
  37. package/components/header/{example-header-with-search-button.njk → example-header-basic-with-search-button.njk} +1 -0
  38. package/components/header/example-header-basic.njk +1 -0
  39. package/components/hero/_macro.njk +9 -2
  40. package/components/hero/_macro.spec.js +22 -0
  41. package/components/hero/example-hero-grey.njk +2 -1
  42. package/components/input/_macro.njk +1 -2
  43. package/components/input/_macro.spec.js +2 -22
  44. package/components/input/example-input-search-with-character-check.njk +0 -1
  45. package/components/language-selector/_macro.njk +1 -1
  46. package/components/language-selector/_macro.spec.js +45 -0
  47. package/components/navigation/navigation.js +24 -17
  48. package/components/pagination/_macro.njk +17 -7
  49. package/components/pagination/_macro.spec.js +191 -17
  50. package/components/textarea/_macro.njk +19 -3
  51. package/components/textarea/_macro.spec.js +76 -3
  52. package/components/textarea/example-textarea-with-word-limit.njk +20 -0
  53. package/css/main.css +1 -1
  54. package/js/main.js +2 -0
  55. package/package.json +3 -2
  56. package/scripts/main.es5.js +3 -1
  57. package/scripts/main.js +3 -1
@@ -610,9 +610,6 @@ $button-shadow-size: 3px;
610
610
  }
611
611
 
612
612
  &--menu {
613
- align-items: center;
614
- display: flex;
615
- padding: 1rem;
616
613
  border-bottom: 4px solid rgb(0 0 0 / 0%);
617
614
  .ons-icon {
618
615
  transform: rotate(90deg);
@@ -699,9 +696,14 @@ $button-shadow-size: 3px;
699
696
  &--search-icon,
700
697
  &--close {
701
698
  border-bottom: 4px solid transparent;
699
+ }
700
+
701
+ &--menu,
702
+ &--search-icon,
703
+ &--close {
702
704
  align-items: center;
703
705
  display: flex;
704
- padding: 1rem;
706
+ padding: 1.25rem 1rem 1rem;
705
707
  }
706
708
 
707
709
  &--search-icon &,
@@ -768,8 +770,8 @@ $button-shadow-size: 3px;
768
770
  }
769
771
 
770
772
  &--search-icon:focus {
771
- background-color: var(--ons-color-focus);
772
- border-color: var(--ons-color-text-link-focus);
773
+ background-color: var(--ons-color-focus) !important;
774
+ border-color: var(--ons-color-text-link-focus) !important;
773
775
  }
774
776
 
775
777
  &--search-icon:focus &,
@@ -6,10 +6,11 @@
6
6
  <span
7
7
  id="{{ params.id }}"
8
8
  class="ons-input__limit ons-u-fs-s--b ons-u-d-no ons-u-mt-2xs"
9
- data-charcount-singular="{{ params.charCountSingular }}"
10
- data-charcount-plural="{{ params.charCountPlural }}"
11
- data-charcount-limit-singular="{{ params.charCountOverLimitSingular | default("You have exceeded the character limit by {x} character") }}"
12
- data-charcount-limit-plural="{{ params.charCountOverLimitPlural | default("You have exceeded the character limit by {x} characters") }}"
9
+ data-count-type="{{ params.variant if params.variant == 'word' else 'char' }}"
10
+ data-message-singular="{% if params.variant == 'word' %}{{ params.wordCountSingular }}{% else %}{{ params.charCountSingular }}{% endif %}"
11
+ data-message-plural="{% if params.variant == 'word' %}{{ params.wordCountPlural }}{% else %}{{ params.charCountPlural }}{% endif %}"
12
+ data-message-over-limit-singular="{% if params.variant == 'word' %}{{ params.wordCountOverLimitSingular | default("You have exceeded the word limit by {x} word") }}{% else %}{{ params.charCountOverLimitSingular | default("You have exceeded the character limit by {x} character") }}{% endif %}"
13
+ data-message-over-limit-plural="{% if params.variant == 'word' %}{{ params.wordCountOverLimitPlural | default("You have exceeded the word limit by {x} words") }}{% else %}{{ params.charCountOverLimitPlural | default("You have exceeded the character limit by {x} characters") }}{% endif %}"
13
14
  >
14
15
  </span>
15
16
  {% endmacro %}
@@ -5,7 +5,7 @@ import * as cheerio from 'cheerio';
5
5
  import axe from '../../tests/helpers/axe';
6
6
  import { renderComponent } from '../../tests/helpers/rendering';
7
7
 
8
- import { EXAMPLE_CHAR_CHECK_LIMIT } from './_test-examples';
8
+ import { EXAMPLE_CHAR_CHECK_LIMIT, EXAMPLE_WORD_CHECK_LIMIT, EXAMPLE_CHAR_WORD_CHECK_LIMIT_MESSAGES } from './_test-examples';
9
9
 
10
10
  describe('FOR: Macro: CharCheckLimit', () => {
11
11
  describe('GIVEN: Params: Required', () => {
@@ -17,26 +17,34 @@ describe('FOR: Macro: CharCheckLimit', () => {
17
17
 
18
18
  expect(results).toHaveNoViolations();
19
19
  });
20
+
21
+ test('THEN: sets char as the count type to count number of characters', () => {
22
+ expect($('.ons-input__limit').attr('data-count-type')).toBe('char');
23
+ });
24
+
20
25
  test('THEN: has the provided id attribute', () => {
21
26
  expect($('.ons-input__limit').attr('id')).toBe('example-char-check-limit');
22
27
  });
28
+
23
29
  test('THEN: has the data attribute which defines charCountPlural', () => {
24
- expect($('.ons-input__limit').attr('data-charcount-plural')).toBe('You have {x} characters remaining');
30
+ expect($('.ons-input__limit').attr('data-message-plural')).toBe('You have {x} characters remaining');
25
31
  });
32
+
26
33
  test('THEN: has the data attribute which defines charCountSingular', () => {
27
- expect($('.ons-input__limit').attr('data-charcount-singular')).toBe('You have {x} character remaining');
34
+ expect($('.ons-input__limit').attr('data-message-singular')).toBe('You have {x} character remaining');
28
35
  });
29
36
  test('THEN: has the data attribute which defines charCountOverLimitSingular', () => {
30
- expect($('.ons-input__limit').attr('data-charcount-limit-singular')).toBe('{x} character too many');
37
+ expect($('.ons-input__limit').attr('data-message-over-limit-singular')).toBe('{x} character too many');
31
38
  });
39
+
32
40
  test('THEN: has the data attribute which defines charCountOverLimitPlural', () => {
33
- expect($('.ons-input__limit').attr('data-charcount-limit-plural')).toBe('{x} characters too many');
41
+ expect($('.ons-input__limit').attr('data-message-over-limit-plural')).toBe('{x} characters too many');
34
42
  });
35
43
  });
36
44
  });
37
45
 
38
46
  describe('GIVEN: Params: variant', () => {
39
- describe('WHEN: variant is provided', () => {
47
+ describe('WHEN: variant is set to check', () => {
40
48
  const $ = cheerio.load(
41
49
  renderComponent(
42
50
  'char-check-limit',
@@ -48,7 +56,7 @@ describe('FOR: Macro: CharCheckLimit', () => {
48
56
  ),
49
57
  );
50
58
 
51
- test('THEN: passes jest-axe checks with variant set to check', async () => {
59
+ test('THEN: passes jest-axe checks', async () => {
52
60
  const results = await axe($.html());
53
61
  expect(results).toHaveNoViolations();
54
62
  });
@@ -56,6 +64,158 @@ describe('FOR: Macro: CharCheckLimit', () => {
56
64
  test('THEN: renders the passed content', () => {
57
65
  expect($('.ons-js-char-check-input').text()).toBe('Test content.');
58
66
  });
67
+
68
+ test('THEN: sets char as the count type to count number of characters', () => {
69
+ expect($('.ons-input__limit').attr('data-count-type')).toBe('char');
70
+ });
71
+ });
72
+
73
+ describe('WHEN: variant is set to word', () => {
74
+ const $ = cheerio.load(renderComponent('char-check-limit', EXAMPLE_WORD_CHECK_LIMIT));
75
+
76
+ test('THEN: passes jest-axe checks', async () => {
77
+ const results = await axe($.html());
78
+ expect(results).toHaveNoViolations();
79
+ });
80
+
81
+ test('THEN: sets word as the count type to count number of words', () => {
82
+ expect($('.ons-input__limit').attr('data-count-type')).toBe('word');
83
+ });
84
+
85
+ test('THEN: has the data attribute which defines wordCountPlural', () => {
86
+ const $ = cheerio.load(renderComponent('char-check-limit', EXAMPLE_WORD_CHECK_LIMIT));
87
+ expect($('.ons-input__limit').attr('data-message-plural')).toBe('You have {x} words remaining');
88
+ });
89
+
90
+ test('THEN: has the data attribute which defines wordCountSingular', () => {
91
+ const $ = cheerio.load(renderComponent('char-check-limit', EXAMPLE_WORD_CHECK_LIMIT));
92
+ expect($('.ons-input__limit').attr('data-message-singular')).toBe('You have {x} word remaining');
93
+ });
94
+
95
+ test('THEN: has the data attribute which defines wordCountOverLimitPlural', () => {
96
+ const $ = cheerio.load(renderComponent('char-check-limit', EXAMPLE_WORD_CHECK_LIMIT));
97
+ expect($('.ons-input__limit').attr('data-message-over-limit-plural')).toBe('You have {x} words too many');
98
+ });
99
+
100
+ test('THEN: has the data attribute which defines wordCountOverLimitSingular', () => {
101
+ const $ = cheerio.load(renderComponent('char-check-limit', EXAMPLE_WORD_CHECK_LIMIT));
102
+ expect($('.ons-input__limit').attr('data-message-over-limit-singular')).toBe('You have {x} word too many');
103
+ });
104
+ });
105
+ });
106
+
107
+ describe('GIVEN: Params: Character and Word count messages', () => {
108
+ describe('WHEN: character and word count messages are provided', () => {
109
+ const $ = cheerio.load(renderComponent('char-check-limit', EXAMPLE_CHAR_WORD_CHECK_LIMIT_MESSAGES));
110
+
111
+ test('THEN: passes jest-axe checks', async () => {
112
+ const results = await axe($.html());
113
+
114
+ expect(results).toHaveNoViolations();
115
+ });
116
+
117
+ test('THEN: sets char as the count type to count number of characters', () => {
118
+ expect($('.ons-input__limit').attr('data-count-type')).toBe('char');
119
+ });
120
+
121
+ test('THEN: has the provided id attribute', () => {
122
+ expect($('.ons-input__limit').attr('id')).toBe('example-char-word-check-limit');
123
+ });
124
+
125
+ test('THEN: has the data attribute which defines charCountPlural', () => {
126
+ expect($('.ons-input__limit').attr('data-message-plural')).toBe('You have {x} characters remaining');
127
+ });
128
+
129
+ test('THEN: has the data attribute which defines charCountSingular', () => {
130
+ expect($('.ons-input__limit').attr('data-message-singular')).toBe('You have {x} character remaining');
131
+ });
132
+ test('THEN: has the data attribute which defines charCountOverLimitSingular', () => {
133
+ expect($('.ons-input__limit').attr('data-message-over-limit-singular')).toBe('You have {x} character too many');
134
+ });
135
+
136
+ test('THEN: has the data attribute which defines charCountOverLimitPlural', () => {
137
+ expect($('.ons-input__limit').attr('data-message-over-limit-plural')).toBe('You have {x} characters too many');
138
+ });
139
+ });
140
+
141
+ describe('WHEN: character and word count messages are provided and variant is set to check', () => {
142
+ const $ = cheerio.load(
143
+ renderComponent(
144
+ 'char-check-limit',
145
+ {
146
+ ...EXAMPLE_CHAR_WORD_CHECK_LIMIT_MESSAGES,
147
+ variant: 'check',
148
+ },
149
+ ['<p>Test content.</p>'],
150
+ ),
151
+ );
152
+
153
+ test('THEN: passes jest-axe checks', async () => {
154
+ const results = await axe($.html());
155
+
156
+ expect(results).toHaveNoViolations();
157
+ });
158
+
159
+ test('THEN: sets char as the count type to count number of characters', () => {
160
+ expect($('.ons-input__limit').attr('data-count-type')).toBe('char');
161
+ });
162
+
163
+ test('THEN: has the provided id attribute', () => {
164
+ expect($('.ons-input__limit').attr('id')).toBe('example-char-word-check-limit');
165
+ });
166
+
167
+ test('THEN: has the data attribute which defines charCountPlural', () => {
168
+ expect($('.ons-input__limit').attr('data-message-plural')).toBe('You have {x} characters remaining');
169
+ });
170
+
171
+ test('THEN: has the data attribute which defines charCountSingular', () => {
172
+ expect($('.ons-input__limit').attr('data-message-singular')).toBe('You have {x} character remaining');
173
+ });
174
+ test('THEN: has the data attribute which defines charCountOverLimitSingular', () => {
175
+ expect($('.ons-input__limit').attr('data-message-over-limit-singular')).toBe('You have {x} character too many');
176
+ });
177
+
178
+ test('THEN: has the data attribute which defines charCountOverLimitPlural', () => {
179
+ expect($('.ons-input__limit').attr('data-message-over-limit-plural')).toBe('You have {x} characters too many');
180
+ });
181
+ });
182
+
183
+ describe('WHEN: character and word count messages are provided and variant is set to word', () => {
184
+ const $ = cheerio.load(
185
+ renderComponent('char-check-limit', {
186
+ ...EXAMPLE_CHAR_WORD_CHECK_LIMIT_MESSAGES,
187
+ variant: 'word',
188
+ }),
189
+ );
190
+
191
+ test('THEN: passes jest-axe checks', async () => {
192
+ const results = await axe($.html());
193
+
194
+ expect(results).toHaveNoViolations();
195
+ });
196
+
197
+ test('THEN: sets char as the count type to count number of words', () => {
198
+ expect($('.ons-input__limit').attr('data-count-type')).toBe('word');
199
+ });
200
+
201
+ test('THEN: has the provided id attribute', () => {
202
+ expect($('.ons-input__limit').attr('id')).toBe('example-char-word-check-limit');
203
+ });
204
+
205
+ test('THEN: has the data attribute which defines wordCountPlural', () => {
206
+ expect($('.ons-input__limit').attr('data-message-plural')).toBe('You have {x} words remaining');
207
+ });
208
+
209
+ test('THEN: has the data attribute which defines wordCountSingular', () => {
210
+ expect($('.ons-input__limit').attr('data-message-singular')).toBe('You have {x} word remaining');
211
+ });
212
+ test('THEN: has the data attribute which defines wordCountOverLimitSingular', () => {
213
+ expect($('.ons-input__limit').attr('data-message-over-limit-singular')).toBe('You have {x} word too many');
214
+ });
215
+
216
+ test('THEN: has the data attribute which defines wordCountOverLimitPlural', () => {
217
+ expect($('.ons-input__limit').attr('data-message-over-limit-plural')).toBe('You have {x} words too many');
218
+ });
59
219
  });
60
220
  });
61
221
  });
@@ -1,8 +1,8 @@
1
1
  const inputClassLimitReached = 'ons-input--limit-reached';
2
2
  const remainingClassLimitReached = 'ons-input__limit--reached';
3
- const attrCharCheckRef = 'data-char-check-ref';
4
- const attrCharCheckCountdown = 'data-char-check-countdown';
5
- const attrCharCheckVal = 'data-char-check-num';
3
+ const attrMessageCheckRef = 'data-message-check-ref';
4
+ const attrMessageCheckVal = 'data-message-check-num';
5
+ const countType = 'data-count-type';
6
6
 
7
7
  export default class CharCheck {
8
8
  constructor(context) {
@@ -18,13 +18,12 @@ export default class CharCheck {
18
18
  // Find the button: if input is passed directly, look at its parent
19
19
  let parent = this.input.parentNode;
20
20
  this.button = parent ? parent.querySelector('button') : null;
21
- this.checkElement = document.getElementById(this.input.getAttribute(attrCharCheckRef));
22
- this.checkVal = this.input.getAttribute(attrCharCheckVal);
23
- this.countdown = this.input.getAttribute(attrCharCheckCountdown) || false;
24
- this.singularMessage = this.checkElement.getAttribute('data-charcount-singular') || null;
25
- this.pluralMessage = this.checkElement.getAttribute('data-charcount-plural') || null;
26
- this.charLimitSingularMessage = this.checkElement.getAttribute('data-charcount-limit-singular') || null;
27
- this.charLimitPluralMessage = this.checkElement.getAttribute('data-charcount-limit-plural') || null;
21
+ this.checkElement = document.getElementById(this.input.getAttribute(attrMessageCheckRef));
22
+ this.checkVal = this.input.getAttribute(attrMessageCheckVal);
23
+ this.singularMessage = this.checkElement.getAttribute('data-message-singular') || null;
24
+ this.pluralMessage = this.checkElement.getAttribute('data-message-plural') || null;
25
+ this.overLimitSingularMessage = this.checkElement.getAttribute('data-message-over-limit-singular') || null;
26
+ this.overLimitPluralMessage = this.checkElement.getAttribute('data-message-over-limit-plural') || null;
28
27
 
29
28
  this.updateCheckReadout(this.input);
30
29
 
@@ -37,7 +36,8 @@ export default class CharCheck {
37
36
 
38
37
  updateCheckReadout(event, firstRun) {
39
38
  const value = this.input.value;
40
- const remaining = this.checkVal - this.getCharLength(value);
39
+ const currentLength = this.checkElement.getAttribute(countType) == 'char' ? this.getCharLength(value) : this.getWordLength(value);
40
+ const remaining = this.checkVal - currentLength;
41
41
 
42
42
  // Prevent aria live announcement when component initialises
43
43
  if (!firstRun && event.inputType) {
@@ -53,12 +53,12 @@ export default class CharCheck {
53
53
 
54
54
  checkRemaining(remaining) {
55
55
  let message;
56
- if (this.countdown && remaining === 1) {
56
+ if (remaining === 1) {
57
57
  message = this.singularMessage;
58
58
  } else if (remaining === -1) {
59
- message = this.charLimitSingularMessage;
59
+ message = this.overLimitSingularMessage;
60
60
  } else if (remaining < -1) {
61
- message = this.charLimitPluralMessage;
61
+ message = this.overLimitPluralMessage;
62
62
  } else {
63
63
  message = this.pluralMessage;
64
64
  }
@@ -82,9 +82,7 @@ export default class CharCheck {
82
82
  // Always display the remaining character message for textarea
83
83
  this.checkElement.classList['remove']('ons-u-d-no');
84
84
  } else {
85
- this.checkElement.classList[(remaining < this.checkVal && remaining > 0 && this.countdown) || remaining < 0 ? 'remove' : 'add'](
86
- 'ons-u-d-no',
87
- );
85
+ this.checkElement.classList[(remaining < this.checkVal && remaining > 0) || remaining < 0 ? 'remove' : 'add']('ons-u-d-no');
88
86
  }
89
87
  }
90
88
 
@@ -97,4 +95,12 @@ export default class CharCheck {
97
95
  const lineBreaks = (text.match(/\n/g) || []).length;
98
96
  return text.length + lineBreaks;
99
97
  }
98
+
99
+ getWordLength(text) {
100
+ return text
101
+ .trim()
102
+ .split(/\s+/) // split by any whitespace
103
+ .map((word) => word.replace(/[.,!?;:]+$/, '')) // remove trailing punctuation
104
+ .filter(Boolean).length;
105
+ }
100
106
  }
@@ -11,7 +11,6 @@ const EXAMPLE_INPUT_WITH_CHARACTER_CHECK = {
11
11
  text: 'Filter',
12
12
  },
13
13
  charCheckLimit: {
14
- charcheckCountdown: true,
15
14
  limit: 11,
16
15
  charCountOverLimitSingular: '{x} number too many',
17
16
  charCountOverLimitPlural: '{x} numbers too many',
@@ -51,6 +50,23 @@ const EXAMPLE_CHARACTER_CHECK_WITH_MUTUALLY_EXCLUSIVE = {
51
50
  },
52
51
  };
53
52
 
53
+ const EXAMPLE_INPUT_WITH_WORD_CHECK = {
54
+ id: 'example-textarea',
55
+ name: 'feedback-limited',
56
+ width: '30',
57
+ label: {
58
+ text: 'Please provide some feedback',
59
+ description: 'For example describe any difficulties you experienced in the use of this service',
60
+ },
61
+ wordCheckLimit: {
62
+ limit: 5,
63
+ wordCountSingular: 'You have {x} word remaining',
64
+ wordCountPlural: 'You have {x} words remaining',
65
+ wordCountOverLimitSingular: 'You are {x} word over the limit',
66
+ wordCountOverLimitPlural: 'You are {x} words over the limit',
67
+ },
68
+ };
69
+
54
70
  describe('script: character-check', () => {
55
71
  describe('mode: basic', () => {
56
72
  beforeEach(async () => {
@@ -197,4 +213,93 @@ describe('script: character-check', () => {
197
213
  });
198
214
  });
199
215
  });
216
+
217
+ describe('Word limit', () => {
218
+ beforeEach(async () => {
219
+ await setTestPage('/test', renderComponent('textarea', EXAMPLE_INPUT_WITH_WORD_CHECK));
220
+ });
221
+
222
+ describe('Given that the word limit helper has initialised correctly', () => {
223
+ it('the word limit readout should be visible', async () => {
224
+ const hasClass = await page.$eval('#example-textarea-check', (node) => node.classList.contains('ons-u-d-no'));
225
+ expect(hasClass).toBe(false);
226
+ });
227
+ });
228
+
229
+ describe('Given that the user has not typed into the textarea', () => {
230
+ describe('when the user types into the textarea', () => {
231
+ beforeEach(async () => {
232
+ await page.type('#example-textarea', 'Lorem ipsum dolor');
233
+ });
234
+
235
+ it('then the word limit helper text reflects the number of words remaining', async () => {
236
+ const readout = await page.$eval('#example-textarea-check', (node) => node.textContent);
237
+ expect(readout).toBe('You have 2 words remaining');
238
+ });
239
+ });
240
+
241
+ describe('when the user reaches the maxlength of the textarea', () => {
242
+ beforeEach(async () => {
243
+ await page.type('#example-textarea', 'Lorem ipsum dolor sit amet');
244
+ });
245
+
246
+ it('then the word limit helper text reflects the number of words remaining', async () => {
247
+ const readout = await page.$eval('#example-textarea-check', (node) => node.textContent);
248
+ expect(readout).toBe('You have 0 words remaining');
249
+ });
250
+ });
251
+ });
252
+ describe('when the user exceeds the maxlength of the textarea', () => {
253
+ beforeEach(async () => {
254
+ await page.type('#example-textarea', 'Lorem ipsum dolor sit amet, consectetur porttitor.');
255
+ });
256
+
257
+ it('then the word limit helper text reflects the number of words exceeded', async () => {
258
+ const readout = await page.$eval('#example-textarea-check', (node) => node.textContent);
259
+ expect(readout).toBe('You are 2 words over the limit');
260
+ });
261
+
262
+ it('then the textarea should be given limit reached classes', async () => {
263
+ const hasClass = await page.$eval('#example-textarea', (node) => node.classList.contains('ons-input--limit-reached'));
264
+ expect(hasClass).toBe(true);
265
+ });
266
+
267
+ it('then the word limit helper text should be given limit reached classes', async () => {
268
+ const hasClass = await page.$eval('#example-textarea-check', (node) =>
269
+ node.classList.contains('ons-input__limit--reached'),
270
+ );
271
+ expect(hasClass).toBe(true);
272
+ });
273
+ });
274
+
275
+ describe('Given that the user has reached the maxlength of the textarea', () => {
276
+ beforeEach(async () => {
277
+ await page.type('#example-textarea', 'Lorem ipsum dolor sit a');
278
+ });
279
+
280
+ describe('when the user removes a character', () => {
281
+ beforeEach(async () => {
282
+ await page.focus('#example-textarea');
283
+ await page.keyboard.press('Backspace');
284
+ });
285
+
286
+ it('then the word limit helper text reflects the number of words remaining', async () => {
287
+ const readout = await page.$eval('#example-textarea-check', (node) => node.textContent);
288
+ expect(readout).toBe('You have 1 word remaining');
289
+ });
290
+
291
+ it('then the textarea should not be given limit reached classes', async () => {
292
+ const hasClass = await page.$eval('#example-textarea', (node) => node.classList.contains('ons-input--limit-reached'));
293
+ expect(hasClass).toBe(false);
294
+ });
295
+
296
+ it('then the readout should not be given limit reached classes', async () => {
297
+ const hasClass = await page.$eval('#example-textarea-check', (node) =>
298
+ node.classList.contains('ons-input__limit--reached'),
299
+ );
300
+ expect(hasClass).toBe(false);
301
+ });
302
+ });
303
+ });
304
+ });
200
305
  });
@@ -3,6 +3,10 @@
3
3
  container-type: inline-size;
4
4
  max-width: $grid-max-width;
5
5
 
6
+ &__chart {
7
+ overflow: visible !important;
8
+ }
9
+
6
10
  &__download-title {
7
11
  @extend .ons-u-pt-l;
8
12
  @extend .ons-u-fs-r--b;
@@ -12,7 +16,7 @@
12
16
  color: var(--ons-color-grey-75);
13
17
  }
14
18
 
15
- &__footnote-number {
19
+ &__annotations-footnotes-number {
16
20
  // !important is necessary for some range annotation labels at mobile, otherwise
17
21
  // the inline-styled block display takes precedence. This inline style is only
18
22
  // applied in some cases - it seems to happen when the shaded area is narrower than the label width.
@@ -34,7 +38,7 @@
34
38
  }
35
39
 
36
40
  /* Override Highcharts styling */
37
- &__footnote-number span {
41
+ &__annotations-footnotes-number span {
38
42
  position: static !important;
39
43
  transform-origin: 0 0 !important;
40
44
  font-size: inherit !important;
@@ -43,7 +47,7 @@
43
47
  left: 0 !important;
44
48
  }
45
49
 
46
- &__footnotes {
50
+ &__annotations-footnotes {
47
51
  list-style: none;
48
52
  padding: 0;
49
53
  margin: 0 0 1rem;
@@ -56,12 +60,14 @@
56
60
  }
57
61
  }
58
62
 
59
- &__footnote_item {
63
+ &__annotations-footnotes_item {
60
64
  display: flex;
61
- align-items: center;
65
+ align-items: flex-start;
62
66
  gap: 0.5rem;
63
67
  font-size: 0.875rem; // 14px
64
68
  color: var(--ons-color-grey-100);
69
+ line-height: 1.2rem;
70
+ margin-bottom: 0.5rem;
65
71
  }
66
72
 
67
73
  &__boxplot-legend {
@@ -95,6 +101,15 @@
95
101
  font-size: inherit;
96
102
  }
97
103
  }
104
+
105
+ &__iframe {
106
+ width: 100%;
107
+
108
+ @media (scripting: none) {
109
+ // Approximately matches the aspect ratio of the fallback image inside the iframe
110
+ aspect-ratio: 16 / 9;
111
+ }
112
+ }
98
113
  }
99
114
 
100
115
  // This is a workaround to position the axis title to the left
@@ -148,3 +163,16 @@
148
163
  border-top: 1px solid var(--ons-color-grey-100);
149
164
  border-left: 1px solid var(--ons-color-grey-100);
150
165
  }
166
+
167
+ // Allow last label to overflow
168
+ .highcharts-container {
169
+ overflow: visible !important;
170
+ }
171
+
172
+ .highcharts-xaxis-labels span:last-child {
173
+ overflow: visible !important;
174
+ }
175
+
176
+ .bar-chart-container .highcharts-yaxis-labels span:last-child {
177
+ overflow: visible !important;
178
+ }