@ons/design-system 72.4.0 → 72.6.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 (62) hide show
  1. package/components/button/_button.scss +171 -3
  2. package/components/button/_macro.njk +4 -0
  3. package/components/card/_card.scss +5 -0
  4. package/components/card/_macro.njk +10 -2
  5. package/components/card/_macro.spec.js +58 -0
  6. package/components/card/example-card-set-with-headline-figures.njk +62 -0
  7. package/components/chart/_chart.scss +80 -0
  8. package/components/chart/_macro.njk +125 -0
  9. package/components/chart/_macro.spec.js +825 -0
  10. package/components/chart/annotations-options.js +78 -0
  11. package/components/chart/bar-chart.js +164 -0
  12. package/components/chart/chart-constants.js +21 -0
  13. package/components/chart/chart.dom.js +8 -0
  14. package/components/chart/chart.js +242 -0
  15. package/components/chart/column-chart.js +48 -0
  16. package/components/chart/common-chart-options.js +247 -0
  17. package/components/chart/example-bar-chart-with-annotations.njk +62 -0
  18. package/components/chart/example-bar-chart.njk +55 -0
  19. package/components/chart/example-bar-with-line-chart.njk +64 -0
  20. package/components/chart/example-clustered-bar-chart.njk +60 -0
  21. package/components/chart/example-clustered-column-chart.njk +74 -0
  22. package/components/chart/example-column-chart-with-annotations.njk +62 -0
  23. package/components/chart/example-column-chart.njk +54 -0
  24. package/components/chart/example-column-with-line-chart.njk +62 -0
  25. package/components/chart/example-line-chart-with-annotations.njk +235 -0
  26. package/components/chart/example-line-chart.njk +221 -0
  27. package/components/chart/example-stacked-bar-chart.njk +60 -0
  28. package/components/chart/example-stacked-column-chart.njk +67 -0
  29. package/components/chart/line-chart.js +42 -0
  30. package/components/chart/specific-chart-options.js +22 -0
  31. package/components/footer/_footer.scss +12 -0
  32. package/components/footer/_macro.njk +38 -17
  33. package/components/header/_header.scss +18 -2
  34. package/components/header/_macro.njk +81 -8
  35. package/components/header/_macro.spec.js +97 -0
  36. package/components/header/example-header-with-search-button.njk +190 -0
  37. package/components/header/header.spec.js +128 -0
  38. package/components/hero/_hero.scss +39 -7
  39. package/components/hero/_macro.njk +47 -17
  40. package/components/hero/_macro.spec.js +94 -0
  41. package/components/hero/example-hero-grey.njk +9 -0
  42. package/components/icon/_macro.njk +8 -8
  43. package/components/input/_input.scss +15 -0
  44. package/components/input/_macro.njk +3 -3
  45. package/components/navigation/navigation.dom.js +17 -0
  46. package/components/navigation/navigation.js +72 -2
  47. package/components/pagination/_pagination.scss +7 -1
  48. package/components/summary/_macro.njk +1 -1
  49. package/components/summary/_macro.spec.js +6 -0
  50. package/components/table-of-contents/_macro.njk +40 -0
  51. package/components/table-of-contents/_macro.spec.js +72 -0
  52. package/components/table-of-contents/_table-of-contents.scss +11 -0
  53. package/components/table-of-contents/example-table-of-contents-related-links-with-button.njk +60 -0
  54. package/css/main.css +1 -1
  55. package/js/cookies-functions.js +11 -6
  56. package/js/cookies-functions.spec.js +44 -0
  57. package/js/main.js +2 -0
  58. package/package.json +4 -1
  59. package/scripts/main.es5.js +1 -1
  60. package/scripts/main.js +1 -1
  61. package/scss/main.scss +1 -0
  62. package/scss/utilities/_grid.scss +13 -4
