@ons/design-system 66.0.1 → 67.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/components/accordion/_macro.spec.js +2 -8
  2. package/components/accordion/accordion.dom.js +2 -2
  3. package/components/accordion/accordion.spec.js +22 -19
  4. package/components/accordion/example-accordion-open.njk +3 -3
  5. package/components/autosuggest/_macro.spec.js +2 -10
  6. package/components/button/_button.scss +23 -12
  7. package/components/button/button.js +12 -3
  8. package/components/card/_card.scss +1 -1
  9. package/components/checkboxes/_macro.njk +1 -1
  10. package/components/checkboxes/example-checkboxes-with-revealed-checkboxes-expanded.njk +1 -1
  11. package/components/checkboxes/example-checkboxes-with-revealed-checkboxes.njk +1 -1
  12. package/components/cookies-banner/_macro.njk +1 -1
  13. package/components/cookies-banner/_macro.spec.js +1 -1
  14. package/components/details/_details.scss +66 -57
  15. package/components/details/_macro.njk +4 -4
  16. package/components/details/_macro.spec.js +3 -10
  17. package/components/details/details.dom.js +1 -1
  18. package/components/details/details.js +13 -1
  19. package/components/details/details.spec.js +43 -11
  20. package/components/details/example-details-with-warning.njk +1 -2
  21. package/components/footer/_macro.njk +1 -1
  22. package/components/header/_macro.njk +2 -2
  23. package/components/header/_macro.spec.js +128 -5
  24. package/components/header/example-header-external-with-navigation-and-search.njk +57 -0
  25. package/components/icon/_macro.njk +4 -3
  26. package/components/label/_macro.njk +2 -2
  27. package/components/list/_list.scss +1 -1
  28. package/components/navigation/_macro.njk +3 -1
  29. package/components/related-content/_macro.njk +28 -30
  30. package/components/section-navigation/_macro.njk +56 -55
  31. package/components/video/_macro.njk +11 -11
  32. package/css/main.css +1 -1
  33. package/css/print.css +1 -1
  34. package/layout/_template.njk +29 -30
  35. package/package.json +1 -1
  36. package/scripts/main.es5.js +1 -1
  37. package/scripts/main.js +1 -1
  38. package/scss/objects/_page.scss +3 -5
  39. package/scss/print.scss +28 -5
  40. package/components/collapsible/_macro.njk +0 -22
  41. package/components/collapsible/_macro.spec.js +0 -151
@@ -57,10 +57,7 @@ describe('macro: accordion', () => {
57
57
  it('has provided title text', () => {
58
58
  const $ = cheerio.load(renderComponent('accordion', EXAMPLE_ACCORDION_WITH_TWO_ITEMS));
59
59
 
60
- const titleText = $('.ons-details__title')
61
- .first()
62
- .text()
63
- .trim();
60
+ const titleText = $('.ons-details__title').first().text().trim();
64
61
  expect(titleText).toBe('Title for item 1');
65
62
  });
66
63
 
@@ -84,10 +81,7 @@ describe('macro: accordion', () => {
84
81
  it('has provided content text', () => {
85
82
  const $ = cheerio.load(renderComponent('accordion', EXAMPLE_ACCORDION_WITH_TWO_ITEMS));
86
83
 
87
- const titleText = $('.ons-details__content')
88
- .first()
89
- .text()
90
- .trim();
84
+ const titleText = $('.ons-details__content').first().text().trim();
91
85
  expect(titleText).toBe('Content for item 1');
92
86
  });
93
87
 
@@ -8,9 +8,9 @@ async function initialiseAccordions() {
8
8
 
9
9
  const Details = (await import('../details/details')).default;
10
10
  const Accordion = (await import('./accordion')).default;
11
- const detailsEls = detailsComponents.map(element => new Details(element));
11
+ const detailsEls = detailsComponents.map((element) => new Details(element));
12
12
 
13
- toggleAllButtons.forEach(button => {
13
+ toggleAllButtons.forEach((button) => {
14
14
  new Accordion(button, detailsEls);
15
15
  });
16
16
  }
@@ -39,10 +39,11 @@ describe('script: accordion', () => {
39
39
  }),
40
40
  );
41
41
 
42
- const openElements = await page.$$eval('.ons-js-details', nodes => nodes.filter(node => node.open));
43
- expect(openElements[0]).not.toBe(undefined);
44
- expect(openElements[1]).not.toBe(undefined);
45
- expect(openElements[2]).not.toBe(undefined);
42
+ const detailsElementStates = await page.$$eval('.ons-js-details', (nodes) =>
43
+ nodes.map((node) => node.classList.contains('ons-details--open')),
44
+ );
45
+
46
+ expect(detailsElementStates).toEqual([true, true, true]);
46
47
  });
47
48
 
48
49
  it('sets toggle all button label to "Hide all" when open is specified', async () => {
@@ -54,7 +55,7 @@ describe('script: accordion', () => {
54
55
  }),
55
56
  );
56
57
 
57
- const buttonText = await page.$eval('button[data-test-trigger]', element => element.innerText);
58
+ const buttonText = await page.$eval('button[data-test-trigger]', (element) => element.innerText);
58
59
  expect(buttonText.trim()).toBe('Close all');
59
60
  });
60
61
 
@@ -67,7 +68,7 @@ describe('script: accordion', () => {
67
68
  }),
68
69
  );
69
70
 
70
- const ariaExpanded = await page.$eval('button[data-test-trigger]', element => element.getAttribute('aria-expanded'));
71
+ const ariaExpanded = await page.$eval('button[data-test-trigger]', (element) => element.getAttribute('aria-expanded'));
71
72
  expect(ariaExpanded).toBe('true');
72
73
  });
