@madgex/design-system 13.6.4 → 13.7.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.
- package/dist/css/index.css +1 -1
- package/dist/js/index.js +1 -1
- package/package.json +1 -1
- package/src/components/scroll-spy/README.md +34 -0
- package/src/components/scroll-spy/scroll-spy.js +127 -0
- package/src/components/scroll-spy/scroll-spy.njk +184 -0
- package/src/components/scroll-spy/scroll-spy.spec.js +462 -0
- package/src/js/index.js +4 -0
- package/src/scss/core/_lists.scss +21 -0
- package/src/scss/helpers/__index.scss +1 -0
- package/src/scss/helpers/_position.scss +4 -0
- package/src/typography/lists.njk +24 -11
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
// eslint-disable-next-line n/no-unpublished-import
|
|
2
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
3
|
+
import { MdsScrollSpy } from './scroll-spy.js';
|
|
4
|
+
|
|
5
|
+
// Polyfill CSS.escape for jsdom (not available by default)
|
|
6
|
+
if (typeof CSS === 'undefined' || !CSS.escape) {
|
|
7
|
+
globalThis.CSS = {
|
|
8
|
+
escape: (str) => str.replace(/([^\w-])/g, (match) => `\\${match}`),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Mock IntersectionObserver
|
|
13
|
+
let lastObserverInstance = null;
|
|
14
|
+
|
|
15
|
+
class MockIntersectionObserver {
|
|
16
|
+
constructor(callback, options) {
|
|
17
|
+
this.callback = callback;
|
|
18
|
+
this.options = options;
|
|
19
|
+
this.observedElements = [];
|
|
20
|
+
lastObserverInstance = this;
|
|
21
|
+
}
|
|
22
|
+
observe(el) {
|
|
23
|
+
this.observedElements.push(el);
|
|
24
|
+
}
|
|
25
|
+
unobserve(el) {
|
|
26
|
+
this.observedElements = this.observedElements.filter((e) => e !== el);
|
|
27
|
+
}
|
|
28
|
+
disconnect() {
|
|
29
|
+
this.observedElements = [];
|
|
30
|
+
}
|
|
31
|
+
// Helper to simulate intersection events
|
|
32
|
+
triggerIntersect(entries) {
|
|
33
|
+
this.callback(entries, this);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
vi.stubGlobal('IntersectionObserver', MockIntersectionObserver);
|
|
38
|
+
|
|
39
|
+
// Register the custom element
|
|
40
|
+
if (!customElements.get('mds-scroll-spy')) {
|
|
41
|
+
customElements.define('mds-scroll-spy', MdsScrollSpy);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe('MdsScrollSpy', () => {
|
|
45
|
+
let container;
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
container = document.createElement('div');
|
|
49
|
+
document.body.appendChild(container);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
container.remove();
|
|
54
|
+
vi.restoreAllMocks();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Helper to create a scroll-spy and attach it to the container with links.
|
|
59
|
+
* Uses innerHTML to ensure proper URL resolution in jsdom.
|
|
60
|
+
*/
|
|
61
|
+
function createScrollSpyWithLinks(anchors) {
|
|
62
|
+
const linksHtml = anchors.map(({ href, text }) => `<a href="${href}">${text}</a>`).join('');
|
|
63
|
+
container.innerHTML = `<mds-scroll-spy>${linksHtml}</mds-scroll-spy>`;
|
|
64
|
+
return container.querySelector('mds-scroll-spy');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ------------------------------
|
|
68
|
+
// Constructor
|
|
69
|
+
// ------------------------------
|
|
70
|
+
describe('constructor', () => {
|
|
71
|
+
it('creates instance without errors', () => {
|
|
72
|
+
const scrollSpy = new MdsScrollSpy();
|
|
73
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
74
|
+
expect(scrollSpy).toBeInstanceOf(HTMLElement);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// ------------------------------
|
|
79
|
+
// observer-threshold attribute parsing (tested via behavior)
|
|
80
|
+
// ------------------------------
|
|
81
|
+
describe('observer-threshold attribute', () => {
|
|
82
|
+
it('accepts single numeric threshold value', () => {
|
|
83
|
+
const section = document.createElement('section');
|
|
84
|
+
section.id = 'test-section';
|
|
85
|
+
document.body.appendChild(section);
|
|
86
|
+
|
|
87
|
+
container.innerHTML = `
|
|
88
|
+
<mds-scroll-spy observer-threshold="0.5">
|
|
89
|
+
<a href="#test-section">Link</a>
|
|
90
|
+
</mds-scroll-spy>
|
|
91
|
+
`;
|
|
92
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
93
|
+
|
|
94
|
+
// Element should initialize without errors
|
|
95
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
96
|
+
|
|
97
|
+
section.remove();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('accepts JSON array threshold value', () => {
|
|
101
|
+
const section = document.createElement('section');
|
|
102
|
+
section.id = 'test-section';
|
|
103
|
+
document.body.appendChild(section);
|
|
104
|
+
|
|
105
|
+
container.innerHTML = `
|
|
106
|
+
<mds-scroll-spy observer-threshold="[0, 0.5, 1]">
|
|
107
|
+
<a href="#test-section">Link</a>
|
|
108
|
+
</mds-scroll-spy>
|
|
109
|
+
`;
|
|
110
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
111
|
+
|
|
112
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
113
|
+
|
|
114
|
+
section.remove();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('warns and uses default for invalid threshold', () => {
|
|
118
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
119
|
+
const section = document.createElement('section');
|
|
120
|
+
section.id = 'test-section';
|
|
121
|
+
document.body.appendChild(section);
|
|
122
|
+
|
|
123
|
+
container.innerHTML = `
|
|
124
|
+
<mds-scroll-spy observer-threshold="invalid">
|
|
125
|
+
<a href="#test-section">Link</a>
|
|
126
|
+
</mds-scroll-spy>
|
|
127
|
+
`;
|
|
128
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
129
|
+
|
|
130
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
131
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Invalid observer-threshold'));
|
|
132
|
+
|
|
133
|
+
section.remove();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('warns and uses default for empty array threshold', () => {
|
|
137
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
138
|
+
const section = document.createElement('section');
|
|
139
|
+
section.id = 'test-section';
|
|
140
|
+
document.body.appendChild(section);
|
|
141
|
+
|
|
142
|
+
container.innerHTML = `
|
|
143
|
+
<mds-scroll-spy observer-threshold="[]">
|
|
144
|
+
<a href="#test-section">Link</a>
|
|
145
|
+
</mds-scroll-spy>
|
|
146
|
+
`;
|
|
147
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
148
|
+
|
|
149
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
150
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
151
|
+
|
|
152
|
+
section.remove();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// ------------------------------
|
|
157
|
+
// Section discovery (via links)
|
|
158
|
+
// ------------------------------
|
|
159
|
+
describe('section discovery', () => {
|
|
160
|
+
it('observes sections referenced by links', () => {
|
|
161
|
+
const section1 = document.createElement('section');
|
|
162
|
+
section1.id = 'section-1';
|
|
163
|
+
const section2 = document.createElement('section');
|
|
164
|
+
section2.id = 'section-2';
|
|
165
|
+
document.body.appendChild(section1);
|
|
166
|
+
document.body.appendChild(section2);
|
|
167
|
+
|
|
168
|
+
container.innerHTML = `
|
|
169
|
+
<mds-scroll-spy>
|
|
170
|
+
<a href="#section-1">Section 1</a>
|
|
171
|
+
<a href="#section-2">Section 2</a>
|
|
172
|
+
</mds-scroll-spy>
|
|
173
|
+
`;
|
|
174
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
175
|
+
|
|
176
|
+
// Verify observer was created and is observing sections
|
|
177
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
178
|
+
|
|
179
|
+
section1.remove();
|
|
180
|
+
section2.remove();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('warns when section element does not exist', () => {
|
|
184
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
185
|
+
|
|
186
|
+
createScrollSpyWithLinks([{ href: '#nonexistent', text: 'Link' }]);
|
|
187
|
+
|
|
188
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("doesn't exist on this page"));
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('ignores external links', () => {
|
|
192
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
193
|
+
|
|
194
|
+
container.innerHTML = `
|
|
195
|
+
<mds-scroll-spy>
|
|
196
|
+
<a href="https://external.com/page#section">External</a>
|
|
197
|
+
</mds-scroll-spy>
|
|
198
|
+
`;
|
|
199
|
+
|
|
200
|
+
// No warning should be logged for external links
|
|
201
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('ignores links without hash', () => {
|
|
205
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
206
|
+
|
|
207
|
+
container.innerHTML = `
|
|
208
|
+
<mds-scroll-spy>
|
|
209
|
+
<a href="/page">No hash</a>
|
|
210
|
+
</mds-scroll-spy>
|
|
211
|
+
`;
|
|
212
|
+
|
|
213
|
+
// No warning should be logged for links without hash
|
|
214
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('handles IDs with special characters', () => {
|
|
218
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
219
|
+
const section = document.createElement('section');
|
|
220
|
+
section.id = 'section:with.special-chars';
|
|
221
|
+
document.body.appendChild(section);
|
|
222
|
+
|
|
223
|
+
createScrollSpyWithLinks([{ href: '#section:with.special-chars', text: 'Special' }]);
|
|
224
|
+
|
|
225
|
+
// No warning means section was found
|
|
226
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
227
|
+
|
|
228
|
+
section.remove();
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// ------------------------------
|
|
233
|
+
// Observer options (via attributes)
|
|
234
|
+
// ------------------------------
|
|
235
|
+
describe('observer options', () => {
|
|
236
|
+
it('uses default threshold when no attribute provided', () => {
|
|
237
|
+
const section = document.createElement('section');
|
|
238
|
+
section.id = 'test-section';
|
|
239
|
+
document.body.appendChild(section);
|
|
240
|
+
|
|
241
|
+
container.innerHTML = `
|
|
242
|
+
<mds-scroll-spy>
|
|
243
|
+
<a href="#test-section">Link</a>
|
|
244
|
+
</mds-scroll-spy>
|
|
245
|
+
`;
|
|
246
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
247
|
+
|
|
248
|
+
// Check that observer was created (via mock)
|
|
249
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
250
|
+
|
|
251
|
+
section.remove();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('uses observer-root attribute', () => {
|
|
255
|
+
const rootEl = document.createElement('div');
|
|
256
|
+
rootEl.id = 'scroll-root';
|
|
257
|
+
document.body.appendChild(rootEl);
|
|
258
|
+
|
|
259
|
+
const section = document.createElement('section');
|
|
260
|
+
section.id = 'test-section';
|
|
261
|
+
rootEl.appendChild(section);
|
|
262
|
+
|
|
263
|
+
container.innerHTML = `
|
|
264
|
+
<mds-scroll-spy observer-root="#scroll-root">
|
|
265
|
+
<a href="#test-section">Link</a>
|
|
266
|
+
</mds-scroll-spy>
|
|
267
|
+
`;
|
|
268
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
269
|
+
|
|
270
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
271
|
+
|
|
272
|
+
rootEl.remove();
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('uses observer-threshold attribute', () => {
|
|
276
|
+
const section = document.createElement('section');
|
|
277
|
+
section.id = 'test-section';
|
|
278
|
+
document.body.appendChild(section);
|
|
279
|
+
|
|
280
|
+
container.innerHTML = `
|
|
281
|
+
<mds-scroll-spy observer-threshold="0.75">
|
|
282
|
+
<a href="#test-section">Link</a>
|
|
283
|
+
</mds-scroll-spy>
|
|
284
|
+
`;
|
|
285
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
286
|
+
|
|
287
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
288
|
+
|
|
289
|
+
section.remove();
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// ------------------------------
|
|
294
|
+
// connectedCallback
|
|
295
|
+
// ------------------------------
|
|
296
|
+
describe('connectedCallback', () => {
|
|
297
|
+
it('finds links within the element', () => {
|
|
298
|
+
container.innerHTML = `
|
|
299
|
+
<mds-scroll-spy>
|
|
300
|
+
<a href="#a">A</a>
|
|
301
|
+
<a href="#b">B</a>
|
|
302
|
+
</mds-scroll-spy>
|
|
303
|
+
`;
|
|
304
|
+
|
|
305
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
306
|
+
const links = scrollSpy.querySelectorAll('a');
|
|
307
|
+
expect(links).toHaveLength(2);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('uses aria-current-value attribute when setting active state', () => {
|
|
311
|
+
const section = document.createElement('section');
|
|
312
|
+
section.id = 'test';
|
|
313
|
+
document.body.appendChild(section);
|
|
314
|
+
|
|
315
|
+
container.innerHTML = `
|
|
316
|
+
<mds-scroll-spy aria-current-value="location">
|
|
317
|
+
<a href="#test">Test</a>
|
|
318
|
+
</mds-scroll-spy>
|
|
319
|
+
`;
|
|
320
|
+
|
|
321
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
322
|
+
expect(scrollSpy.getAttribute('aria-current-value')).toBe('location');
|
|
323
|
+
|
|
324
|
+
section.remove();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('does not create observer when no sections found', () => {
|
|
328
|
+
vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
329
|
+
|
|
330
|
+
container.innerHTML = `
|
|
331
|
+
<mds-scroll-spy>
|
|
332
|
+
<a href="#nonexistent">Link</a>
|
|
333
|
+
</mds-scroll-spy>
|
|
334
|
+
`;
|
|
335
|
+
|
|
336
|
+
// Element should still be created without errors
|
|
337
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
338
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('creates observer when sections exist', () => {
|
|
342
|
+
const section = document.createElement('section');
|
|
343
|
+
section.id = 'test-section';
|
|
344
|
+
document.body.appendChild(section);
|
|
345
|
+
|
|
346
|
+
const scrollSpy = createScrollSpyWithLinks([{ href: '#test-section', text: 'Link' }]);
|
|
347
|
+
|
|
348
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
349
|
+
|
|
350
|
+
section.remove();
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// ------------------------------
|
|
355
|
+
// disconnectedCallback
|
|
356
|
+
// ------------------------------
|
|
357
|
+
describe('disconnectedCallback', () => {
|
|
358
|
+
it('cleans up without errors when removed', () => {
|
|
359
|
+
const section = document.createElement('section');
|
|
360
|
+
section.id = 'cleanup-test';
|
|
361
|
+
document.body.appendChild(section);
|
|
362
|
+
|
|
363
|
+
const scrollSpy = createScrollSpyWithLinks([{ href: '#cleanup-test', text: 'Link' }]);
|
|
364
|
+
|
|
365
|
+
// Should not throw when removing
|
|
366
|
+
expect(() => scrollSpy.remove()).not.toThrow();
|
|
367
|
+
|
|
368
|
+
section.remove();
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('handles disconnect when no observer was created', () => {
|
|
372
|
+
container.innerHTML = `<mds-scroll-spy></mds-scroll-spy>`;
|
|
373
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
374
|
+
|
|
375
|
+
// Should not throw
|
|
376
|
+
expect(() => scrollSpy.remove()).not.toThrow();
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// ------------------------------
|
|
381
|
+
// setObserver / aria-current behavior
|
|
382
|
+
// ------------------------------
|
|
383
|
+
describe('setObserver', () => {
|
|
384
|
+
it('sets up intersection observer for sections', () => {
|
|
385
|
+
const section = document.createElement('section');
|
|
386
|
+
section.id = 'observer-test';
|
|
387
|
+
document.body.appendChild(section);
|
|
388
|
+
|
|
389
|
+
container.innerHTML = `
|
|
390
|
+
<mds-scroll-spy observer-threshold="0.5">
|
|
391
|
+
<a href="#observer-test">Link</a>
|
|
392
|
+
</mds-scroll-spy>
|
|
393
|
+
`;
|
|
394
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
395
|
+
|
|
396
|
+
expect(scrollSpy).toBeInstanceOf(MdsScrollSpy);
|
|
397
|
+
|
|
398
|
+
section.remove();
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('sets aria-current on most visible section link', () => {
|
|
402
|
+
const section1 = document.createElement('section');
|
|
403
|
+
section1.id = 'section-1';
|
|
404
|
+
const section2 = document.createElement('section');
|
|
405
|
+
section2.id = 'section-2';
|
|
406
|
+
document.body.appendChild(section1);
|
|
407
|
+
document.body.appendChild(section2);
|
|
408
|
+
|
|
409
|
+
container.innerHTML = `
|
|
410
|
+
<mds-scroll-spy>
|
|
411
|
+
<a href="#section-1">Section 1</a>
|
|
412
|
+
<a href="#section-2">Section 2</a>
|
|
413
|
+
</mds-scroll-spy>
|
|
414
|
+
`;
|
|
415
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
416
|
+
const link1 = scrollSpy.querySelector('a[href="#section-1"]');
|
|
417
|
+
const link2 = scrollSpy.querySelector('a[href="#section-2"]');
|
|
418
|
+
|
|
419
|
+
// Verify initial state (no aria-current)
|
|
420
|
+
expect(link1.getAttribute('aria-current')).toBeNull();
|
|
421
|
+
expect(link2.getAttribute('aria-current')).toBeNull();
|
|
422
|
+
|
|
423
|
+
// Simulate intersection: section-1 is more visible
|
|
424
|
+
lastObserverInstance.triggerIntersect([
|
|
425
|
+
{ target: section1, intersectionRatio: 0.8 },
|
|
426
|
+
{ target: section2, intersectionRatio: 0.2 },
|
|
427
|
+
]);
|
|
428
|
+
|
|
429
|
+
expect(link1.getAttribute('aria-current')).toBe('step');
|
|
430
|
+
expect(link2.getAttribute('aria-current')).toBeNull();
|
|
431
|
+
|
|
432
|
+
// Simulate scrolling: section-2 becomes more visible
|
|
433
|
+
lastObserverInstance.triggerIntersect([
|
|
434
|
+
{ target: section1, intersectionRatio: 0.1 },
|
|
435
|
+
{ target: section2, intersectionRatio: 0.9 },
|
|
436
|
+
]);
|
|
437
|
+
|
|
438
|
+
expect(link1.getAttribute('aria-current')).toBeNull();
|
|
439
|
+
expect(link2.getAttribute('aria-current')).toBe('step');
|
|
440
|
+
|
|
441
|
+
section1.remove();
|
|
442
|
+
section2.remove();
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('uses custom aria-current-value attribute', () => {
|
|
446
|
+
const section = document.createElement('section');
|
|
447
|
+
section.id = 'custom-aria';
|
|
448
|
+
document.body.appendChild(section);
|
|
449
|
+
|
|
450
|
+
container.innerHTML = `
|
|
451
|
+
<mds-scroll-spy aria-current-value="page">
|
|
452
|
+
<a href="#custom-aria">Link</a>
|
|
453
|
+
</mds-scroll-spy>
|
|
454
|
+
`;
|
|
455
|
+
const scrollSpy = container.querySelector('mds-scroll-spy');
|
|
456
|
+
|
|
457
|
+
expect(scrollSpy.getAttribute('aria-current-value')).toBe('page');
|
|
458
|
+
|
|
459
|
+
section.remove();
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
});
|
package/src/js/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { MdsCardLink } from '../components/card/card-link';
|
|
|
15
15
|
import { MdsConditionalSection } from '../components/conditional-section/conditional-section';
|
|
16
16
|
import { MdsImageCropper } from '../components/image-cropper/image-cropper';
|
|
17
17
|
import { MdsCategoryPicker } from '../components/inputs/category-picker/category-picker';
|
|
18
|
+
import { MdsScrollSpy } from '../components/scroll-spy/scroll-spy';
|
|
18
19
|
|
|
19
20
|
if (!window.customElements.get('mds-dropdown-nav')) {
|
|
20
21
|
window.customElements.define('mds-dropdown-nav', MdsDropdownNav);
|
|
@@ -37,6 +38,9 @@ if (!window.customElements.get('mds-file-upload')) {
|
|
|
37
38
|
if (!window.customElements.get('mds-category-picker')) {
|
|
38
39
|
window.customElements.define('mds-category-picker', MdsCategoryPicker);
|
|
39
40
|
}
|
|
41
|
+
if (!window.customElements.get('mds-scroll-spy')) {
|
|
42
|
+
window.customElements.define('mds-scroll-spy', MdsScrollSpy);
|
|
43
|
+
}
|
|
40
44
|
|
|
41
45
|
const initAll = () => {
|
|
42
46
|
tabs.init();
|
|
@@ -126,5 +126,26 @@
|
|
|
126
126
|
color: $constant-color-neutral-light;
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
|
|
130
|
+
&.mds-step-list__item--has-subnav:before {
|
|
131
|
+
margin-bottom: 0;
|
|
132
|
+
}
|
|
129
133
|
}
|
|
130
134
|
}
|
|
135
|
+
|
|
136
|
+
.mds-step-list-subnav {
|
|
137
|
+
list-style-type: none;
|
|
138
|
+
border-left: 1px solid $constant-color-neutral-light;
|
|
139
|
+
margin-left: 1.25em;
|
|
140
|
+
}
|
|
141
|
+
.mds-step-list-subnav__link {
|
|
142
|
+
display: block;
|
|
143
|
+
padding: $constant-size-baseline * 5;
|
|
144
|
+
font-weight: normal;
|
|
145
|
+
border-left: 4px solid transparent;
|
|
146
|
+
|
|
147
|
+
&[aria-current] {
|
|
148
|
+
color: var(--mds-color-text-base);
|
|
149
|
+
border-left-color: $link-color;
|
|
150
|
+
}
|
|
151
|
+
}
|
package/src/typography/lists.njk
CHANGED
|
@@ -10,14 +10,12 @@
|
|
|
10
10
|
<a href="#three">three</a>
|
|
11
11
|
</li>
|
|
12
12
|
</ul>
|
|
13
|
-
|
|
14
13
|
<h2>Ordered list</h2>
|
|
15
14
|
<ol class="mds-list mds-list--number">
|
|
16
15
|
<li class="mds-list__item">one</li>
|
|
17
16
|
<li class="mds-list__item">two</li>
|
|
18
17
|
<li class="mds-list__item">three</li>
|
|
19
18
|
</ol>
|
|
20
|
-
|
|
21
19
|
<h2>Definition List with border</h2>
|
|
22
20
|
<dl class="mds-list mds-list--definition mds-list--border">
|
|
23
21
|
<dt class="mds-list__key">Recruiter</dt>
|
|
@@ -27,21 +25,18 @@
|
|
|
27
25
|
<dt class="mds-list__key">Expires</dt>
|
|
28
26
|
<dd class="mds-list__value">12pm, 25th November 1983</dd>
|
|
29
27
|
</dl>
|
|
30
|
-
|
|
31
28
|
<h2>Inline list</h2>
|
|
32
29
|
<ul class="mds-list mds-list--inline">
|
|
33
30
|
<li class="mds-list__item">One</li>
|
|
34
31
|
<li class="mds-list__item">Two</li>
|
|
35
32
|
<li class="mds-list__item">Three</li>
|
|
36
33
|
</ul>
|
|
37
|
-
|
|
38
34
|
<h2>Bordered inline list (separated)</h2>
|
|
39
35
|
<ul class="mds-list mds-list--inline mds-list--pipe">
|
|
40
36
|
<li class="mds-list__item">Four</li>
|
|
41
37
|
<li class="mds-list__item">Five</li>
|
|
42
38
|
<li class="mds-list__item">Six</li>
|
|
43
39
|
</ul>
|
|
44
|
-
|
|
45
40
|
<h2>Multilevel list</h2>
|
|
46
41
|
<ol class="mds-list mds-list--number mds-list--multilevel">
|
|
47
42
|
<li class="mds-list__item">
|
|
@@ -54,7 +49,13 @@
|
|
|
54
49
|
<ul class="mds-list mds-list--bullet">
|
|
55
50
|
<li class="mds-list__item">first</li>
|
|
56
51
|
<li class="mds-list__item">second</li>
|
|
57
|
-
<li class="mds-list__item">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eget metus nisi. Morbi
|
|
52
|
+
<li class="mds-list__item">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eget metus nisi. Morbi
|
|
53
|
+
sollicitudin, erat sed elementum suscipit, dolor nibh sagittis dui, at ultrices massa erat non ipsum. Nam euismod
|
|
54
|
+
dapibus augue, sit amet efficitur urna malesuada vitae. Sed tincidunt felis a turpis vulputate, vitae sodales nibh
|
|
55
|
+
imperdiet. Donec ullamcorper risus quis convallis mollis. Nullam nec magna lectus. Etiam in lobortis purus. Duis
|
|
56
|
+
vestibulum, nibh sit amet commodo porttitor, nunc metus eleifend magna, at venenatis tortor justo at eros. Vivamus eu
|
|
57
|
+
tincidunt mi, vel fermentum tortor. Aenean tempus, magna a scelerisque consequat, libero tortor porttitor neque, nec
|
|
58
|
+
aliquam lorem purus sodales diam. Pellentesque a orci vitae orci fringilla sollicitudin quis vitae leo.
|
|
58
59
|
<ul class="mds-list mds-list--bullet">
|
|
59
60
|
<li class="mds-list__item">once</li>
|
|
60
61
|
<li class="mds-list__item">twice</li>
|
|
@@ -69,7 +70,6 @@
|
|
|
69
70
|
<a href="#two">five</a>
|
|
70
71
|
</li>
|
|
71
72
|
</ol>
|
|
72
|
-
|
|
73
73
|
<h2>List with no indent</h2>
|
|
74
74
|
<ul class="mds-list mds-list--bullet mds-list--noindent">
|
|
75
75
|
<li class="mds-list__item">
|
|
@@ -82,17 +82,30 @@
|
|
|
82
82
|
<a href="#three">three</a>
|
|
83
83
|
</li>
|
|
84
84
|
</ul>
|
|
85
|
-
|
|
86
85
|
<h2>Step List</h2>
|
|
87
86
|
<ol class="mds-step-list">
|
|
88
87
|
<li class="mds-step-list__item">Step 1</li>
|
|
89
88
|
<li class="mds-step-list__item">Step 2</li>
|
|
90
89
|
<li class="mds-step-list__item">Step 3</li>
|
|
91
90
|
</ol>
|
|
92
|
-
|
|
93
91
|
<h2>Step List with page progress</h2>
|
|
94
92
|
<ol class="mds-step-list">
|
|
95
|
-
<li class="mds-step-list__item"
|
|
96
|
-
|
|
93
|
+
<li class="mds-step-list__item">
|
|
94
|
+
<a href="https://www.google.com">Past page with link</a>
|
|
95
|
+
</li>
|
|
96
|
+
<li class="mds-step-list__item mds-step-list__item--current mds-step-list__item--has-subnav" aria-current="page">Current
|
|
97
|
+
page
|
|
98
|
+
<ul class="mds-step-list-subnav">
|
|
99
|
+
<li class="mds-step-list-subnav__item">
|
|
100
|
+
<a class="mds-step-list-subnav__link" href="#section-1">Section 1</a>
|
|
101
|
+
</li>
|
|
102
|
+
<li class="mds-step-list-subnav__item">
|
|
103
|
+
<a class="mds-step-list-subnav__link" href="#section-2" aria-current="location">Section 2</a>
|
|
104
|
+
</li>
|
|
105
|
+
<li class="mds-step-list-subnav__item">
|
|
106
|
+
<a class="mds-step-list-subnav__link" href="#section-3">Section 3</a>
|
|
107
|
+
</li>
|
|
108
|
+
</ul>
|
|
109
|
+
</li>
|
|
97
110
|
<li class="mds-step-list__item mds-step-list__item--future">Ghost of pages future</li>
|
|
98
111
|
</ol>
|