@@ -0,0 +1,128 @@
1
+ import { renderComponent, setTestPage } from '../../tests/helpers/rendering';
2
+ import { EXAMPLE_HEADER_SEARCH_AND_MENU_LINKS } from './_test-examples';
3
+ import { getNodeAttributes } from '../../tests/helpers/puppeteer';
4
+
5
+ describe('script: header', () => {
6
+ beforeEach(async () => {
7
+ await setTestPage('/test', renderComponent('header', { ...EXAMPLE_HEADER_SEARCH_AND_MENU_LINKS, variants: 'basic' }));
8
+ });
9
+
10
+ describe('when the search button is clicked', () => {
11
+ beforeEach(async () => {
12
+ await page.click('.ons-js-toggle-header-search');
13
+ });
14
+
15
+ it('then the search input form is visible and does not have the ons-u-d-no class', async () => {
16
+ await page.waitForSelector('.ons-header-nav-search');
17
+ const isSearchNavVisible = await page.$eval('.ons-header-nav-search', (el) => !el.classList.contains('ons-u-d-no'));
18
+ expect(isSearchNavVisible).toBe(true);
19
+ });
20
+
21
+ it('then the button switches to the close icon', async () => {
22
+ const hasCloseIcon = await page.$eval('.ons-js-toggle-header-search svg', (el) => el.classList.contains('ons-icon--close'));
23
+ expect(hasCloseIcon).toBe(true);
24
+ });
25
+
26
+ it('then the button has aria-expanded="true"', async () => {
27
+ const attributes = await getNodeAttributes(page, '.ons-js-toggle-header-search');
28
+ expect(attributes['aria-expanded']).toBe('true');
29
+ });
30
+
31
+ it('then the button has aria-hidden="false"', async () => {
32
+ const attributes = await getNodeAttributes(page, '.ons-js-header-search');
33
+ expect(attributes['aria-hidden']).toBe('false');
34
+ });
35
+
36
+ it('then the search field has full width', async () => {
37
+ await page.waitForSelector('.ons-header-nav-search__input .ons-input');
38
+ const isFullWidth = await page.$eval('.ons-header-nav-search__input .ons-input', (el) =>
39
+ el.classList.contains('ons-input--w-full'),
40
+ );
41
+ expect(isFullWidth).toBe(true);
42
+ });
43
+ });
44
+
45
+ describe('when the search button is not clicked', () => {
46
+ it('then the search form is hidden', async () => {
47
+ const isSearchNavHidden = await page.$eval('.ons-header-nav-search', (el) => el.classList.contains('ons-u-d-no'));
48
+ expect(isSearchNavHidden).toBe(true);
49
+ });
50
+
51
+ it('then the button displays the search icon', async () => {
52
+ const hasSearchIcon = await page.$eval('.ons-js-toggle-header-search svg', (el) => el.classList.contains('ons-icon--search'));
53
+ expect(hasSearchIcon).toBe(true);
54
+ });
55
+
56
+ it('then the button has aria-expanded="false"', async () => {
57
+ const attributes = await getNodeAttributes(page, '.ons-js-toggle-header-search');
58
+ expect(attributes['aria-expanded']).toBe('false');
59
+ });
60
+
61
+ it('then the button has aria-hidden="true"', async () => {
62
+ const attributes = await getNodeAttributes(page, '.ons-js-header-search');
63
+ expect(attributes['aria-hidden']).toBe('true');
64
+ });
65
+ });
66
+
67
+ describe('when the search button is clicked again (to close)', () => {
68
+ beforeEach(async () => {
69
+ await page.click('.ons-js-toggle-header-search'); // Open
70
+ await page.waitForSelector('.ons-header-nav-search');
71
+ await page.click('.ons-js-toggle-header-search'); // Close
72
+ });
73
+
74
+ it('then the search form is hidden', async () => {
75
+ const isSearchNavHidden = await page.$eval('.ons-header-nav-search', (el) => el.classList.contains('ons-u-d-no'));
76
+ expect(isSearchNavHidden).toBe(true);
77
+ });
78
+
79
+ it('then the button switches back to the search icon', async () => {
80
+ const hasSearchIcon = await page.$eval('.ons-js-toggle-header-search svg', (el) => el.classList.contains('ons-icon--search'));
81
+ expect(hasSearchIcon).toBe(true);
82
+ });
83
+
84
+ it('then the button has aria-expanded="false"', async () => {
85
+ const attributes = await getNodeAttributes(page, '.ons-js-toggle-header-search');
86
+ expect(attributes['aria-expanded']).toBe('false');
87
+ });
88
+ });
89
+
90
+ describe('when the menu button is clicked', () => {
91
+ beforeEach(async () => {
92
+ await page.click('.ons-btn--menu');
93
+ });
94
+
95
+ it('then the navigation menu is displayed', async () => {
96
+ await page.waitForSelector('.ons-header-nav-menu');
97
+ const isMenuNavVisible = await page.$eval('.ons-header-nav-menu', (el) => !el.classList.contains('ons-u-d-no'));
98
+ expect(isMenuNavVisible).toBe(true);
99
+ });
100
+
101
+ it('then the menu button is marked as expanded', async () => {
102
+ const menuButtonAriaExpanded = await page.$eval('.ons-btn--menu', (el) => el.getAttribute('aria-expanded'));
103
+ expect(menuButtonAriaExpanded).toBe('true');
104
+ });
105
+
106
+ it('then the navigation menu aria-hidden attribute is set to false', async () => {
107
+ const isAriaHiddenFalse = await page.$eval('.ons-header-nav-menu', (el) => el.getAttribute('aria-hidden') === 'false');
108
+ expect(isAriaHiddenFalse).toBe(true);
109
+ });
110
+ });
111
+
112
+ describe('when the menu button is not clicked', () => {
113
+ it('then the navigation menu is hidden', async () => {
114
+ const isMenuNavHidden = await page.$eval('.ons-header-nav-menu', (el) => el.classList.contains('ons-u-d-no'));
115
+ expect(isMenuNavHidden).toBe(true);
116
+ });
117
+
118
+ it('then the menu button is marked as not expanded', async () => {
119
+ const menuButtonAriaExpanded = await page.$eval('.ons-btn--menu', (el) => el.getAttribute('aria-expanded'));
120
+ expect(menuButtonAriaExpanded).toBe('false');
121
+ });
122
+
123
+ it('then the navigation menu aria-hidden attribute is set to true', async () => {
124
+ const isAriaHiddenTrue = await page.$eval('.ons-header-nav-menu', (el) => el.getAttribute('aria-hidden') === 'true');
125
+ expect(isAriaHiddenTrue).toBe(true);
126
+ });
127
+ });
128
+ });
@@ -1,4 +1,14 @@
1
1
  // Default
