@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
@@ -0,0 +1,207 @@
1
+ ---
2
+ 'fullWidth': true
3
+ ---
4
+
5
+ {% from "components/header/_macro.njk" import onsHeader %}
6
+
7
+ {{
8
+ onsHeader({
9
+ "variants": 'basic',
10
+ "language": {
11
+ "languages": [
12
+ {
13
+ "url": '#0',
14
+ "isoCode": 'en',
15
+ "text": 'English',
16
+ "current": false
17
+ },
18
+ {
19
+ "url": '#0',
20
+ "isoCode": 'cy',
21
+ "text": 'Cymraeg',
22
+ "current": true
23
+ }
24
+ ]
25
+ },
26
+ "menuLinks": {
27
+ "id": "menu-links",
28
+ "keyLinks": [
29
+ {
30
+ 'heading': 'Taking part in a survey?',
31
+ 'description': 'It’s never been more important.'
32
+ },
33
+ {
34
+ 'heading': 'Release calendar',
35
+ 'description': 'View our latest and upcoming releases.'
36
+ },
37
+ {
38
+ 'heading': 'Explore local statistics',
39
+ 'url': '#0',
40
+ 'description': 'Explore statistics across the UK.'
41
+ }
42
+ ],
43
+ "columns": [
44
+ {
45
+ "groups": [
46
+ {
47
+ "heading": "People, population and community",
48
+ "groupItems": [
49
+ {
50
+ "text": "Armed forces community"
51
+ },
52
+ {
53
+ "text": "Births, deaths and marriages"
54
+ },
55
+ {
56
+ "text": "Crime and justice"
57
+ },
58
+ {
59
+ "text": "Cultural identity"
60
+ },
61
+ {
62
+ "text": "Education and childcare"
63
+ },
64
+ {
65
+ "text": "Elections"
66
+ },
67
+ {
68
+ "text": "Health and social care"
69
+ },
70
+ {
71
+ "text": "Household characteristics"
72
+ },
73
+ {
74
+ "text": "Housing"
75
+ },
76
+ {
77
+ "text": "Leisure and tourism"
78
+ },
79
+ {
80
+ "text": "Personal and household finances"
81
+ },
82
+ {
83
+ "text": "Population and migration"
84
+ },
85
+ {
86
+ "text": "Well-being"
87
+ }
88
+ ]
89
+ }
90
+ ]
91
+ },
92
+ {
93
+ "groups": [
94
+ {
95
+ "heading": "Business, industry and trade",
96
+ "groupItems": [
97
+ {
98
+ "text": "Business"
99
+ },
100
+ {
101
+ "text": "Changes to business"
102
+ },
103
+ {
104
+ "text": "Construction industry"
105
+ },
106
+ {
107
+ "text": "International trade"
108
+ },
109
+ {
110
+ "text": "IT and internet industry"
111
+ },
112
+ {
113
+ "text": "Manufacturing and production industry"
114
+ },
115
+ {
116
+ "text": "Retail industry",
117
+ "url": "#0"
118
+ },
119
+ {
120
+ "text": "Tourism industry"
121
+ }
122
+ ]
123
+ },
124
+ {
125
+ "heading": "Employment and labour market",
126
+ "url": "#0",
127
+ "groupItems":
128
+ [
129
+ {
130
+ "text": "People in work"
131
+ },
132
+ {
133
+ "text": "People not in work"
134
+ }
135
+ ]
136
+
137
+ }
138
+ ]
139
+ },
140
+ {
141
+ "groups": [
142
+ {
143
+ "heading": "Economy",
144
+ "groupItems": [
145
+ {
146
+ "text": "Economic output and productivity"
147
+ },
148
+ {
149
+ "text": "Government, public sector and taxes"
150
+ },
151
+ {
152
+ "text": "Gross Value Added (GVA)"
153
+ },
154
+ {
155
+ "text": "Investments, pensions and trusts"
156
+ },
157
+ {
158
+ "text": "Regional accounts"
159
+ },
160
+ {
161
+ "text": "Environmental accounts"
162
+ },
163
+ {
164
+ "text": "Gross Domestic Product (GDP)"
165
+ },
166
+ {
167
+ "text": "Inflation and price indices"
168
+ },
169
+ {
170
+ "text": "National accounts"
171
+ }
172
+ ]
173
+ }
174
+ ]
175
+ }
176
+ ]
177
+ },
178
+ "searchLinks": {
179
+ "id": "search-links",
180
+ "searchNavigationAriaLabel": 'Nav Search',
181
+ "searchButtonAriaLabel": 'Toggle search',
182
+ 'heading': 'Popular searches',
183
+ "itemsList": [
184
+ {
185
+ "url": '#1',
186
+ "text": 'Cost of living'
187
+ },
188
+ {
189
+ "url": '#1',
190
+ "text": 'Inflation'
191
+ },
192
+ {
193
+ "url": '#3',
194
+ "text": 'NHS waiting times'
195
+ },
196
+ {
197
+ "url": '#0',
198
+ "text": 'Wellbeing'
199
+ },
200
+ {
201
+ "url": '#0',
202
+ "text": 'Baby names'
203
+ }
204
+ ]
205
+ }
206
+ })
207
+ }}
@@ -8,6 +8,7 @@
8
8
  onsHeader({
9
9
  "variants": 'basic',
10
10
  "menuLinks": {
11
+ "id": "menu-links",
11
12
  "keyLinks": [
12
13
  {
13
14
  'heading': 'Taking part in a survey?',
@@ -8,6 +8,7 @@
8
8
  onsHeader({
9
9
  "variants": 'basic',
10
10
  "menuLinks": {
11
+ "id": "menu-links",
11
12
  "keyLinks": [
12
13
  {
13
14
  'heading': 'Taking part in a survey?',
@@ -94,10 +94,17 @@
94
94
  {% if params.informationPanel %}
95
95
  <div class="ons-hero__information ons-u-mt-s">
96
96
  <div class="ons-hero__panel ons-hero__panel--{{ params.informationPanel.panelType }}">
97
- {{- params.informationPanel.panelText -}}
97
+ <h2 class="ons-u-fs-r--b ons-u-m-no">{{- params.informationPanel.panelText -}}</h2>
98
98
  </div>
99
99
  <div class="ons-hero__link">
100
- <a href="{{ params.informationPanel.panelLink.url }}">{{ params.informationPanel.panelLink.text }}</a>
100
+ <a
101
+ href="{{ params.informationPanel.panelLink.url }}"
102
+ {% if params.informationPanel.panelLink.ariaLabel %}
103
+ aria-label="{{ params.informationPanel.panelLink.ariaLabel }}"
104
+ {% endif %}
105
+ >
106
+ {{ params.informationPanel.panelLink.text }}
107
+ </a>
101
108
  </div>
102
109
  </div>
103
110
  {% endif %}
@@ -176,6 +176,28 @@ describe('macro: hero', () => {
176
176
  expect($('.ons-hero__link > a').text().trim()).toBe('Some link text');
177
177
  expect($('.ons-hero__link > a').attr('href')).toBe('#0');
178
178
  });
179
+
180
+ it('renders the `informationPanel` link with a custom aria-label', () => {
181
+ const $ = cheerio.load(
182
+ renderComponent('hero', {
183
+ ...EXAMPLE_HERO,
184
+ variants: 'grey',
185
+ informationPanel: {
186
+ panelText: 'Some panel text',
187
+ panelType: 'ons-green',
188
+ panelLink: {
189
+ text: 'Some link text',
190
+ url: '#0',
191
+ ariaLabel: 'Some link aria label',
192
+ },
193
+ },
194
+ }),
195
+ );
196
+ const link = $('.ons-hero__link > a');
197
+ expect(link.attr('aria-label')).toBe('Some link aria label');
198
+ expect(link.text().trim()).toBe('Some link text');
199
+ expect(link.attr('href')).toBe('#0');
200
+ });
179
201
  });
180
202
 
181
203
  describe('and `panelType` is set to `ons-green`', () => {
@@ -14,7 +14,8 @@
14
14
  "panelType": 'ons-green',
15
15
  "panelLink": {
16
16
  "text": 'View previous releases',
17
- "url": '#0'
17
+ "url": '#0',
18
+ "ariaLabel": 'View previous release of Retail sales rise amid summer discounts and sporting events'
18
19
  }
19
20
  },
20
21
  "topic": 'Statistical article',
@@ -44,10 +44,9 @@
44
44
  {% if params.autocomplete %}autocomplete="{{ params.autocomplete }}"{% endif %}
45
45
  {% if params.accessiblePlaceholder %}placeholder="{{ params.label.text }}"{% endif %}
46
46
  {% if params.charCheckLimit %}
47
- data-char-check-ref="{{ params.id }}-check" data-char-check-num="{{ params.charCheckLimit.limit }}"
47
+ data-message-check-ref="{{ params.id }}-check" data-message-check-num="{{ params.charCheckLimit.limit }}"
48
48
  aria-describedby="{{ params.id }}-check"
49
49
  {% endif %}
50
- {% if params.charCheckLimit and params.charCheckLimit.charcheckCountdown %}data-char-check-countdown="true"{% endif %}
51
50
  {% if params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{ ' ' }}{{ attribute }}{% if value %}="{{ value }}"{% endif %}{% endfor %}{% endif %}
52
51
  {% if params.label and params.label.description %}{% if params.label.id %}aria-describedby="{{ params.label.id }}-description-hint"{% else %}aria-describedby="description-hint"{% endif %}{% endif %}
53
52
  />
@@ -570,36 +570,16 @@ describe('macro: input', () => {
570
570
  expect($('.ons-input').attr('maxlength')).toBe('200');
571
571
  });
572
572
 
573
- it('does not have `data-char-check-countdown` attribute when `charcheckCountdown` is not provided', () => {
574
- const $ = cheerio.load(renderComponent('input', EXAMPLE_INPUT_WITH_CHARACTER_LIMIT));
575
-
576
- expect($('.ons-input').attr('data-char-check-countdown')).toBe(undefined);
577
- });
578
-
579
- it('has `data-char-check-countdown` attribute when `charcheckCountdown` is true', () => {
580
- const $ = cheerio.load(
581
- renderComponent('input', {
582
- ...EXAMPLE_INPUT_WITH_CHARACTER_LIMIT,
583
- charCheckLimit: {
584
- ...EXAMPLE_INPUT_WITH_CHARACTER_LIMIT.charCheckLimit,
585
- charcheckCountdown: true,
586
- },
587
- }),
588
- );
589
-
590
- expect($('.ons-input').attr('data-char-check-countdown')).toBe('true');
591
- });
592
-
593
573
  it('has data attribute which references the character limit component', () => {
594
574
  const $ = cheerio.load(renderComponent('input', EXAMPLE_INPUT_WITH_CHARACTER_LIMIT));
595
575
 
596
- expect($('.ons-input').attr('data-char-check-ref')).toBe('example-id-check');
576
+ expect($('.ons-input').attr('data-message-check-ref')).toBe('example-id-check');
597
577
  });
598
578
 
599
579
  it('has data attribute which defines limit for character check', () => {
600
580
  const $ = cheerio.load(renderComponent('input', EXAMPLE_INPUT_WITH_CHARACTER_LIMIT));
601
581
 
602
- expect($('.ons-input').attr('data-char-check-num')).toBe('200');
582
+ expect($('.ons-input').attr('data-message-check-num')).toBe('200');
603
583
  });
604
584
 
605
585
  it('has `aria-describedby` attribute which references the character limit component', () => {
@@ -11,7 +11,6 @@
11
11
  },
12
12
  "width": "10",
13
13
  "charCheckLimit": {
14
- "charcheckCountdown": true,
15
14
  "limit": 10,
16
15
  "charCountSingular": "{x} more character needed",
17
16
  "charCountPlural": "{x} more characters needed",
@@ -11,7 +11,7 @@
11
11
  {% if language.attributes %}{% for attribute, value in (language.attributes) %}{{ ' ' }}{{ attribute }}="{{ value }}"{% endfor %}{% endif %}
12
12
  >
13
13
  {# Visually hidden helper text for screenreaders #}
14
- <span class="ons-u-vh">Change language to{{ ' ' }}</span>
14
+ <span class="ons-u-vh">{{ (language.srText | default("Change language to")) + ' ' }}</span>
15
15
  {% if language.abbrText %}
16
16
  <span class="ons-u-d-no@s">{{ language.abbrText }}</span><span class="ons-u-d-no@2xs@s">{{- language.text -}}</span>
17
17
  {% else %}
@@ -135,4 +135,49 @@ describe('macro: language-selector', () => {
135
135
  expect($('.ons-language-links').hasClass('ons-u-d-no@2xs@m')).toBe(true);
136
136
  });
137
137
  });
138
+
139
+ describe('srText parameter', () => {
140
+ it('renders custom srText when provided', () => {
141
+ const params = {
142
+ languages: [
143
+ {
144
+ url: '/welsh',
145
+ isoCode: 'cy',
146
+ text: 'Cymraeg',
147
+ srText: 'Custom screen reader text',
148
+ current: false,
149
+ },
150
+ {
151
+ url: '/english',
152
+ isoCode: 'en',
153
+ text: 'English',
154
+ current: true,
155
+ },
156
+ ],
157
+ };
158
+ const $ = cheerio.load(renderComponent('language-selector', params));
159
+ expect($('.ons-language-links__item a .ons-u-vh').text().trim()).toBe('Custom screen reader text');
160
+ });
161
+
162
+ it('renders default srText when not provided', () => {
163
+ const params = {
164
+ languages: [
165
+ {
166
+ url: '/welsh',
167
+ isoCode: 'cy',
168
+ text: 'Cymraeg',
169
+ current: false,
170
+ },
171
+ {
172
+ url: '/english',
173
+ isoCode: 'en',
174
+ text: 'English',
175
+ current: true,
176
+ },
177
+ ],
178
+ };
179
+ const $ = cheerio.load(renderComponent('language-selector', params));
180
+ expect($('.ons-language-links__item a .ons-u-vh').text().trim()).toBe('Change language to');
181
+ });
182
+ });
138
183
  });
@@ -82,9 +82,11 @@ export default class NavigationToggle {
82
82
  <span class="ons-btn__text"></span>
83
83
  </span>`,
84
84
  };
85
- toggle.innerHTML = isOpen ? icons.open : icons.close;
86
- toggle.classList.toggle('ons-btn--close', isOpen);
87
- toggle.classList.toggle('ons-btn--search-icon', !isOpen);
85
+ if (isOpen != null) {
86
+ toggle.innerHTML = isOpen ? icons.open : icons.close;
87
+ toggle.classList.toggle('ons-btn--close', isOpen);
88
+ toggle.classList.toggle('ons-btn--search-icon', !isOpen);
89
+ }
88
90
  }
89
91
 
90
92
  isHidden(el) {
@@ -115,22 +117,27 @@ export default class NavigationToggle {
115
117
  }
116
118
 
117
119
  toggleMenuAndSearch() {
118
- const isMenuOpen = this.menuBtn.getAttribute('aria-expanded') === 'true';
119
- const isSearchOpen = this.searchBtn.getAttribute('aria-expanded') === 'true';
120
-
121
- if (isMenuOpen && this.toggle == this.menuBtn) {
122
- this.updateSearchIcon(false, this.searchToggleBtn);
123
- this.searchBtn.setAttribute('aria-expanded', 'false');
124
- this.searchEl.setAttribute('aria-hidden', 'true');
125
- this.searchEl.classList.add('ons-u-d-no');
126
- this.searchToggleBtn.classList.remove('active');
120
+ if (this.menuBtn) {
121
+ const isMenuOpen = this.menuBtn.getAttribute('aria-expanded') === 'true';
122
+
123
+ if (isMenuOpen && this.toggle == this.menuBtn && this.searchBtn) {
124
+ this.updateSearchIcon(false, this.searchToggleBtn && this.searchBtn);
125
+ this.searchBtn.setAttribute('aria-expanded', 'false');
126
+ this.searchEl.setAttribute('aria-hidden', 'true');
127
+ this.searchEl.classList.add('ons-u-d-no');
128
+ this.searchToggleBtn.classList.remove('active');
129
+ }
127
130
  }
128
131
 
129
- if (isSearchOpen && this.toggle == this.searchToggleBtn) {
130
- this.menuBtn.setAttribute('aria-expanded', 'false');
131
- this.menuEl.setAttribute('aria-hidden', 'true');
132
- this.menuEl.classList.add('ons-u-d-no');
133
- this.menuBtn.classList.remove('active');
132
+ if (this.searchBtn) {
133
+ const isSearchOpen = this.searchBtn.getAttribute('aria-expanded') === 'true';
134
+
135
+ if (isSearchOpen && this.toggle == this.searchToggleBtn && this.menuBtn) {
136
+ this.menuBtn.setAttribute('aria-expanded', 'false');
137
+ this.menuEl.setAttribute('aria-hidden', 'true');
138
+ this.menuEl.classList.add('ons-u-d-no');
139
+ this.menuBtn.classList.remove('active');
140
+ }
134
141
  }
135
142
  }
136
143
  }
@@ -7,10 +7,12 @@
7
7
  {% set firstPage = params.pages | first %}
8
8
  {% set prevPageIndex = currentPageIndex - 1 %}
9
9
  {% set nextPageIndex = currentPageIndex + 1 %}
10
+ {% set prevPageAriaLabel = (params.previousAriaLabel or 'Go to the previous page') %}
11
+ {% set nextPageAriaLabel = (params.nextAriaLabel or 'Go to the next page') %}
10
12
 
11
13
  <nav
12
14
  class="ons-pagination{{ ' ' + params.classes if params.classes else '' }}{{ ' ons-pagination--no-indicator' if params.hideRangeIndicator }}"
13
- aria-label="Pagination ({{ position }})"
15
+ aria-label="{{ params.ariaLabel | default ('Pagination') }} ({{ position }})"
14
16
  >
15
17
  <div class="ons-pagination__position">{{ position }}</div>
16
18
  <ul class="ons-pagination__items{{ ' ons-pagination__items--no-previous' if currentPageIndex == 1 }}">
@@ -21,7 +23,7 @@
21
23
  href="{{ params.pages[prevPageIndex].url }}"
22
24
  class="ons-pagination__link ons-pagination__link--no-underline"
23
25
  rel="prev"
24
- aria-label="Go to the previous page (Page {{ currentPageIndex - 1 }})"
26
+ aria-label="{{ prevPageAriaLabel }} ({{ currentPageIndex - 1 }})"
25
27
  >
26
28
  {{-
27
29
  onsIcon({
@@ -36,7 +38,12 @@
36
38
  {% endif %}
37
39
  {% if currentPageIndex > 2 %}
38
40
  <li class="ons-pagination__item{{ ' ons-pagination__item--current' if currentPageIndex == 1 }}">
39
- <a href="{{ firstPage.url }}" class="ons-pagination__link" aria-label="Go to the first page (Page 1)">1</a>
41
+ <a
42
+ href="{{ firstPage.url }}"
43
+ class="ons-pagination__link"
44
+ aria-label="{{ params.firstAriaLabel | default('Go to the first page') }}"
45
+ >1</a
46
+ >
40
47
  </li>
41
48
  {% endif %}
42
49
  {% if currentPageIndex > 4 %}
@@ -56,9 +63,9 @@
56
63
  href="{{ page.url }}"
57
64
  class="ons-pagination__link"
58
65
  {%- if loop.index == currentPageIndex -%}
59
- aria-current="true" aria-label="Current page ({{ position }})"
66
+ aria-current="true" aria-label="{{ params.currentAriaLabel | default('Current page') }} ({{ position }})"
60
67
  {%- else -%}
61
- aria-label="Go to page {{ loop.index }}"
68
+ aria-label="{{ params.goToAriaLabel | default('Go to page') }} {{ loop.index }}"
62
69
  {%- endif -%}
63
70
  {%- if loop.index == currentPageIndex - 1 -%}
64
71
  rel="prev"
@@ -76,7 +83,10 @@
76
83
  {% endif %}
77
84
  {% if currentPageIndex < totalPages - 1 %}
78
85
  <li class="ons-pagination__item{{ ' ons-pagination__item--current' if currentPageIndex == totalPages }}">
79
- <a href="{{ lastPage.url }}" class="ons-pagination__link" aria-label="Go to the last page (Page {{ totalPages }})"
86
+ <a
87
+ href="{{ lastPage.url }}"
88
+ class="ons-pagination__link"
89
+ aria-label="{{ params.lastAriaLabel | default('Go to the last page') }} ({{ totalPages }})"
80
90
  >{{ totalPages }}</a
81
91
  >
82
92
  </li>
@@ -87,7 +97,7 @@
87
97
  href="{{ params.pages[currentPageIndex].url }}"
88
98
  class="ons-pagination__link ons-pagination__link--no-underline"
89
99
  rel="next"
90
- aria-label="Go to the next page (Page {{ currentPageIndex + 1 }})"
100
+ aria-label="{{ nextPageAriaLabel }} ({{ currentPageIndex + 1 }})"
91
101
  >
92
102
  <span class="ons-pagination__link-text">{{ params.next | default("Next") }}</span>
93
103
  {{-