@ons/design-system 72.9.1 → 72.9.2

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.
@@ -69,6 +69,7 @@ const EXAMPLE_TABLE_OF_CONTENTS_RELATED_LINKS_BUTTON = {
69
69
  ],
70
70
  relatedLinks: {
71
71
  title: 'Related publications',
72
+ ariaLabel: 'Related publications',
72
73
  itemsList: [
73
74
  {
74
75
  url: '#0',
@@ -209,6 +210,12 @@ describe('macro: table-of-contents', () => {
209
210
  expect($('.ons-table-of-contents__related-links h2').text().trim()).toBe('Related publications');
210
211
  });
211
212
 
213
+ it('renders related links section with correct aria-label', () => {
214
+ const $ = cheerio.load(renderComponent('table-of-contents', EXAMPLE_TABLE_OF_CONTENTS_RELATED_LINKS_BUTTON));
215
+
216
+ expect($('.ons-table-of-contents__related-links').attr('aria-label')).toBe('Related publications');
217
+ });
218
+
212
219
  it('outputs `lists` component for related links with expected parameters', () => {
213
220
  const faker = templateFaker();
214
221
  const listsSpy = faker.spy('list');
@@ -1,39 +1,56 @@
1
1
  export default class TableOfContents {
2
2
  constructor(component) {
3
3
  this.component = component;
4
- this.sections = [...this.component.querySelectorAll('section[id]')];
5
- this.refreshIntervalId = setInterval(() => this.setCurrent(), 100);
6
- this.setCurrent();
7
- }
4
+ this.sections = [...this.component.querySelectorAll('section[id]')].filter((section) => section.id);
5
+ this.tocLinks = {};
6
+ this.activeSection = null;
8
7
 
9
- setCurrent() {
10
- let activeSection = this.sections[0];
11
- for (let section of this.sections) {
12
- const top = section.getBoundingClientRect().top;
13
- if (top > 100) {
14
- break;
8
+ this.sections.forEach((section) => {
9
+ const link = this.component.querySelector(`.ons-list__link[href="#${section.id}"]`);
10
+ if (link) {
11
+ this.tocLinks[section.id] = link;
15
12
  }
13
+ });
16
14
 
17
- activeSection = section;
15
+ this.observer = new IntersectionObserver(this.handleIntersect.bind(this), {
16
+ rootMargin: '0px 0px -100% 0px', // trigger when top of section is at the top of viewport
17
+ });
18
18
 
19
- if (top >= 0 && top <= 100) {
20
- break;
21
- }
22
- }
19
+ this.sections.forEach((section) => this.observer.observe(section));
23
20
 
24
- if (activeSection === this.activeSection) {
25
- return;
26
- }
21
+ this.setInitialActiveSection();
22
+ }
27
23
 
28
- this.activeSection = activeSection;
24
+ setInitialActiveSection() {
25
+ const firstSection = this.sections[0];
26
+ if (!firstSection) return;
29
27
 
30
- for (let section of this.sections) {
31
- const tocItem = document.querySelector(`.ons-table-of-contents .ons-list__link[href="#${section.id}"]`);
32
- if (section === activeSection) {
33
- tocItem.classList.add('ons-table-of-contents__link-active');
34
- } else {
35
- tocItem.classList.remove('ons-table-of-contents__link-active');
36
- }
28
+ this.activeSection = firstSection;
29
+ this.updateTocLinks();
30
+ }
31
+
32
+ handleIntersect(entries) {
33
+ // Find the topmost visible section
34
+ const visibleSections = entries
35
+ .filter((entry) => entry.isIntersecting)
36
+ .sort((a, b) => a.target.getBoundingClientRect().top - b.target.getBoundingClientRect().top);
37
+
38
+ if (visibleSections.length === 0) return;
39
+
40
+ const newActive = visibleSections[0].target;
41
+
42
+ if (newActive === this.activeSection) return;
43
+
44
+ this.activeSection = newActive;
45
+ this.updateTocLinks();
46
+ }
47
+
48
+ updateTocLinks() {
49
+ for (const section of this.sections) {
50
+ const link = this.tocLinks[section.id];
51
+
52
+ // toggle active link class when link id matches current section id
53
+ link?.classList.toggle('ons-table-of-contents__link-active', section === this.activeSection);
37
54
  }
38
55
  }
39
56
  }