@natachah/vanilla-frontend 0.2.2 → 0.2.3
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/docs/pages/components/drawer.html +23 -1
- package/docs/pages/javascript/trap.html +2 -2
- package/docs/src/js/doc-layout.js +1 -1
- package/js/_drawer.js +9 -4
- package/js/tests/drawer.test.js +3 -0
- package/js/tests/trap.test.js +10 -9
- package/js/utilities/_trap.js +24 -21
- package/natachah-vanilla-frontend-0.2.3.tgz +0 -0
- package/package.json +1 -1
- package/natachah-vanilla-frontend-0.2.2.tgz +0 -0
|
@@ -102,13 +102,35 @@
|
|
|
102
102
|
<p><code>false</code> by default, or a <code>string</code></p>
|
|
103
103
|
</td>
|
|
104
104
|
</tr>
|
|
105
|
+
<tr>
|
|
106
|
+
<td data-label="Name">
|
|
107
|
+
<p>trap.exclusions</p>
|
|
108
|
+
</td>
|
|
109
|
+
<td data-label="Description">
|
|
110
|
+
<p>The elements that should not be inert for Accessibility</p>
|
|
111
|
+
</td>
|
|
112
|
+
<td data-label="Value">
|
|
113
|
+
<p><code>['#backdrop']</code> by default, or an <code>Array</code></p>
|
|
114
|
+
</td>
|
|
115
|
+
</tr>
|
|
116
|
+
<tr>
|
|
117
|
+
<td data-label="Name">
|
|
118
|
+
<p>trap.inclusions</p>
|
|
119
|
+
</td>
|
|
120
|
+
<td data-label="Description">
|
|
121
|
+
<p>The elements to include into the <kbd>TAB</kbd> trap focus</p>
|
|
122
|
+
</td>
|
|
123
|
+
<td data-label="Value">
|
|
124
|
+
<p><code>null</code> by default, or an <code>Array</code></p>
|
|
125
|
+
</td>
|
|
126
|
+
</tr>
|
|
105
127
|
</tbody>
|
|
106
128
|
</table>
|
|
107
129
|
|
|
108
130
|
<doc-code data-type="js">
|
|
109
131
|
import Drawer from '@natachah/vanilla-frontend/js/utilities/_drawer.js'
|
|
110
132
|
const drawer = document.getElementById('drawer')
|
|
111
|
-
if (drawer) new Drawer(drawer, { breakpoint : 960, cookie: '_drawer-cookie' })
|
|
133
|
+
if (drawer) new Drawer(drawer, { breakpoint : 960, cookie: '_drawer-cookie': trap: {exclusions: ['#backdrop', '#toggleMenu'], inclusions: ['#toggleMenu']} })
|
|
112
134
|
</doc-code>
|
|
113
135
|
|
|
114
136
|
<blockquote>
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
|
|
16
16
|
<h2>Javascript</h2>
|
|
17
17
|
<p>This component is only in javascript, to use it you must import the javascript file and create a new Trap object.</p>
|
|
18
|
-
<p>You can pass the element and an array of
|
|
18
|
+
<p>You can pass the element, an array of exclusions (= element that should not be inert) and an array of inclusions (= element that should be included into the trap) when initiate the component.</p>
|
|
19
19
|
|
|
20
20
|
<doc-code data-type="js">
|
|
21
21
|
import Trap from "@natachah/vanilla-frontend/js/utilities/_trap"
|
|
22
|
-
new Trap(document.getElementById('drawer'), ['#backdrop'])
|
|
22
|
+
new Trap(document.getElementById('drawer'), ['#backdrop','#toggleMenu'], ['#toggleMenu'])
|
|
23
23
|
</doc-code>
|
|
24
24
|
|
|
25
25
|
<h3>Methods</h3>
|
|
@@ -18,7 +18,7 @@ class DocLayout extends HTMLElement {
|
|
|
18
18
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pin-angle" viewBox="0 0 16 16">
|
|
19
19
|
<path d="M9.828.722a.5.5 0 0 1 .354.146l4.95 4.95a.5.5 0 0 1 0 .707c-.48.48-1.072.588-1.503.588-.177 0-.335-.018-.46-.039l-3.134 3.134a6 6 0 0 1 .16 1.013c.046.702-.032 1.687-.72 2.375a.5.5 0 0 1-.707 0l-2.829-2.828-3.182 3.182c-.195.195-1.219.902-1.414.707s.512-1.22.707-1.414l3.182-3.182-2.828-2.829a.5.5 0 0 1 0-.707c.688-.688 1.673-.767 2.375-.72a6 6 0 0 1 1.013.16l3.134-3.133a3 3 0 0 1-.04-.461c0-.43.108-1.022.589-1.503a.5.5 0 0 1 .353-.146m.122 2.112v-.002zm0-.002v.002a.5.5 0 0 1-.122.51L6.293 6.878a.5.5 0 0 1-.511.12H5.78l-.014-.004a5 5 0 0 0-.288-.076 5 5 0 0 0-.765-.116c-.422-.028-.836.008-1.175.15l5.51 5.509c.141-.34.177-.753.149-1.175a5 5 0 0 0-.192-1.054l-.004-.013v-.001a.5.5 0 0 1 .12-.512l3.536-3.535a.5.5 0 0 1 .532-.115l.096.022c.087.017.208.034.344.034q.172.002.343-.04L9.927 2.028q-.042.172-.04.343a1.8 1.8 0 0 0 .062.46z"/>
|
|
20
20
|
</svg>
|
|
21
|
-
0.2.
|
|
21
|
+
0.2.3
|
|
22
22
|
</span>
|
|
23
23
|
</li>
|
|
24
24
|
<li>
|
package/js/_drawer.js
CHANGED
|
@@ -20,7 +20,10 @@ export default class Drawer extends BaseComponent {
|
|
|
20
20
|
static OPTIONS = {
|
|
21
21
|
breakpoint: 960,
|
|
22
22
|
cookie: false,
|
|
23
|
-
|
|
23
|
+
trap: {
|
|
24
|
+
exclusions: null,
|
|
25
|
+
inclusions: null
|
|
26
|
+
}
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
/**
|
|
@@ -34,6 +37,10 @@ export default class Drawer extends BaseComponent {
|
|
|
34
37
|
|
|
35
38
|
if (options.breakpoint && typeof options.breakpoint !== 'number') throw new Error(ErrorMessage.typeOf('options.breakpoint', 'number'))
|
|
36
39
|
if (options.cookie && typeof options.cookie !== 'string') throw new Error(ErrorMessage.typeOf('options.cookie', 'string'))
|
|
40
|
+
if (options.trap) {
|
|
41
|
+
if (options.trap.exclusions && typeof options.trap.exclusions !== 'object') throw new Error(ErrorMessage.typeOf('options.trap.exclusions', 'object'))
|
|
42
|
+
if (options.trap.inclusions && typeof options.trap.inclusions !== 'object') throw new Error(ErrorMessage.typeOf('options.trap.inclusions', 'object'))
|
|
43
|
+
}
|
|
37
44
|
|
|
38
45
|
// Run the SUPER constructor from BaseComponent
|
|
39
46
|
super(el, options, 'drawer')
|
|
@@ -49,9 +56,7 @@ export default class Drawer extends BaseComponent {
|
|
|
49
56
|
|
|
50
57
|
this._cookie = this._options.cookie ? new Cookie(this._options.cookie) : null
|
|
51
58
|
|
|
52
|
-
this._trap = new Trap(this._element, this._options.exclusions)
|
|
53
|
-
|
|
54
|
-
console.log(this._buttons)
|
|
59
|
+
this._trap = new Trap(this._element, this._options.trap.exclusions ?? ['#backdrop'], this._options.trap.inclusions ?? [])
|
|
55
60
|
|
|
56
61
|
// Init the event listener
|
|
57
62
|
this.#init()
|
package/js/tests/drawer.test.js
CHANGED
|
@@ -38,6 +38,9 @@ describe('Structure of the class', () => {
|
|
|
38
38
|
|
|
39
39
|
test('Constructor: Passing the correct parameters', () => {
|
|
40
40
|
expect(() => new Drawer(document.getElementById('drawer'), { breakpoint: 'make-error' })).toThrowError(ErrorMessage.typeOf('options.breakpoint', 'number'))
|
|
41
|
+
expect(() => new Drawer(document.getElementById('drawer'), { cookie: [] })).toThrowError(ErrorMessage.typeOf('options.cookie', 'string'))
|
|
42
|
+
expect(() => new Drawer(document.getElementById('drawer'), { trap: { exclusions: 'error' } })).toThrowError(ErrorMessage.typeOf('options.trap.exclusions', 'object'))
|
|
43
|
+
expect(() => new Drawer(document.getElementById('drawer'), { trap: { inclusions: 'error' } })).toThrowError(ErrorMessage.typeOf('options.trap.inclusions', 'object'))
|
|
41
44
|
})
|
|
42
45
|
|
|
43
46
|
test('Constructor: Return the correct properties', () => {
|
package/js/tests/trap.test.js
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* - Constructor: Passing the correct parameters
|
|
8
8
|
* - FocusOnFirst(): Passing the correct parameters
|
|
9
9
|
* - Activate(): Save the new cookie value
|
|
10
|
-
* -
|
|
10
|
+
* - Deactivate(): Passing the correct parameters
|
|
11
11
|
* - HandleKeydown(): Verify the existance of a key in the cookie value
|
|
12
|
-
* -
|
|
12
|
+
* - GetFocusableElementss(): Passing the correct parameters
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { describe, test, expect, beforeAll, vi } from "vitest"
|
|
@@ -54,7 +54,7 @@ describe('Structure of the class', () => {
|
|
|
54
54
|
test('Has all the public method', () => {
|
|
55
55
|
expect(fakeTrap.focusOnFirst).toBeTypeOf('function')
|
|
56
56
|
expect(fakeTrap.activate).toBeTypeOf('function')
|
|
57
|
-
expect(fakeTrap.
|
|
57
|
+
expect(fakeTrap.deactivate).toBeTypeOf('function')
|
|
58
58
|
expect(fakeTrap.handleKeydown).toBeTypeOf('function')
|
|
59
59
|
expect(fakeTrap.getFocusableElements).toBeTypeOf('function')
|
|
60
60
|
expect(fakeTrap.getSiblingsOutside).toBeTypeOf('function')
|
|
@@ -63,6 +63,7 @@ describe('Structure of the class', () => {
|
|
|
63
63
|
test('Constructor: Passing the correct parameters', () => {
|
|
64
64
|
expect(() => new Trap()).toThrowError(ErrorMessage.instanceOf('el', 'HTMLElement'))
|
|
65
65
|
expect(() => new Trap(document.getElementById('trap'), 'error')).toThrowError(ErrorMessage.typeOf('exclusions', 'object'))
|
|
66
|
+
expect(() => new Trap(document.getElementById('trap'), null, 'error')).toThrowError(ErrorMessage.typeOf('inclusions', 'object'))
|
|
66
67
|
})
|
|
67
68
|
|
|
68
69
|
})
|
|
@@ -87,7 +88,7 @@ describe('FocusOnFirst()', () => {
|
|
|
87
88
|
describe('Toggle the trap', () => {
|
|
88
89
|
|
|
89
90
|
test('Activate the trap', () => {
|
|
90
|
-
const eventSpy = vi.spyOn(
|
|
91
|
+
const eventSpy = vi.spyOn(document, 'addEventListener')
|
|
91
92
|
|
|
92
93
|
fakeTrap.activate()
|
|
93
94
|
|
|
@@ -95,8 +96,8 @@ describe('Toggle the trap', () => {
|
|
|
95
96
|
expect(fakeTrap._items).not.toContain(fakeDisabledLink)
|
|
96
97
|
expect(fakeTrap._first).toStrictEqual(document.getElementById('button'))
|
|
97
98
|
expect(fakeTrap._last).toStrictEqual(document.getElementById('input'))
|
|
98
|
-
expect(fakeTrap.
|
|
99
|
-
expect(fakeTrap.
|
|
99
|
+
expect(fakeTrap._inertElements).toStrictEqual(fakeTrap.getSiblingsOutside())
|
|
100
|
+
expect(fakeTrap._inertElements).not.toContain(document.getElementById('backdrop'))
|
|
100
101
|
expect(document.activeElement).toBe(fakeTrap._first)
|
|
101
102
|
expect(eventSpy).toHaveBeenCalledWith('keydown', fakeTrap.handleKeydown)
|
|
102
103
|
expect(document.getElementById('backdrop').hasAttribute('inert')).toBeFalsy()
|
|
@@ -106,9 +107,9 @@ describe('Toggle the trap', () => {
|
|
|
106
107
|
|
|
107
108
|
})
|
|
108
109
|
|
|
109
|
-
test('
|
|
110
|
-
const eventSpy = vi.spyOn(
|
|
111
|
-
fakeTrap.
|
|
110
|
+
test('Deactivate the trap', () => {
|
|
111
|
+
const eventSpy = vi.spyOn(document, 'removeEventListener')
|
|
112
|
+
fakeTrap.deactivate()
|
|
112
113
|
expect(eventSpy).toHaveBeenCalledWith('keydown', fakeTrap.handleKeydown)
|
|
113
114
|
expect(document.getElementById('backdrop').hasAttribute('inert')).toBeFalsy()
|
|
114
115
|
expect(document.getElementById('backdrop').hasAttribute('aria-hidden')).toBeFalsy()
|
package/js/utilities/_trap.js
CHANGED
|
@@ -17,21 +17,22 @@ export default class Trap {
|
|
|
17
17
|
*
|
|
18
18
|
* @constructor
|
|
19
19
|
*/
|
|
20
|
-
constructor(el, exclusions = null) {
|
|
20
|
+
constructor(el, exclusions = null, inclusions = null) {
|
|
21
21
|
|
|
22
22
|
// Check for errors
|
|
23
23
|
if (!(el instanceof HTMLElement)) throw new Error(ErrorMessage.instanceOf('el', 'HTMLElement'))
|
|
24
24
|
if (exclusions && typeof exclusions !== 'object') throw new Error(ErrorMessage.typeOf('exclusions', 'object'))
|
|
25
|
+
if (inclusions && typeof inclusions !== 'object') throw new Error(ErrorMessage.typeOf('inclusions', 'object'))
|
|
25
26
|
|
|
26
27
|
// Define elements
|
|
27
28
|
this._element = el
|
|
28
29
|
this._items = []
|
|
29
30
|
this._first = null
|
|
30
31
|
this._last = null
|
|
31
|
-
this.
|
|
32
|
-
this._exclusions = exclusions ??
|
|
32
|
+
this._inertElements = []
|
|
33
|
+
this._exclusions = Array.isArray(exclusions) ? exclusions : Object.values(exclusions ?? {})
|
|
34
|
+
this._inclusions = Array.isArray(inclusions) ? inclusions : Object.values(inclusions ?? {})
|
|
33
35
|
this.handleKeydown = this.handleKeydown.bind(this)
|
|
34
|
-
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
/**
|
|
@@ -47,7 +48,7 @@ export default class Trap {
|
|
|
47
48
|
this._first = this._element.querySelector('[tabindex="0"]') ?? this._items[0]
|
|
48
49
|
|
|
49
50
|
// Focus the first element by default
|
|
50
|
-
this._first.focus()
|
|
51
|
+
if (this._first) this._first.focus()
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
/**
|
|
@@ -64,28 +65,27 @@ export default class Trap {
|
|
|
64
65
|
this._last = this._items[this._items.length - 1]
|
|
65
66
|
|
|
66
67
|
// Define event listener
|
|
67
|
-
|
|
68
|
+
document.addEventListener('keydown', this.handleKeydown)
|
|
68
69
|
|
|
69
70
|
// Focus the first element by default
|
|
70
71
|
this._first.focus()
|
|
71
72
|
|
|
72
73
|
// Make the rest of the content inert for screen reader
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
74
|
+
this._inertElements = this.getSiblingsOutside()
|
|
75
|
+
this._inertElements.forEach(el => {
|
|
75
76
|
el.setAttribute('aria-hidden', 'true')
|
|
76
77
|
el.setAttribute('inert', true)
|
|
77
78
|
})
|
|
78
|
-
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
|
-
*
|
|
82
|
+
* Deactivate the trap
|
|
83
83
|
*
|
|
84
84
|
*/
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
deactivate() {
|
|
86
|
+
document.removeEventListener('keydown', this.handleKeydown)
|
|
87
87
|
|
|
88
|
-
this.
|
|
88
|
+
this._inertElements.forEach(el => {
|
|
89
89
|
el.removeAttribute('aria-hidden')
|
|
90
90
|
el.removeAttribute('inert')
|
|
91
91
|
})
|
|
@@ -97,8 +97,6 @@ export default class Trap {
|
|
|
97
97
|
* @param {event} e - Event
|
|
98
98
|
*/
|
|
99
99
|
handleKeydown(e) {
|
|
100
|
-
|
|
101
|
-
// Check for the Tab event only
|
|
102
100
|
if (e.key !== 'Tab') return
|
|
103
101
|
|
|
104
102
|
const isShift = e.shiftKey
|
|
@@ -110,17 +108,24 @@ export default class Trap {
|
|
|
110
108
|
e.preventDefault()
|
|
111
109
|
this._first.focus()
|
|
112
110
|
}
|
|
113
|
-
|
|
114
111
|
}
|
|
115
112
|
|
|
116
113
|
/**
|
|
117
114
|
* Get all focusable elements
|
|
118
115
|
*
|
|
116
|
+
* @param {HTMLElement} element
|
|
117
|
+
* @returns {Array}
|
|
119
118
|
*/
|
|
120
119
|
getFocusableElements() {
|
|
121
|
-
|
|
122
|
-
this._element.querySelectorAll('a[href], area[href], input:not([disabled]):not([type="hidden"]), select:not([disabled]),textarea:not([disabled]), button:not([disabled]), iframe, object, embed,[contenteditable], [tabindex]:not([tabindex="-1"])')
|
|
120
|
+
const focusables = Array.from(
|
|
121
|
+
this._element.querySelectorAll('a[href], area[href], input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [contenteditable], [tabindex]:not([tabindex="-1"])')
|
|
123
122
|
).filter(el => el.offsetWidth > 0 || el.offsetHeight > 0 || el === document.activeElement)
|
|
123
|
+
|
|
124
|
+
const inclusions = Array.isArray(this._inclusions)
|
|
125
|
+
? this._inclusions.flatMap(selector => Array.from(document.querySelectorAll(selector)))
|
|
126
|
+
: []
|
|
127
|
+
|
|
128
|
+
return [...focusables, ...inclusions]
|
|
124
129
|
}
|
|
125
130
|
|
|
126
131
|
/**
|
|
@@ -149,7 +154,5 @@ export default class Trap {
|
|
|
149
154
|
}
|
|
150
155
|
|
|
151
156
|
return Array.from(hiddenEls)
|
|
152
|
-
|
|
153
157
|
}
|
|
154
|
-
|
|
155
|
-
}
|
|
158
|
+
}
|
|
Binary file
|
package/package.json
CHANGED
|
Binary file
|