2
+ @mixin panel-variant($name, $color, $color-bg) {
3
+ .ons-hero__panel {
4
+ &--#{$name} {
5
+ background: $color-bg;
6
+ border-color: $color !important;
7
+ outline: 1px solid transparent; // Add transparent outline because Windows High Contrast Mode doesn't show background
8
+ }
9
+ }
10
+ }
11
+
2
12
  .ons-hero {
3
13
  background-color: var(--ons-color-hero-bg);
4
14
  display: flex;
@@ -70,12 +80,10 @@
70
80
  }
71
81
  }
72
82
  }
73
- &__badge {
74
- @include mq(l) {
75
- position: absolute;
76
- right: 0;
77
- top: 85px;
78
- }
83
+
84
+ &__badge-link {
85
+ display: block;
86
+ width: fit-content;
79
87
  }
80
88
 
81
89
  &--topic {
@@ -95,7 +103,27 @@
95
103
  }
96
104
 
97
105
  &__content {
98
- height: 100%;
106
+ @include mq(l) {
107
+ display: flex;
108
+ justify-content: space-between;
109
+ align-items: flex-start;
110
+ }
111
+ }
112
+
113
+ &__information {
114
+ display: flex;
115
+ flex-wrap: wrap;
116
+ column-gap: 1rem;
117
+
118
+ & .ons-hero__panel {
119
+ padding: 0.5rem 1rem;
120
+ border-left: 5px solid transparent;
121
+ @extend .ons-u-fs-r--b;
122
+ }
123
+
124
+ & .ons-hero__link {
125
+ padding: 0.5rem 0;
126
+ }
99
127
  }
100
128
 
101
129
  &__pre-title {
@@ -434,3 +462,7 @@
434
462
  }
435
463
  }
436
464
  }
465
+
466
+ @include panel-variant(ons-red, var(--ons-color-errors), var(--ons-color-errors-tint));
467
+ @include panel-variant(ons-green, var(--ons-color-success), var(--ons-color-success-tint));
468
+ @include panel-variant(ons-orange, var(--ons-color-pending), var(--ons-color-pending-tint));
@@ -37,27 +37,47 @@
37
37
  {% if params.topic %}
38
38
  <h2 class="ons-hero--topic">{{ params.topic | safe }}</h2>
39
39
  {% endif %}