73
74
 
@@ -76,10 +77,11 @@ describe('script: accordion', () => {
76
77
 
77
78
  await page.click('button[data-test-trigger]');
78
79
 
79
- const openElements = await page.$$eval('.ons-js-details', nodes => nodes.filter(node => node.open));
80
- expect(openElements[0]).not.toBe(undefined);
81
- expect(openElements[1]).not.toBe(undefined);
82
- expect(openElements[2]).not.toBe(undefined);
80
+ const detailsElementStates = await page.$$eval('.ons-js-details', (nodes) =>
81
+ nodes.map((node) => node.classList.contains('ons-details--open')),
82
+ );
83
+
84
+ expect(detailsElementStates).toEqual([true, true, true]);
83
85
  });
84
86
 
85
87
  it('closes all items when accordion `allbutton` is clicked twice', async () => {
@@ -88,23 +90,24 @@ describe('script: accordion', () => {
88
90
  await page.click('button[data-test-trigger]');
89
91
  await page.click('button[data-test-trigger]');
90
92
 
91
- const openElements = await page.$$eval('.ons-js-details', nodes => nodes.filter(node => node.open));
92
- expect(openElements[0]).toBe(undefined);
93
- expect(openElements[1]).toBe(undefined);
94
- expect(openElements[2]).toBe(undefined);
93
+ const detailsElementStates = await page.$$eval('.ons-js-details', (nodes) =>
94
+ nodes.map((node) => node.classList.contains('ons-details--open')),
95
+ );
96
+
97
+ expect(detailsElementStates).toEqual([false, false, false]);
95
98
  });
96
99
 
97
100
  it('starts with the toggle all button labelled as "Open all"', async () => {
98
101
  await setTestPage('/test', renderComponent('accordion', EXAMPLE_ACCORDION_WITH_ALL_BUTTON));
99
102
 
100
- const buttonText = await page.$eval('button[data-test-trigger]', element => element.innerText);
103
+ const buttonText = await page.$eval('button[data-test-trigger]', (element) => element.innerText);
101
104
  expect(buttonText.trim()).toBe('Open all');
102
105
  });
103
106
 
104
107
  it('starts with the toggle all button aria-expanded set to false', async () => {
105
108
  await setTestPage('/test', renderComponent('accordion', EXAMPLE_ACCORDION_WITH_ALL_BUTTON));
106
109
 
107
- const ariaExpanded = await page.$eval('button[data-test-trigger]', element => element.getAttribute('aria-expanded'));
110
+ const ariaExpanded = await page.$eval('button[data-test-trigger]', (element) => element.getAttribute('aria-expanded'));
108
111
  expect(ariaExpanded).toBe('false');
109
112
  });
110
113
 
@@ -113,7 +116,7 @@ describe('script: accordion', () => {
113
116
 
114
117
  await page.click('button[data-test-trigger]');
115
118
 
116
- const buttonText = await page.$eval('button[data-test-trigger]', element => element.innerText);
119
+ const buttonText = await page.$eval('button[data-test-trigger]', (element) => element.innerText);
117
120
  expect(buttonText.trim()).toBe('Close all');
118
121
  });
119
122
 
@@ -122,7 +125,7 @@ describe('script: accordion', () => {
122
125
 
123
126
  await page.click('button[data-test-trigger]');
124
127
 
125
- const ariaExpanded = await page.$eval('button[data-test-trigger]', element => element.getAttribute('aria-expanded'));
128
+ const ariaExpanded = await page.$eval('button[data-test-trigger]', (element) => element.getAttribute('aria-expanded'));
126
129
  expect(ariaExpanded).toBe('true');
127
130
  });
128
131
 
@@ -133,7 +136,7 @@ describe('script: accordion', () => {
133
136
  await page.click('#example-accordion-2 .ons-details__heading');
134
137
  await page.click('#example-accordion-3 .ons-details__heading');
135
138
 
136
- const buttonText = await page.$eval('button[data-test-trigger]', element => element.innerText);
139
+ const buttonText = await page.$eval('button[data-test-trigger]', (element) => element.innerText);
137
140
  expect(buttonText.trim()).toBe('Close all');
138
141
  });
139
142
  });