40
- <div class="ons-hero__title-container">
41
- <header>
42
- <h1 class="ons-hero__title ons-u-fs-3xl">{{ params.title }}</h1>
43
- {% if params.subtitle %}
44
- <h2 class="ons-hero__subtitle ons-u-fs-r--b">{{ params.subtitle }}</h2>
40
+
41
+ <div class="ons-hero__content">
42
+ <div class="ons-hero__title-container">
43
+ <header>
44
+ <h1 class="ons-hero__title ons-u-fs-3xl">{{ params.title }}</h1>
45
+ {% if params.subtitle %}
46
+ <h2 class="ons-hero__subtitle ons-u-fs-r--b">{{ params.subtitle }}</h2>
47
+ {% endif %}
48
+ </header>
49
+
50
+ {% if params.text %}
51
+ <p class="ons-hero__text">{{ params.text | safe }}</p>
45
52
  {% endif %}
46
- </header>
47
- {% if params.text %}
48
- <p class="ons-hero__text">{{ params.text | safe }}</p>
49
- {% endif %}
50
- </div>
51
- {% if params.variants == "grey" %}
52
- {% if params.officialStatisticsBadge == true %}
53
+ </div>
54
+
55
+ {% if params.variants == "grey" and params.officialStatisticsBadge == true %}
53
56
  <div class="ons-hero__badge ons-u-mb-s">
54
- {{-
55
- onsIcon({
56
- "iconType": 'official-statistics'
57
- })
58
- -}}
57
+ {% if params.officialStatisticsBadgeUrl %}
58
+ <a
59
+ href="{{ params.officialStatisticsBadgeUrl }}"
60
+ target="_blank"
61
+ rel="noopener noreferrer"
62
+ class="ons-hero__badge-link"
63
+ >
64
+ {{-
65
+ onsIcon({
66
+ "iconType": 'official-statistics'
67
+ })
68
+ -}}
69
+ </a>
70
+ {% else %}
71
+ {{-
72
+ onsIcon({
73
+ "iconType": 'official-statistics'
74
+ })
75
+ -}}
76
+ {% endif %}
59
77
  </div>
60
78
  {% endif %}
79
+ </div>
80
+ {% if params.variants == "grey" %}
61
81
  {% if params.censusLogo == true %}
62
82
  <div class="ons-hero__census-logo">
63
83
  {{-
@@ -67,6 +87,16 @@
67
87
  -}}
68
88
  </div>
69
89
  {% endif %}
90
+ {% if params.informationPanel %}
91
+ <div class="ons-hero__information ons-u-mt-s">
92
+ <div class="ons-hero__panel ons-hero__panel--{{ params.informationPanel.panelType }}">
93
+ {{- params.informationPanel.panelText -}}
94
+ </div>
95
+ <div class="ons-hero__link">
96
+ <a href="{{ params.informationPanel.panelLink.url }}">{{ params.informationPanel.panelLink.text }}</a>
97
+ </div>
98
+ </div>
99
+ {% endif %}
70
100
  {% endif %}
71
101
 
72
102
  {% if params.button %}
@@ -112,6 +112,22 @@ describe('macro: hero', () => {
112
112
  expect($('.ons-hero__badge svg title').text().trim()).toBe('Offical Statistics Badge');
113
113
  });
114
114
 
115
+ it('outputs the statistics badge as a link when officialStatisticsBadgeUrl is provided', () => {
116
+ const $ = cheerio.load(
117
+ renderComponent('hero', {
118
+ ...EXAMPLE_HERO,
119
+ variants: 'grey',
120
+ officialStatisticsBadge: true,
121
+ officialStatisticsBadgeUrl: 'https://example.com/badge',
122
+ }),
123
+ );
124
+
125
+ expect($('.ons-hero__badge-link').length).toBe(1);
126
+ expect($('.ons-hero__badge-link').attr('href')).toBe('https://example.com/badge');
127
+ expect($('.ons-hero__badge-link').attr('target')).toBe('_blank');
128
+ expect($('.ons-hero__badge-link').attr('rel')).toBe('noopener noreferrer');
129
+ });
130
+
115
131
  it('outputs the Census 2021 Logo when censusLogo is set to true and variants is set to "grey"', () => {
116
132
  const $ = cheerio.load(renderComponent('hero', { ...EXAMPLE_HERO, variants: 'grey', censusLogo: true }));
117
133
 
@@ -136,6 +152,84 @@ describe('macro: hero', () => {
136
152
  expect($('.ons-hero--grey').length).toBe(1);
137
153
  });
138
154
 
155
+ describe('when `informationPanel` is provided and the variant is set to `grey`', () => {
156
+ describe('and `panelText` and `panelLink` are provided', () => {
157
+ it('renders the information panel with correct text and link`', () => {
158
+ const $ = cheerio.load(
159
+ renderComponent('hero', {
160
+ ...EXAMPLE_HERO,
161
+ variants: 'grey',
162
+ informationPanel: {
163
+ panelText: 'Some panel text',
164
+ panelType: 'ons-green',
165
+ panelLink: {
166
+ text: 'Some link text',
167
+ url: '#0',
168
+ },
169
+ },
170
+ }),
171
+ );
172
+
173
+ expect($('.ons-hero__information').length).toBe(1);
174
+ expect($('.ons-hero__panel').length).toBe(1);
175
+ expect($('.ons-hero__panel').text().trim()).toBe('Some panel text');
176
+ expect($('.ons-hero__link > a').text().trim()).toBe('Some link text');
177
+ expect($('.ons-hero__link > a').attr('href')).toBe('#0');
178
+ });
179
+ });
180
+
181
+ describe('and `panelType` is set to `ons-green`', () => {
182
+ it('renders the green information panel`', () => {
183
+ const $ = cheerio.load(
184
+ renderComponent('hero', {
185
+ ...EXAMPLE_HERO,
186
+ variants: 'grey',
187
+ informationPanel: {
188
+ panelText: 'Some panel text',
189
+ panelType: 'ons-green',
190
+ },
191
+ }),
192
+ );
193
+
194
+ expect($('.ons-hero__panel').hasClass('ons-hero__panel--ons-green')).toBe(true);
195
+ });
196
+ });
197
+
198
+ describe('and `panelType` is set to `ons-red`', () => {
199
+ it('renders the red information panel`', () => {
200
+ const $ = cheerio.load(
201
+ renderComponent('hero', {
202
+ ...EXAMPLE_HERO,
203
+ variants: 'grey',
204
+ informationPanel: {
205
+ panelText: 'Some panel text',
206
+ panelType: 'ons-red',
207
+ },
208
+ }),
209
+ );
210
+
211
+ expect($('.ons-hero__panel').hasClass('ons-hero__panel--ons-red')).toBe(true);
212
+ });
213
+ });
214
+
215
+ describe('and `panelType` is set to `ons-orange`', () => {
216
+ it('renders the orange information panel`', () => {
217
+ const $ = cheerio.load(
218
+ renderComponent('hero', {
219
+ ...EXAMPLE_HERO,
220
+ variants: 'grey',
221
+ informationPanel: {
222
+ panelText: 'Some panel text',
223
+ panelType: 'ons-orange',
224
+ },
225
+ }),
226
+ );
227
+
228
+ expect($('.ons-hero__panel').hasClass('ons-hero__panel--ons-orange')).toBe(true);
229
+ });
230
+ });
231
+ });
232
+
139
233
  it('outputs the correct breadcrumbs', () => {
140
234
  const $ = cheerio.load(
141
235
  renderComponent('hero', {
@@ -8,6 +8,15 @@
8
8
  "variants": 'grey',
9
9
  "detailsColumns": '12',
10
10
  "officialStatisticsBadge": true,
11
+ "officialStatisticsBadgeUrl": 'https://uksa.statisticsauthority.gov.uk/about-the-authority/uk-statistical-system/types-of-official-statistics/',
12
+ "informationPanel":{
13
+ "panelText": 'Latest release',
14
+ "panelType": 'ons-green',
15
+ "panelLink": {
16
+ "text": 'View previous releases',
17
+ "url": '#0'
18
+ }
19
+ },
11
20
  "topic": 'Statistical article',
12
21
  "title": 'Retail sales rise amid summer discounts and sporting events, according to a first estimate',
13
22
  "text": 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum id mattis ligula, nec sollicitudin arcu. Donec non tristique tellus. Donec eget malesuada lorem.',
@@ -203,7 +203,7 @@
203
203
  </svg>
204
204
  {%- elif params.iconType == "search" -%}
205
205
  <svg
206
- class="{% if params.iconSize %}ons-icon ons-icon--{{ params.iconSize }}{% else %}ons-icon{% endif %}{{ iconClasses }}"
206
+ class="{% if params.iconSize %}ons-icon ons-icon--search ons-icon--{{ params.iconSize }}{% else %}ons-icon{% endif %}{{ iconClasses }}"
207
207
  viewBox="0 0 12 12"
208
208
  xmlns="http://www.w3.org/2000/svg"
209
209
  aria-hidden="true"
@@ -345,8 +345,8 @@
345
345
  <svg
346
346
  class="ons-icon--logo{{ iconClasses }}"
347
347
  xmlns="http://www.w3.org/2000/svg"
348
- width="208"
349
- height="20"
348
+ width="250"
349
+ height="24"
350
350
  viewBox="33 2 552 60"
351
351
  aria-labelledby="{{ params.altTextId | default ('ons-logo-en-alt') }}"
352
352
  role="img"
@@ -442,8 +442,8 @@
442
442
  <svg
443
443
  class="ons-icon--logo{{ iconClasses }}"
444
444
  xmlns="http://www.w3.org/2000/svg"
445
- width="218"
446
- height="20"
445
+ width="262"
446
+ height="24"
447
447
  viewBox="15 2 620 60"
448
448
  aria-labelledby="{{ params.altTextId | default ('ons-logo-cy-alt') }}"
449
449
  role="img"
@@ -701,9 +701,9 @@
701
701
  {%- elif params.iconType == "official-statistics" -%}
702
702
  <svg
703
703
  xmlns="http://www.w3.org/2000/svg"
704
- width="77"
705
- height="93"
706
- viewBox="0 0 77 93"
704
+ width="75"
705
+ height="90"
706
+ viewBox="-6 0 90 90"
707
707
  fill="none"
708
708
  title="ons-official-statistics-badge"
709
709
  focusable="false"
@@ -86,6 +86,16 @@
86
86
  margin-top: 0.25rem;
87
87
  }
88
88
 
89
+ .ons-input--header-search {
90
+ border-radius: 0;
91
+ height: 56px;
92
+ border: 2px solid var(--ons-color-ocean-blue);
93
+
94
+ &:focus {
95
+ border: 2px solid var(--ons-color-input-border);
96
+ }
97
+ }
98
+
89
99
  .ons-input--select {
90
100
  appearance: none;
91
101
  background: var(--ons-color-input-bg)
@@ -149,6 +159,11 @@
149
159
  height: 2.5rem;
150
160
  }
151
161
 
162
+ .ons-input__button--header-search {
163
+ gap: 0;
164
+ height: 100%;
165
+ }
166
+
152
167
  .ons-input-search {
153
168
  @extend .ons-input--block;
154
169
  @extend .ons-input--ghost;
@@ -25,7 +25,7 @@
25
25
  <input
26
26
  type="{{ type }}"
27
27
  id="{{ params.id }}"
28
- class="ons-input ons-input--text ons-input-type__input{{ ' ons-input--error' if params.error }}{{ ' ons-search__input' if params.searchButton }}{{ ' ' + params.classes if params.classes else '' }}{% if params.width %}{{ ' ' }}ons-input{% if params.type == 'number' or params.type == 'tel' %}-number{% endif %}--w-{{ params.width }}{% endif %}{{ exclusiveClass }}{{ inputPlaceholder }}"
28
+ class="ons-input ons-input--text ons-input-type__input{{ ' ons-input--error' if params.error }}{{ ' ons-search__input' if params.searchButton }}{{ ' ons-input--header-search' if params.searchButton.variant == 'header' }}{{ ' ' + params.classes if params.classes else '' }}{% if params.width %}{{ ' ' }}ons-input{% if params.type == 'number' or params.type == 'tel' %}-number{% endif %}--w-{{ params.width }}{% endif %}{{ exclusiveClass }}{{ inputPlaceholder }}"
29
29
  {% if params.prefix and params.prefix.id and not params.prefix.title %}aria-labelledby="{{ params.prefix.id }}"{% elif params.suffix and params.suffix.id and not params.suffix.title %}aria-labelledby="{{ params.suffix.id }}"{% endif %}
30
30
  {% if params.prefix and params.prefix.id %}
31
31
  aria-labelledby="{{ params.id }} {{ params.prefix.id }}"
@@ -110,7 +110,7 @@
110
110
  {% endif %}
111
111
  {% elif params.searchButton %}
112
112
  <span
113
- class="ons-grid-flex ons-grid-flex--vertical-top ons-input_search-button{{ ' ons-input--with-text-description' if params.label.description }}"
113
+ class="ons-grid-flex ons-grid-flex--vertical-top ons-input_search-button{{ ' ons-input__button--header-search' if params.searchButton.variant == 'header' }}{{ ' ons-input--with-text-description' if params.label.description }}"
114
114
  >
115
115
  {% if params.accessiblePlaceholder %}
116
116
  {{
@@ -142,7 +142,7 @@
142
142
  "html": buttonLabel,
143
143
  "text": params.searchButton.text,
144
144
  "id": params.searchButton.id,
145
- "variants": 'small',
145
+ "variants": 'header-search' if params.searchButton.variant == 'header' else 'small',
146
146
  "classes": 'ons-search__btn' + (" " + params.searchButton.classes if params.searchButton.classes else ""),
147
147
  "attributes": params.searchButton.attributes,
148
148
  "iconType": params.searchButton.iconType,
@@ -13,6 +13,11 @@ domready(async () => {
13
13
  const toggleServicesBtn = document.querySelector('.ons-js-toggle-services');
14
14
  const servicesEl = document.querySelector('.ons-js-services-mobile-nav');
15
15
  const servicesHideClass = 'ons-u-d-no';
16
+ const toggleHeaderSearchBtn = document.querySelector('.ons-js-toggle-header-search');
17
+ const headerSearchHideClass = 'ons-u-d-no';
18
+ const headerSearchEl = document.querySelector('.ons-js-header-search');
19
+ const menuEl = document.querySelector('.ons-js-nav-menu');
20
+ const toggleHeaderMenuBtn = document.querySelector('.ons-js-toggle-nav-menu');
16
21
 
17
22
  if (toggleNavigationBtn) {
18
23
  const NavigationToggle = (await import('./navigation')).default;
@@ -31,6 +36,18 @@ domready(async () => {
31
36
  new searchToggle(toggleSearchBtn, searchEl, searchHideClass).registerEvents();
32
37
  }
33
38
 
39
+ if (toggleHeaderSearchBtn) {
40
+ const searchToggle = (await import('./navigation')).default;
41
+
42
+ new searchToggle(toggleHeaderSearchBtn, headerSearchEl, headerSearchHideClass).registerEvents();
43
+ }
44
+
45
+ if (toggleHeaderMenuBtn) {
46
+ const searchToggle = (await import('./navigation')).default;
47
+
48
+ new searchToggle(toggleHeaderMenuBtn, menuEl, headerSearchHideClass).registerEvents();
49
+ }
50
+
34
51
  if (toggleServicesBtn) {
35
52
  const servicesToggle = (await import('./navigation')).default;
36
53
 
@@ -8,6 +8,7 @@ export default class NavigationToggle {
8
8
  this.toggle = toggle;
9
9
  this.navigation = navigation;
10
10
  this.hideClass = hideClass;
11
+
11
12
  this.toggle.classList.remove('ons-u-d-no');
12
13
  this.setAria();
13
14
  onViewportChange(this.setAria.bind(this));
@@ -25,7 +26,6 @@ export default class NavigationToggle {
25
26
  openNav() {
26
27
  const input = [...this.navigation.getElementsByTagName('INPUT')][0];
27
28
 
28
- this.toggle.setAttribute(attrExpanded, 'true');
29
29
  this.toggle.classList.add('active');
30
30
  this.navigation.setAttribute(attrHidden, 'false');
31
31
  this.navigation.classList.remove(this.hideClass);
@@ -33,13 +33,57 @@ export default class NavigationToggle {
33
33
  if (input) {
34
34
  input.focus();
35
35
  }
36
+
37
+ const searchToggleBtn = document.querySelector('.ons-js-toggle-header-search');
38
+ if (this.toggle === searchToggleBtn) {
39
+ this.updateSearchIcon(true, this.toggle);
40
+ }
41
+
42
+ this.toggle.setAttribute(attrExpanded, 'true');
43
+
44
+ this.toggleMenuAndSearch();
36
45
  }
37
46
 
38
47
  closeNav() {
39
- this.toggle.setAttribute(attrExpanded, 'false');
40
48
  this.toggle.classList.remove('active');
41
49
  this.navigation.setAttribute(attrHidden, 'true');
42
50
  this.navigation.classList.add(this.hideClass);
51
+
52
+ const searchToggleBtn = document.querySelector('.ons-js-toggle-header-search');
53
+ if (this.toggle === searchToggleBtn) {
54
+ this.updateSearchIcon(false, this.toggle);
55
+ }
56
+
57
+ this.toggle.setAttribute(attrExpanded, 'false');
58
+ }
59
+
60
+ updateSearchIcon(isOpen, toggle) {
61
+ const searchIconSVG = `
62
+ <span class="ons-btn__inner">
63
+ <svg class="ons-icon ons-icon--search ons-u-mr-2xs" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" role="img" title="ons-icon-search">
64
+ <path d="M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z"></path>
65
+ </svg>
66
+ <span class="ons-btn__text"></span>
67
+ </span>`;
68
+
69
+ const closeIconSVG = `
70
+ <span class="ons-btn__inner">
71
+ <svg class="ons-icon ons-icon--close" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" focusable="false" fill="currentColor" role="img" title="ons-icon-close">
72
+ <path d="M12 0C5.4 0 0 5.4 0 12C0 18.6 5.4 24 12 24C18.6 24 24 18.6 24 12C24 5.4 18.6 0 12 0ZM17.3143 15.5143C17.6571 15.8571 17.6571 16.3714 17.3143 16.7143L16.7143 17.3143C16.3714 17.6571 15.8571 17.6571 15.5143 17.3143L12 13.8L8.48571 17.3143C8.14286 17.6571 7.62857 17.6571 7.28571 17.3143L6.68571 16.7143C6.34286 16.3714 6.34286 15.8571 6.68571 15.5143L10.2 12L6.68571 8.48571C6.34286 8.14286 6.34286 7.62857 6.68571 7.28571L7.28571 6.68571C7.62857 6.34286 8.14286 6.34286 8.48571 6.68571L12 10.2L15.5143 6.68571C15.8571 6.34286 16.3714 6.34286 16.7143 6.68571L17.3143 7.28571C17.6571 7.62857 17.6571 8.14286 17.3143 8.48571L13.8 12L17.3143 15.5143Z"/>
73
+ </svg>
74
+ <span class="ons-btn__text"></span>
75
+ </span>`;
76
+
77
+ if (isOpen) {
78
+ toggle.classList.add('ons-btn--close');
79
+ this.toggle.classList.remove('ons-btn--search-icon');
80
+ this.toggle.innerHTML = closeIconSVG;
81
+ }
82
+ if (!isOpen) {
83
+ toggle.classList.remove('ons-btn--close');
84
+ toggle.classList.add('ons-btn--search-icon');
85
+ toggle.innerHTML = searchIconSVG;
86
+ }
43
87
  }
44
88
 
45
89
  isHidden(el) {
@@ -64,4 +108,30 @@ export default class NavigationToggle {
64
108
  }
65
109
  }
66
110
  }
111
+
112
+ toggleMenuAndSearch() {
113
+ const menuBtn = document.querySelector('.ons-js-toggle-nav-menu');
114
+ const menuEl = document.querySelector('.ons-js-nav-menu');
115
+ const searchToggle = document.querySelector('.ons-js-toggle-header-search');
116
+ const searchBtn = document.querySelector('.ons-btn--search');
117
+ const searchEl = document.querySelector('.ons-js-header-search');
118
+
119
+ const isMenuOpen = menuBtn.getAttribute('aria-expanded') === 'true';
120
+ const isSearchOpen = searchBtn.getAttribute('aria-expanded') === 'true';
121
+
122
+ if (isMenuOpen && this.toggle == menuBtn) {
123
+ this.updateSearchIcon(false, searchToggle);
124
+ searchBtn.setAttribute('aria-expanded', 'false');
125
+ searchEl.setAttribute('aria-hidden', 'true');
126
+ searchEl.classList.add('ons-u-d-no');
127
+ searchToggle.classList.remove('active');
128
+ }
129
+
130
+ if (isSearchOpen && this.toggle == searchToggle) {
131
+ menuBtn.setAttribute('aria-expanded', 'false');
132
+ menuEl.setAttribute('aria-hidden', 'true');
133
+ menuEl.classList.add('ons-u-d-no');
134
+ menuBtn.classList.remove('active');
135
+ }
136
+ }
67
137
  }
@@ -7,7 +7,6 @@ $pagination-item-width: 2.5rem;
7
7
  }
8
8
 
9
9
  &__items {
10
- margin: 0 $pagination-item-padding * -1;
11
10
  padding: 0;
12
11
  }
13
12
 
@@ -17,6 +16,7 @@ $pagination-item-width: 2.5rem;
17
16
 
18
17
  &--previous,
19
18
  &--next {
19
+ margin: 0 $pagination-item-padding * -1;
20
20
  display: inline-block;
21
21
  }
22
22
 
@@ -25,6 +25,12 @@ $pagination-item-width: 2.5rem;
25
25
  display: inline-block;
26
26
  }
27
27
  }
28
+
29
+ @include mq('2xs', m) {
30
+ &--previous ~ &--next {
31
+ margin: 0;
32
+ }
33
+ }
28
34
  }
29
35
 
30
36
  &__item,