@@ -62,7 +62,7 @@
62
62
  "text": "Areas (0)"
63
63
  },
64
64
  "value": "areas"
65
- }
65
+ }
66
66
  ]
67
67
  })
68
68
  }}
@@ -98,14 +98,14 @@
98
98
  "text": "Disability (67)"
99
99
  },
100
100
  "value": "disability"
101
- }
101
+ }
102
102
  ]
103
103
  })
104
104
  }}
105
105
  {% endset %}
106
106
 
107
107
  <div class="ons-grid">
108
- <div class="ons-grid__col ons-col-3@xs">
108
+ <div class="ons-grid__col ons-col-4@m">
109
109
  {{
110
110
  onsAccordion({
111
111
  "id": "accordion",
@@ -264,11 +264,7 @@ describe('macro: autosuggest', () => {
264
264
  it('renders div with the provided title text', () => {
265
265
  const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
266
266
 
267
- expect(
268
- $('.ons-autosuggest__results-title')
269
- .text()
270
- .trim(),
271
- ).toBe('Suggestions');
267
+ expect($('.ons-autosuggest__results-title').text().trim()).toBe('Suggestions');
272
268
  });
273
269
 
274
270
  it('renders list with a generated identifier', () => {
@@ -298,11 +294,7 @@ describe('macro: autosuggest', () => {
298
294
  it('renders instructions text', () => {
299
295
  const $ = cheerio.load(renderComponent('autosuggest', EXAMPLE_AUTOSUGGEST));
300
296
 
301
- expect(
302
- $('.ons-autosuggest__instructions')
303
- .text()
304
- .trim(),
305
- ).toBe('Use up and down keys to navigate.');
297
+ expect($('.ons-autosuggest__instructions').text().trim()).toBe('Use up and down keys to navigate.');
306
298
  });
307
299
  });
308
300
  });
@@ -78,13 +78,15 @@ $button-shadow-size: 3px;
78
78
  }
79
79
  }
80
80
 
81
- &:focus:hover:not(:active) &__inner {
81
+ &:focus:hover:not(:active, .active) &__inner {
82
82
  background: var(--ons-color-focus-dark);
83
83
  }
84
84
 
85
85
  // When down
86
86
  &:active &,
87
- &:active:focus & {
87
+ &:active:focus &,
88
+ &.active &,
89
+ &.active:focus & {
88
90
  &__inner {
89
91
  background: var(--ons-color-button);
90
92
  box-shadow: none;
@@ -96,7 +98,8 @@ $button-shadow-size: 3px;
96
98
  }
97
99
  }
98
100
 
99
- &:active {
101
+ &:active,
102
+ &.active {
100
103
  top: ems($button-shadow-size);
101
104
  }
102
105
 
@@ -132,7 +135,9 @@ $button-shadow-size: 3px;
132
135
 
133
136
  &--secondary &,
134
137
  &--secondary:active &,
135
- &--secondary:active:focus & {
138
+ &--secondary.active &,
139
+ &--secondary:active:focus &,
140
+ &--secondary.active:focus & {
136
141
  &__inner {
137
142
  background: var(--ons-color-button-secondary);
138
143
  color: var(--ons-color-text);
@@ -169,8 +174,8 @@ $button-shadow-size: 3px;
169
174
  text-decoration: none;
170
175
  }
171
176
 
172
- &--link:focus:not(:active, &--secondary) &,
173
- &--link:focus:hover:not(:active, &--secondary) & {
177
+ &--link:focus:not(:active, .active, &--secondary) &,
178
+ &--link:focus:hover:not(:active, .active, &--secondary) & {
174
179
  outline: inherit;
175
180
 
176
181
  &__inner {
@@ -238,8 +243,8 @@ $button-shadow-size: 3px;
238
243
  }
239
244
 
240
245
  &--text-link:focus &,
241
- &--text-link.active:focus &,
242
- &--text-link:active:focus & {
246
+ &--text-link:active:focus &,
247
+ &--text-link.active:focus & {
243
248
  &__inner {
244
249
  background-color: var(--ons-color-focus);
245
250
  box-shadow: 0 -2px var(--ons-color-focus),
@@ -288,7 +293,9 @@ $button-shadow-size: 3px;
288
293
  }
289
294
 
290
295
  &--ghost:active:focus,
291
- &--ghost-dark:active:focus {
296
+ &--ghost.active:focus
297
+ &--ghost-dark:active:focus
298
+ &--ghost-dark.active:focus {
292
299
  box-shadow: none;
293
300
  outline: 3px solid transparent;
294
301
  }
@@ -403,7 +410,8 @@ $button-shadow-size: 3px;
403
410
  }
404
411
  }
405
412
 
406
- &--loader.ons-is-loading:active & {
413
+ &--loader.ons-is-loading:active &,
414
+ &--loader.ons-is-loading.active & {
407
415
  &__inner {
408
416
  box-shadow: 0 ems($button-shadow-size) 0 var(--ons-color-button-shadow);
409
417
  }
@@ -448,7 +456,8 @@ $button-shadow-size: 3px;
448
456
  cursor: not-allowed;
449
457
  }
450
458
 
451
- &--disabled:active & {
459
+ &--disabled:active &,
460
+ &--disabled.active & {
452
461
  &__inner {
453
462
  box-shadow: 0 ems($button-shadow-size) 0 var(--ons-color-button-shadow);
454
463
  }
@@ -496,7 +505,9 @@ $button-shadow-size: 3px;
496
505
  }
497
506
 
498
507
  &--dropdown:active &,
499
- &--dropdown:active:focus & {
508
+ &--dropdown.active &,
509
+ &--dropdown:active:focus &,
510
+ &--dropdown.active:focus & {
500
511
  &__inner {
501
512
  background: var(--ons-color-branded-secondary);
502
513
  color: var(--ons-color-white);
@@ -18,7 +18,8 @@ export default class SubmitButton {
18
18
  this.button.addEventListener('click', this.timerButton.bind(this));
19
19
  }
20
20
  } else if (this.submitType == 'link') {
21
- this.button.addEventListener('keydown', this.linkButton.bind(this));
21
+ this.button.addEventListener('keydown', this.linkButtonDown.bind(this));
22
+ this.button.addEventListener('keyup', this.linkButtonUp.bind(this));
22
23
  }
23
24
  }
24
25
 
@@ -48,10 +49,18 @@ export default class SubmitButton {
48
49
  );
49
50
  }
50
51
 
51
- linkButton(e) {
52
- if (e.keyCode == 32) {
52
+ linkButtonDown(e){
53
+ if (e.keyCode == 32 || e.keyCode == 13){
54
+ this.button.classList.add("active");
55
+ }
56
+ }
57
+
58
+ linkButtonUp(e){
59
+ if (e.keyCode == 32 || e.keyCode == 13) {
60
+ this.button.classList.remove("active");
53
61
  e.preventDefault();
54
62
  this.button.click();
55
63
  }
56
64
  }
65
+
57
66
  }
@@ -11,7 +11,7 @@
11
11
  & > .ons-card__title {
12
12
  margin-bottom: 0;
13
13
  }
14
- }
14
+ }
15
15
 
16
16
  &__link:hover {
17
17
  text-decoration-thickness: 3px;
@@ -7,7 +7,7 @@
7
7
  {% set fields %}
8
8
  {% if params.checkboxesLabel is defined %}
9
9
  <p class="ons-checkboxes__label{{ " " + params.checkboxesLabelClasses if params.checkboxesLabelClasses }}">{{ params.checkboxesLabel }}</p>
10
- {% endif %}
10
+ {% endif %}
11
11
  {% set hasOther = false %}
12
12
  {% for checkbox in params.checkboxes %}
13
13
  {% if checkbox.other %}
@@ -82,7 +82,7 @@
82
82
  "text": "Areas (0)"
83
83
  },
84
84
  "value": "areas"
85
- }
85
+ }
86
86
  ]
87
87
  })
88
88
  }}
@@ -80,7 +80,7 @@
80
80
  "text": "Areas (0)"
81
81
  },
82
82
  "value": "areas"
83
- }
83
+ }
84
84
  ]
85
85
  })
86
86
  }}
@@ -28,7 +28,7 @@
28
28
  {% set acceptedText = 'You have accepted all additional cookies.' %}
29
29
  {% set rejectedText = 'You have rejected all additional cookies.' %}
30
30
  {% set confirmationButtonText = 'Hide' %}
31
- {% set contextSuffix = 'this message' %}
31
+ {% set contextSuffix = 'cookie message' %}
32
32
  {% set beforeLinkPreferencesURL = 'You can' %}
33
33
  {% set afterLinkPreferencesURL = 'change your cookie preferences</a> at any time.' %}
34
34
  {% set beforeLinkStatementText = '<p>Cookies are small files stored on your device when you visit a website. We use some essential cookies to make this website work.</p><p>We would like to set' %}
@@ -215,7 +215,7 @@ describe('macro: cookies-banner', () => {
215
215
 
216
216
  faker.renderComponent('cookies-banner', {});
217
217
 
218
- expect(buttonSpy.occurrences[2].buttonContext).toBe('this message');
218
+ expect(buttonSpy.occurrences[2].buttonContext).toBe('cookie message');
219
219
  });
220
220
 
221
221
  it('has `container--wide` class when `wide` is true', () => {
@@ -1,75 +1,81 @@
1
1
  $details-caret-width: 1.5rem;
2
2
 
3
3
  .ons-details {
4
- &__heading {
5
- color: var(--ons-color-text-link);
6
- cursor: pointer;
7
- display: inline-block;
8
- outline: none;
9
- padding: 0 0 0 $details-caret-width;
10
- pointer-events: initial;
11
- position: relative;
12
-
13
- &::marker,
14
- &::-webkit-details-marker {
15
- display: none;
16
- }
17
-
18
- &:focus {
19
- .ons-details__title {
20
- @extend %a-focus;
21
- // extend details focus background behind caret
22
- margin-left: -$details-caret-width;
23
- padding-left: $details-caret-width;
24
- }
25
- .ons-details__icon .ons-icon {
26
- fill: var(--ons-color-text-link-focus);
4
+ &--initialised & {
5
+ &__heading {
6
+ color: var(--ons-color-text-link);
7
+ cursor: pointer;
8
+ display: inline-block;
9
+ outline: none;
10
+ padding: 0 0 0 $details-caret-width;
11
+ pointer-events: initial;
12
+ position: relative;
13
+
14
+ &::marker,
15
+ &::-webkit-details-marker {
16
+ display: none;
27
17
  }
28
- }
29
18
 
30
- &:hover:not(:focus) {
31
- color: var(--ons-color-text-link-hover);
32
- .ons-details__icon {
33
- fill: var(--ons-color-text-link-hover);
19
+ &:focus {
20
+ .ons-details__title {
21
+ @extend %a-focus;
22
+ // extend details focus background behind caret
23
+ margin-left: -$details-caret-width;
24
+ padding-left: $details-caret-width;
25
+ }
26
+ .ons-details__icon .ons-icon {
27
+ fill: var(--ons-color-text-link-focus);
28
+ }
34
29
  }
35
- .ons-details__title {
36
- text-decoration: underline solid var(--ons-color-text-link-hover) 2px;
30
+
31
+ &:hover:not(:focus) {
32
+ color: var(--ons-color-text-link-hover);
33
+ .ons-details__icon {
34
+ fill: var(--ons-color-text-link-hover);
35
+ }
36
+ .ons-details__title {
37
+ text-decoration: underline solid var(--ons-color-text-link-hover) 2px;
38
+ }
37
39
  }
38
40
  }
39
- }
40
41
 
41
- &__icon {
42
- display: inline-block;
43
- fill: var(--ons-color-text-link);
44
- height: $details-caret-width;
45
- left: -0.15rem;
46
- position: absolute;
47
- top: -0.2rem;
48
- width: $details-caret-width;
49
- }
42
+ &__icon {
43
+ display: inline-block;
44
+ fill: var(--ons-color-text-link);
45
+ height: $details-caret-width;
46
+ left: -0.15rem;
47
+ position: absolute;
48
+ top: -0.2rem;
49
+ width: $details-caret-width;
50
+ }
50
51
 
51
- &__title {
52
- display: inline-block;
53
- font-size: 1rem;
54
- font-weight: $font-weight-bold;
55
- margin-bottom: 0;
56
- text-underline-position: under;
57
- transform: translateY(-1px);
58
- }
52
+ &__content {
53
+ display: none;
54
+ }
59
55
 
60
- &__content {
61
- border-left: 4px solid var(--ons-color-borders-indent);
62
- display: block;
63
- margin: 1rem 0 0;
64
- padding: 0 0 0 1.3em;
56
+ &__title {
57
+ display: inline-block;
58
+ font-size: 1rem;
59
+ font-weight: $font-weight-bold;
60
+ margin-bottom: 0;
61
+ text-underline-position: under;
62
+ transform: translateY(-1px);
63
+ }
65
64
  }
66
65
 
67
- &[open] & {
66
+ &--open & {
68
67
  &__icon {
69
68
  left: -0.1rem;
70
69
  top: 0.2rem;
71
70
  transform: rotate(90deg);
72
71
  }
72
+
73
+ &__content {
74
+ border-left: 4px solid var(--ons-color-borders-indent);
75
+ display: block;
76
+ margin: 1rem 0 0;
77
+ padding: 0 0 0 1.3em;
78
+ }
73
79
  }
74
80
 
75
81
  &--accordion & {
@@ -110,12 +116,15 @@ $details-caret-width: 1.5rem;
110
116
  padding: 0;
111
117
  }
112
118
  }
113
- }
114
119
 
115
- .ons-details--accordion {
116
- &[open] {
120
+ &--accordion.ons-details--open {
117
121
  .ons-details__icon {
122
+ position: absolute;
118
123
  top: 1.2rem;
119
124
  }
120
125
  }
126
+
127
+ &__icon {
128
+ display: none;
129
+ }
121
130
  }
@@ -1,5 +1,5 @@
1
1
  {% macro onsDetails(params) %}
2
- <details
2
+ <div
3
3
  id="{{ params.id }}"
4
4
  class="ons-details ons-js-details{% if params.isAccordion %} ons-details--accordion{% endif %}{% if params.classes %} {{ params.classes }}{% endif %}"
5
5
  {% if params.group %} data-group="{{ params.group }}"{% endif %}
@@ -7,7 +7,7 @@
7
7
  {% if params.saveState %} data-save-state="true"{% endif %}
8
8
  {% if params.open %} data-open="true"{% endif %}
9
9
  >
10
- <summary
10
+ <div
11
11
  class="ons-details__heading ons-js-details-heading"
12
12
  {% if params.headingAttributes %}{% for attribute, value in (params.headingAttributes.items() if params.headingAttributes is mapping and params.headingAttributes.items else params.headingAttributes) %}{{ attribute }}{% if value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
13
13
  >
@@ -20,11 +20,11 @@
20
20
  "iconType": "chevron"
21
21
  })
22
22
  }}</span>
23
- </summary>
23
+ </div>
24
24
  <div id="{{ params.id }}-content" class="ons-details__content ons-js-details-content"
25
25
  {% if params.contentAttributes %}{% for attribute, value in (params.contentAttributes.items() if params.contentAttributes is mapping and params.contentAttributes.items else params.contentAttributes) %}{{ attribute }}{% if value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
26
26
  >
27
27
  {{ params.content | safe }}{{ caller() if caller }}
28
28
  </div>
29
- </details>
29
+ </div>
30
30
  {% endmacro %}
@@ -40,10 +40,7 @@ describe('macro: details', () => {
40
40
  it('has provided title text', () => {
41
41
  const $ = cheerio.load(renderComponent('details', EXAMPLE_DETAILS_BASIC));
42
42
 
43
- const titleText = $('.ons-details__title')
44
- .first()
45
- .text()
46
- .trim();
43
+ const titleText = $('.ons-details__title').first().text().trim();
47
44
  expect(titleText).toBe('Title for details');
48
45
  });
49
46
 
@@ -62,9 +59,7 @@ describe('macro: details', () => {
62
59
  it('has provided content text', () => {
63
60
  const $ = cheerio.load(renderComponent('details', EXAMPLE_DETAILS_BASIC));
64
61
 
65
- const titleText = $('.ons-details__content')
66
- .text()
67
- .trim();
62
+ const titleText = $('.ons-details__content').text().trim();
68
63
  expect(titleText).toEqual(expect.stringContaining('Content for details'));
69
64
  });
70
65
 
@@ -143,9 +138,7 @@ describe('macro: details', () => {
143
138
  it('calls with content', () => {
144
139
  const $ = cheerio.load(renderComponent('details', { EXAMPLE_DETAILS_BASIC }, 'Example content...'));
145
140
 
146
- const content = $('.ons-details__content')
147
- .text()
148
- .trim();
141
+ const content = $('.ons-details__content').text().trim();
149
142
  expect(content).toEqual(expect.stringContaining('Example content...'));
150
143
  });
151
144
  });
@@ -6,7 +6,7 @@ async function initialiseDetailsEls() {
6
6
 
7
7
  if (detailsComponents.length && !accordionComponents.length) {
8
8
  const Details = (await import('./details')).default;
9
- detailsComponents.map(element => new Details(element));
9
+ detailsComponents.map((element) => new Details(element));
10
10
  }
11
11
  }
12
12
 
@@ -7,17 +7,25 @@ export default class Details {
7
7
  // Elements
8
8
  this.details = detailsElement;
9
9
  this.detailsHeader = this.details.querySelector('.ons-js-details-heading');
10
+ this.content = this.details.querySelector('.ons-js-details-content');
10
11
 
11
12
  // Initialise
12
13
  const detailsId = detailsElement.getAttribute('id');
13
14
 
15
+ this.details.setAttribute('role', 'group');
16
+ this.detailsHeader.setAttribute('role', 'link');
17
+ this.detailsHeader.setAttribute('aria-controls', detailsId);
18
+ this.detailsHeader.setAttribute('tabindex', 0);
19
+
14
20
  if (localStorage.getItem(detailsId) || this.open) {
15
21
  this.setOpen(true);
16
- this.details['setAttribute']('open', '');
22
+ } else {
23
+ this.setOpen(false);
17
24
  }
18
25
 
19
26
  this.detailsHeader.addEventListener('click', this.toggle.bind(this));
20
27
  this.detailsHeader.addEventListener('keydown', this.keyboardInteraction.bind(this));
28
+ this.details.classList.add('ons-details--initialised');
21
29
  }
22
30
 
23
31
  toggle(event) {
@@ -28,10 +36,14 @@ export default class Details {
28
36
  setOpen(open) {
29
37
  if (open !== this.isOpen) {
30
38
  const action = open ? 'Open' : 'Close';
39
+ const cls = open ? 'add' : 'remove';
31
40
  const openAttribute = open ? 'set' : 'remove';
32
41
 
33
42
  this.isOpen = open;
34
43
  this.details[`${openAttribute}Attribute`]('open', '');
44
+ this.details.classList[cls]('ons-details--open');
45
+ this.detailsHeader.setAttribute('aria-expanded', open);
46
+ this.content.setAttribute('aria-hidden', !open);
35
47
  this.detailsHeader.setAttribute('data-ga-action', `${action} panel`);
36
48
 
37
49
  if (this.onOpen && this.onClose) {