@natachah/vanilla-frontend 0.0.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.
- package/.gitlab-ci.yml +40 -0
- package/LICENSE.md +7 -0
- package/README.md +11 -0
- package/docs/index.html +36 -0
- package/docs/main.js +32 -0
- package/docs/pages/components/badge.html +154 -0
- package/docs/pages/components/button.html +186 -0
- package/docs/pages/components/card.html +184 -0
- package/docs/pages/components/dialog.html +334 -0
- package/docs/pages/components/disclosure.html +310 -0
- package/docs/pages/components/dropdown.html +255 -0
- package/docs/pages/components/form.html +331 -0
- package/docs/pages/components/list.html +140 -0
- package/docs/pages/components/loading.html +58 -0
- package/docs/pages/components/media.html +130 -0
- package/docs/pages/components/nav.html +119 -0
- package/docs/pages/components/progress.html +47 -0
- package/docs/pages/components/slider.html +311 -0
- package/docs/pages/components/table.html +168 -0
- package/docs/pages/javascript/autofill.html +170 -0
- package/docs/pages/javascript/checkall.html +59 -0
- package/docs/pages/javascript/comfort.html +134 -0
- package/docs/pages/javascript/consent.html +112 -0
- package/docs/pages/javascript/cookie.html +81 -0
- package/docs/pages/javascript/form.html +199 -0
- package/docs/pages/javascript/scroll.html +209 -0
- package/docs/pages/javascript/sidebar.html +53 -0
- package/docs/pages/javascript/sortable.html +148 -0
- package/docs/pages/javascript/toggle.html +191 -0
- package/docs/pages/javascript/tree.html +221 -0
- package/docs/pages/layout/grid.html +201 -0
- package/docs/pages/layout/reset.html +53 -0
- package/docs/pages/layout/typography.html +324 -0
- package/docs/pages/quick-start/conventions.html +112 -0
- package/docs/pages/quick-start/customization.html +187 -0
- package/docs/pages/quick-start/installation.html +95 -0
- package/docs/pages/quick-start/mixins.html +228 -0
- package/docs/pages/test.html +15 -0
- package/docs/src/js/demo.js +98 -0
- package/docs/src/js/doc-code.js +102 -0
- package/docs/src/js/doc-demo.js +14 -0
- package/docs/src/js/doc-layout.js +108 -0
- package/docs/src/scss/demo.scss +77 -0
- package/docs/src/scss/layout.scss +160 -0
- package/docs/src/scss/style.scss +278 -0
- package/docs/vite.config.mjs +23 -0
- package/esbuild.mjs +25 -0
- package/js/_autofill.js +131 -0
- package/js/_check-all.js +77 -0
- package/js/_comfort.js +174 -0
- package/js/_consent.js +84 -0
- package/js/_dialog.js +164 -0
- package/js/_dropdown.js +101 -0
- package/js/_scroll.js +184 -0
- package/js/_sidebar.js +97 -0
- package/js/_slider.js +249 -0
- package/js/_sortable.js +143 -0
- package/js/_tabpanel.js +88 -0
- package/js/_toggle.js +123 -0
- package/js/_tree.js +85 -0
- package/js/tests/autofill.test.js +157 -0
- package/js/tests/base-component.test.js +108 -0
- package/js/tests/check-all.test.js +88 -0
- package/js/tests/comfort.test.js +219 -0
- package/js/tests/consent.test.js +84 -0
- package/js/tests/cookie.test.js +102 -0
- package/js/tests/dialog.test.js +189 -0
- package/js/tests/dropdown.test.js +115 -0
- package/js/tests/form-helper.test.js +155 -0
- package/js/tests/scroll.test.js +203 -0
- package/js/tests/sidebar.test.js +99 -0
- package/js/tests/slider.test.js +307 -0
- package/js/tests/sortable.test.js +124 -0
- package/js/tests/tabpanel.test.js +114 -0
- package/js/tests/toggle.test.js +190 -0
- package/js/tests/tree.test.js +165 -0
- package/js/utilities/_base-component.js +101 -0
- package/js/utilities/_cookie.js +98 -0
- package/js/utilities/_error.js +80 -0
- package/js/utilities/_form-helper.js +101 -0
- package/package.json +42 -0
- package/scss/_badge.scss +37 -0
- package/scss/_button.scss +34 -0
- package/scss/_card.scss +122 -0
- package/scss/_dialog.scss +116 -0
- package/scss/_disclosure.scss +101 -0
- package/scss/_dropdown.scss +68 -0
- package/scss/_form.scss +197 -0
- package/scss/_grid.scss +40 -0
- package/scss/_group.scss +57 -0
- package/scss/_list.scss +18 -0
- package/scss/_loading.scss +49 -0
- package/scss/_media.scss +37 -0
- package/scss/_nav.scss +72 -0
- package/scss/_progress.scss +40 -0
- package/scss/_slider.scss +35 -0
- package/scss/_table.scss +36 -0
- package/scss/utilities/_mixin.scss +322 -0
- package/scss/utilities/_reset.scss +145 -0
- package/scss/utilities/_typography.scss +107 -0
- package/scss/vanilla-frontend.scss +23 -0
- package/scss/variables/_root.scss +70 -0
- package/scss/variables/_setting.scss +63 -0
- package/vitest.config.js +7 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ------------------------------------------------------------------
|
|
3
|
+
* TEST for the _toggle.js
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* The test will take care of:
|
|
6
|
+
* ! herit from BaseComponent - Constructor: Passing the correct parameters
|
|
7
|
+
* - Constructor: Return the correct properties
|
|
8
|
+
* - #Toggle(): Button : Toggle the [aria-pressed] and [aria-expanded] attribute on the <button>
|
|
9
|
+
* - #Toggle(): Button : Toggle the [hidden] attribute on the <div>
|
|
10
|
+
* - #Toggle(): Checkbox : Toggle the [aria-expanded] attribute on the <checkbox>
|
|
11
|
+
* - #Toggle(): Checkbox : Toggle the [hidden] attribute on the <div>
|
|
12
|
+
* - #Toggle(): Radio : Toggle the [aria-expanded] attribute on the <radio>
|
|
13
|
+
* - #Toggle(): Radio : Toggle the [hidden] attribute on the <div>
|
|
14
|
+
* - #Toggle(): Select : Toggle the [aria-expanded] attribute on the <select>
|
|
15
|
+
* - #Toggle(): Select : Toggle the [hidden] attribute on the <div>
|
|
16
|
+
* - #Toggle(): Emmit the toggle:changed event
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { describe, test, expect, beforeAll, vi } from "vitest"
|
|
20
|
+
import { fireEvent } from "@testing-library/dom"
|
|
21
|
+
import Toggle from "../_toggle"
|
|
22
|
+
|
|
23
|
+
let fakeToggleButton, fakeToggleCheckbox, fakeToggleRadioA, fakeToggleRadioB, fakeToggleSelect
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Before all tests
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
beforeAll(() => {
|
|
31
|
+
|
|
32
|
+
document.body.innerHTML =
|
|
33
|
+
'<button id="fakeButton" aria-controls="fakeCollapse" aria-expanded="false" aria-pressed="false"></button>' +
|
|
34
|
+
'<div id="fakeCollapse" hidden></div>' +
|
|
35
|
+
'<input id="fakeCheckbox" type="checkbox" aria-controls="fakeCollapseCheckbox" aria-expanded="false">' +
|
|
36
|
+
'<div id="fakeCollapseCheckbox" hidden></div>' +
|
|
37
|
+
'<input id="fakeRadioA" type="radio" name="fake-radio" aria-controls="fakeCollapseRadioA" aria-expanded="false">' +
|
|
38
|
+
'<input id="fakeRadioB" type="radio" name="fake-radio" aria-controls="fakeCollapseRadioB" aria-expanded="false">' +
|
|
39
|
+
'<div id="fakeCollapseRadioA" hidden></div>' +
|
|
40
|
+
'<div id="fakeCollapseRadioB" hidden></div>' +
|
|
41
|
+
'<select id="fakeSelect" aria-controls="fakeCollapseSelectA fakeCollapseSelectB fakeCollapseSelectC" aria-expanded="false">' +
|
|
42
|
+
'<option value="null"></option>' +
|
|
43
|
+
'<option value="A"></option>' +
|
|
44
|
+
'<optgroup label="G">' +
|
|
45
|
+
'<option value="B"></option>' +
|
|
46
|
+
'</optgroup>' +
|
|
47
|
+
'</select>' +
|
|
48
|
+
'<div id="fakeCollapseSelectA" data-toggle-when="A" hidden></div>' +
|
|
49
|
+
'<div id="fakeCollapseSelectB" data-toggle-when="B" hidden></div>' +
|
|
50
|
+
'<div id="fakeCollapseSelectG" data-toggle-when="G" hidden></div>'
|
|
51
|
+
|
|
52
|
+
fakeToggleButton = new Toggle(document.getElementById('fakeButton'))
|
|
53
|
+
fakeToggleCheckbox = new Toggle(document.getElementById('fakeCheckbox'))
|
|
54
|
+
fakeToggleRadioA = new Toggle(document.getElementById('fakeRadioA'))
|
|
55
|
+
fakeToggleRadioB = new Toggle(document.getElementById('fakeRadioB'))
|
|
56
|
+
fakeToggleSelect = new Toggle(document.getElementById('fakeSelect'))
|
|
57
|
+
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('Structure of the class', () => {
|
|
61
|
+
|
|
62
|
+
test('Constructor: Return the correct properties', () => {
|
|
63
|
+
expect(fakeToggleButton).toHaveProperty('_type')
|
|
64
|
+
expect(fakeToggleButton).toHaveProperty('_toggables')
|
|
65
|
+
expect(fakeToggleButton._type).toBe('button')
|
|
66
|
+
expect(fakeToggleCheckbox._type).toBe('checkbox')
|
|
67
|
+
expect(fakeToggleRadioA._type).toBe('radio')
|
|
68
|
+
expect(fakeToggleSelect._type).toBe('select')
|
|
69
|
+
expect(fakeToggleButton._toggables).toStrictEqual([...document.querySelectorAll('#fakeCollapse')])
|
|
70
|
+
expect(fakeToggleSelect._toggables).toStrictEqual([...document.querySelectorAll('#fakeCollapseSelectA, #fakeCollapseSelectB, #fakeCollapseSelectC')])
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('#Toggle()', () => {
|
|
76
|
+
|
|
77
|
+
describe('Button', () => {
|
|
78
|
+
|
|
79
|
+
test('Toggle the [aria-pressed] and [aria-expanded] attribute on the <button>', () => {
|
|
80
|
+
expect(fakeToggleButton._element.getAttribute('aria-pressed')).toBe('false')
|
|
81
|
+
expect(fakeToggleButton._element.getAttribute('aria-expanded')).toBe('false')
|
|
82
|
+
fireEvent(fakeToggleButton._element, new MouseEvent('click'))
|
|
83
|
+
expect(fakeToggleButton._element.getAttribute('aria-pressed')).toBe('true')
|
|
84
|
+
expect(fakeToggleButton._element.getAttribute('aria-expanded')).toBe('true')
|
|
85
|
+
fireEvent(fakeToggleButton._element, new MouseEvent('click'))
|
|
86
|
+
expect(fakeToggleButton._element.getAttribute('aria-pressed')).toBe('false')
|
|
87
|
+
expect(fakeToggleButton._element.getAttribute('aria-expanded')).toBe('false')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('Toggle the [hidden] attribute on the <div>', () => {
|
|
91
|
+
const div = document.getElementById('fakeCollapse')
|
|
92
|
+
expect(div.hidden).toBeTruthy()
|
|
93
|
+
fireEvent(fakeToggleButton._element, new MouseEvent('click'))
|
|
94
|
+
expect(div.hidden).toBeFalsy()
|
|
95
|
+
fireEvent(fakeToggleButton._element, new MouseEvent('click'))
|
|
96
|
+
expect(div.hidden).toBeTruthy()
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
describe('Checkbox', () => {
|
|
102
|
+
|
|
103
|
+
test('Toggle the [aria-expanded] attribute on the <checkbox>', () => {
|
|
104
|
+
expect(fakeToggleCheckbox._element.getAttribute('aria-expanded')).toBe('false')
|
|
105
|
+
fireEvent.change(fakeToggleCheckbox._element, { target: { checked: true } })
|
|
106
|
+
expect(fakeToggleCheckbox._element.getAttribute('aria-expanded')).toBe('true')
|
|
107
|
+
fireEvent.change(fakeToggleCheckbox._element, { target: { checked: false } })
|
|
108
|
+
expect(fakeToggleCheckbox._element.getAttribute('aria-expanded')).toBe('false')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('Toggle the [hidden] attribute on the <div>', () => {
|
|
112
|
+
const div = document.getElementById('fakeCollapseCheckbox')
|
|
113
|
+
expect(div.hidden).toBeTruthy()
|
|
114
|
+
fireEvent.change(fakeToggleCheckbox._element, { target: { checked: true } })
|
|
115
|
+
expect(div.hidden).toBeFalsy()
|
|
116
|
+
fireEvent.change(fakeToggleCheckbox._element, { target: { checked: false } })
|
|
117
|
+
expect(div.hidden).toBeTruthy()
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
describe('Radio', () => {
|
|
123
|
+
|
|
124
|
+
test('Toggle the [aria-expanded] attribute on the <radio>', () => {
|
|
125
|
+
expect(fakeToggleRadioA._element.getAttribute('aria-expanded')).toBe('false')
|
|
126
|
+
expect(fakeToggleRadioB._element.getAttribute('aria-expanded')).toBe('false')
|
|
127
|
+
fireEvent.change(fakeToggleRadioA._element, { target: { checked: true } })
|
|
128
|
+
expect(fakeToggleRadioA._element.getAttribute('aria-expanded')).toBe('true')
|
|
129
|
+
expect(fakeToggleRadioB._element.getAttribute('aria-expanded')).toBe('false')
|
|
130
|
+
fireEvent.change(fakeToggleRadioB._element, { target: { checked: true } })
|
|
131
|
+
expect(fakeToggleRadioA._element.getAttribute('aria-expanded')).toBe('false')
|
|
132
|
+
expect(fakeToggleRadioB._element.getAttribute('aria-expanded')).toBe('true')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test('Toggle the [hidden] attribute on the <div>', () => {
|
|
136
|
+
const divA = document.getElementById('fakeCollapseRadioA')
|
|
137
|
+
const divB = document.getElementById('fakeCollapseRadioB')
|
|
138
|
+
expect(divA.hidden).toBeTruthy()
|
|
139
|
+
expect(divB.hidden).toBeFalsy()
|
|
140
|
+
fireEvent.change(fakeToggleRadioA._element, { target: { checked: true } })
|
|
141
|
+
expect(divA.hidden).toBeFalsy()
|
|
142
|
+
expect(divB.hidden).toBeTruthy()
|
|
143
|
+
fireEvent.change(fakeToggleRadioB._element, { target: { checked: true } })
|
|
144
|
+
expect(divA.hidden).toBeTruthy()
|
|
145
|
+
expect(divB.hidden).toBeFalsy()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
describe('Select', () => {
|
|
151
|
+
|
|
152
|
+
test('Toggle the [aria-expanded] attribute on the <select>', () => {
|
|
153
|
+
expect(fakeToggleSelect._element.getAttribute('aria-expanded')).toBe('false')
|
|
154
|
+
fireEvent.change(fakeToggleSelect._element, { target: { value: 'A' } })
|
|
155
|
+
expect(fakeToggleSelect._element.getAttribute('aria-expanded')).toBe('true')
|
|
156
|
+
fireEvent.change(fakeToggleSelect._element, { target: { value: 'null' } })
|
|
157
|
+
expect(fakeToggleSelect._element.getAttribute('aria-expanded')).toBe('false')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test('Toggle the [hidden] attribute on the <div>', () => {
|
|
161
|
+
const divA = document.getElementById('fakeCollapseSelectA')
|
|
162
|
+
const divB = document.getElementById('fakeCollapseSelectB')
|
|
163
|
+
const divC = document.getElementById('fakeCollapseSelectB')
|
|
164
|
+
expect(divA.hidden).toBeTruthy()
|
|
165
|
+
expect(divB.hidden).toBeTruthy()
|
|
166
|
+
expect(divC.hidden).toBeTruthy()
|
|
167
|
+
fireEvent.change(fakeToggleSelect._element, { target: { value: 'A' } })
|
|
168
|
+
expect(divA.hidden).toBeFalsy()
|
|
169
|
+
expect(divB.hidden).toBeTruthy()
|
|
170
|
+
expect(divC.hidden).toBeTruthy()
|
|
171
|
+
fireEvent.change(fakeToggleSelect._element, { target: { value: 'B' } })
|
|
172
|
+
expect(divA.hidden).toBeTruthy()
|
|
173
|
+
expect(divB.hidden).toBeFalsy()
|
|
174
|
+
expect(divC.hidden).toBeFalsy()
|
|
175
|
+
fireEvent.change(fakeToggleSelect._element, { target: { value: 'null' } })
|
|
176
|
+
expect(divA.hidden).toBeTruthy()
|
|
177
|
+
expect(divB.hidden).toBeTruthy()
|
|
178
|
+
expect(divC.hidden).toBeTruthy()
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test('Emmit the toggle:changed event', () => {
|
|
184
|
+
const eventSpy = vi.spyOn(fakeToggleButton, 'emmitEvent')
|
|
185
|
+
expect(eventSpy).not.toHaveBeenCalled()
|
|
186
|
+
fireEvent(fakeToggleButton._element, new MouseEvent('click'))
|
|
187
|
+
expect(eventSpy).toHaveBeenCalledWith('changed')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
})
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ------------------------------------------------------------------
|
|
3
|
+
* TEST for the _tree.js
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* The test will take care of:
|
|
6
|
+
* ! herit from BaseComponent - Constructor: Passing the correct parameters
|
|
7
|
+
* - Constructor: Return the correct properties
|
|
8
|
+
* - #Init() : Do nothing if click outside the handle
|
|
9
|
+
* - #Toggle(): Passing the correct parameter
|
|
10
|
+
* - #Toggle(): List: Update the [aria-expanded] attribute
|
|
11
|
+
* - #Toggle(): List: Update the [hidden] attribute on children
|
|
12
|
+
* - #Toggle(): Grid: Update the [aria-expanded] attribute
|
|
13
|
+
* - #Toggle(): Grid: Update the [hidden] attribute on children
|
|
14
|
+
* - #Toggle(): Grid: Collapse the sub-children
|
|
15
|
+
* - #Toggle(): Emmit the tree:changed event
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { describe, test, expect, beforeAll, vi } from "vitest"
|
|
19
|
+
import { fireEvent } from "@testing-library/dom"
|
|
20
|
+
import Tree from "../_tree"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
let fakeListTree, fakeGridTree, fakeLi, fakeRow, customClick
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Before all tests
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
beforeAll(() => {
|
|
31
|
+
|
|
32
|
+
document.body.innerHTML =
|
|
33
|
+
'<ul id="fakeListTree" role="tree">' +
|
|
34
|
+
'<li role="treeitem"></li>' +
|
|
35
|
+
'<li role="treeitem" aria-expanded="false" aria-owns="child">' +
|
|
36
|
+
'<ul id="child" role="group" hidden>' +
|
|
37
|
+
'<li role="treeitem"></li>' +
|
|
38
|
+
'</ul>' +
|
|
39
|
+
'</li>' +
|
|
40
|
+
'<li role="treeitem"></li>' +
|
|
41
|
+
'</ul>' +
|
|
42
|
+
'<table id="fakeGridTree" role="treegrid">' +
|
|
43
|
+
'<tr aria-level="1"><td></td></tr>' +
|
|
44
|
+
'<tr aria-level="1" aria-expanded="false" aria-owns="lvl2"><td data-handle></td></tr>' +
|
|
45
|
+
'<tr id="lvl2" aria-level="2" aria-expanded="false" aria-owns="lvl3" hidden><td data-handle></td></tr>' +
|
|
46
|
+
'<tr id="lvl3" aria-level="3" hidden><td></td></tr>' +
|
|
47
|
+
'<tr aria-level="1"><td></td></tr>' +
|
|
48
|
+
'</table>'
|
|
49
|
+
|
|
50
|
+
fakeListTree = new Tree(document.getElementById('fakeListTree'))
|
|
51
|
+
fakeGridTree = new Tree(document.getElementById('fakeGridTree'))
|
|
52
|
+
fakeLi = fakeListTree._element.querySelector('li[aria-expanded]')
|
|
53
|
+
fakeRow = fakeGridTree._element.querySelector('tr[aria-level="1"][aria-expanded]')
|
|
54
|
+
|
|
55
|
+
customClick = new MouseEvent('click')
|
|
56
|
+
Object.defineProperty(customClick, 'target', { value: fakeRow.querySelector('[data-handle]') }) // Define the click event with custom target
|
|
57
|
+
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('Structure of the class', () => {
|
|
61
|
+
|
|
62
|
+
test('Constructor: Return the correct properties', () => {
|
|
63
|
+
expect(fakeListTree).toHaveProperty('_type')
|
|
64
|
+
expect(fakeListTree).toHaveProperty('_withHandle')
|
|
65
|
+
expect(fakeListTree).toHaveProperty('_items')
|
|
66
|
+
expect(fakeListTree._type).toBe('list')
|
|
67
|
+
expect(fakeGridTree._type).toBe('grid')
|
|
68
|
+
expect(fakeListTree._withHandle).toBeFalsy()
|
|
69
|
+
expect(fakeGridTree._withHandle).toBeTruthy()
|
|
70
|
+
expect(fakeListTree._items).toBeTypeOf('object')
|
|
71
|
+
expect(fakeListTree._items).toStrictEqual(document.querySelectorAll('#fakeListTree [aria-expanded]'))
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe('#Init()', () => {
|
|
77
|
+
|
|
78
|
+
test('Do nothing if click outside the handle', () => {
|
|
79
|
+
expect(fakeRow.getAttribute('aria-expanded')).toBe('false')
|
|
80
|
+
fireEvent(fakeRow, new MouseEvent('click'))
|
|
81
|
+
expect(fakeRow.getAttribute('aria-expanded')).toBe('false')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
describe('#Toggle()', () => {
|
|
87
|
+
|
|
88
|
+
describe('List', () => {
|
|
89
|
+
|
|
90
|
+
test('Update the [aria-expanded] attribute', () => {
|
|
91
|
+
expect(fakeLi.getAttribute('aria-expanded')).toBe('false')
|
|
92
|
+
fireEvent(fakeLi, new MouseEvent('click'))
|
|
93
|
+
expect(fakeLi.getAttribute('aria-expanded')).toBe('true')
|
|
94
|
+
fireEvent(fakeLi, new MouseEvent('click'))
|
|
95
|
+
expect(fakeLi.getAttribute('aria-expanded')).toBe('false')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('Update the [hidden] attribute on children', () => {
|
|
99
|
+
const child = document.getElementById('child')
|
|
100
|
+
expect(child.hidden).toBeTruthy()
|
|
101
|
+
fireEvent(fakeLi, new MouseEvent('click'))
|
|
102
|
+
expect(child.hidden).toBeFalsy()
|
|
103
|
+
fireEvent(fakeLi, new MouseEvent('click'))
|
|
104
|
+
expect(child.hidden).toBeTruthy()
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('Grid', () => {
|
|
110
|
+
|
|
111
|
+
test('Update the [aria-expanded] attribute', () => {
|
|
112
|
+
expect(fakeRow.getAttribute('aria-expanded')).toBe('false')
|
|
113
|
+
fireEvent(fakeRow, customClick)
|
|
114
|
+
expect(fakeRow.getAttribute('aria-expanded')).toBe('true')
|
|
115
|
+
fireEvent(fakeRow, customClick)
|
|
116
|
+
expect(fakeRow.getAttribute('aria-expanded')).toBe('false')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('Update the [hidden] attribute on children', () => {
|
|
120
|
+
const child = document.getElementById('lvl2')
|
|
121
|
+
expect(child.hidden).toBeTruthy()
|
|
122
|
+
fireEvent(fakeRow, customClick)
|
|
123
|
+
expect(child.hidden).toBeFalsy()
|
|
124
|
+
fireEvent(fakeRow, customClick)
|
|
125
|
+
expect(child.hidden).toBeTruthy()
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
test('Collapse the sub-children', () => {
|
|
129
|
+
const lvl2 = document.getElementById('lvl2')
|
|
130
|
+
const lvl3 = document.getElementById('lvl3')
|
|
131
|
+
|
|
132
|
+
const customClickLvl2 = new MouseEvent('click')
|
|
133
|
+
Object.defineProperty(customClickLvl2, 'target', { value: lvl2.querySelector('[data-handle]') }) // Define the click event with custom target
|
|
134
|
+
|
|
135
|
+
const customClickLvl3 = new MouseEvent('click')
|
|
136
|
+
Object.defineProperty(customClickLvl3, 'target', { value: lvl3.querySelector('[data-handle]') }) // Define the click event with custom target
|
|
137
|
+
|
|
138
|
+
fireEvent(fakeRow, customClick)
|
|
139
|
+
fireEvent(lvl2, customClickLvl2)
|
|
140
|
+
fireEvent(lvl3, customClickLvl3)
|
|
141
|
+
|
|
142
|
+
expect(fakeRow.getAttribute('aria-expanded')).toBe('true')
|
|
143
|
+
expect(lvl2.hidden).toBeFalsy()
|
|
144
|
+
expect(lvl2.getAttribute('aria-expanded')).toBe('true')
|
|
145
|
+
expect(lvl3.hidden).toBeFalsy()
|
|
146
|
+
|
|
147
|
+
fireEvent(fakeRow, customClick)
|
|
148
|
+
|
|
149
|
+
expect(fakeRow.getAttribute('aria-expanded')).toBe('false')
|
|
150
|
+
expect(lvl2.hidden).toBeTruthy()
|
|
151
|
+
expect(lvl2.getAttribute('aria-expanded')).toBe('false')
|
|
152
|
+
expect(lvl3.hidden).toBeTruthy()
|
|
153
|
+
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test('Emmit the tree:changed event', () => {
|
|
159
|
+
const eventSpy = vi.spyOn(fakeListTree, 'emmitEvent')
|
|
160
|
+
expect(eventSpy).not.toHaveBeenCalled()
|
|
161
|
+
fireEvent(fakeLi, new MouseEvent('click'))
|
|
162
|
+
expect(eventSpy).toHaveBeenCalledWith('changed', { isOpen: true })
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
})
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ------------------------------------------------------------------
|
|
3
|
+
* Base component
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* This class regroup common functionalities for other classes
|
|
6
|
+
*
|
|
7
|
+
* @author Natacha Herth
|
|
8
|
+
* @version 0.0.1
|
|
9
|
+
* @copyright Natacha Herth, design & web development
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import ErrorMessage from "./_error"
|
|
13
|
+
|
|
14
|
+
export default class BaseComponent {
|
|
15
|
+
|
|
16
|
+
#NAME
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates an instance
|
|
20
|
+
*
|
|
21
|
+
* @param {HTMLElement} el - The element
|
|
22
|
+
* @param {Object} options - The custom options
|
|
23
|
+
* @constructor
|
|
24
|
+
*/
|
|
25
|
+
constructor(el, options = {}) {
|
|
26
|
+
|
|
27
|
+
// Check for errors
|
|
28
|
+
if (!(el instanceof HTMLElement)) throw new Error(ErrorMessage.instanceOf('el', 'HTMLElement'))
|
|
29
|
+
if (!(options instanceof Object)) throw new Error(ErrorMessage.instanceOf('options', 'Object'))
|
|
30
|
+
|
|
31
|
+
// Define the #NAME private properties
|
|
32
|
+
this.#NAME = this.constructor.name.toLowerCase() ?? ''
|
|
33
|
+
|
|
34
|
+
// Define the _element property
|
|
35
|
+
this._element = el
|
|
36
|
+
|
|
37
|
+
// Define the _options property by merging the options parameter with the static OPTIONS
|
|
38
|
+
const defaultOptions = this.constructor.OPTIONS ?? {}
|
|
39
|
+
this._options = this.mergeObject(defaultOptions, options)
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Merge two object to a single one (accept multi-level)
|
|
45
|
+
*
|
|
46
|
+
* @param {Object} one
|
|
47
|
+
* @param {Object} two
|
|
48
|
+
* @returns {Object}
|
|
49
|
+
*/
|
|
50
|
+
mergeObject(one, two) {
|
|
51
|
+
|
|
52
|
+
// Check for errors
|
|
53
|
+
if (!(one instanceof Object)) throw new Error(ErrorMessage.instanceOf('one', 'Object'))
|
|
54
|
+
if (!(two instanceof Object)) throw new Error(ErrorMessage.instanceOf('two', 'Object'))
|
|
55
|
+
|
|
56
|
+
// Reduce the objects in a single one
|
|
57
|
+
return Object.keys(one).reduce((obj, key) => {
|
|
58
|
+
|
|
59
|
+
// Define the final option (if key is an object => call the method )
|
|
60
|
+
const option = two.hasOwnProperty(key) ? (one[key] instanceof Object ? this.mergeObject(one[key], two[key]) : two[key]) : one[key]
|
|
61
|
+
|
|
62
|
+
// Merge option
|
|
63
|
+
return { ...obj, [key]: option }
|
|
64
|
+
|
|
65
|
+
}, {})
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create a CustomEvent
|
|
71
|
+
*
|
|
72
|
+
* @param {String} name - The name of the event
|
|
73
|
+
* @param {Object} data - The data to pass
|
|
74
|
+
* @param {HTMLElement} selector - The HTML element who will have the event
|
|
75
|
+
* @returns {CustomEvent}
|
|
76
|
+
*/
|
|
77
|
+
emmitEvent(name, data = {}, selector = this._element) {
|
|
78
|
+
|
|
79
|
+
// Check for errors
|
|
80
|
+
if (typeof name !== 'string') throw new Error(ErrorMessage.typeOf('name', 'string'))
|
|
81
|
+
if (!(data instanceof Object)) throw new Error(ErrorMessage.instanceOf('data', 'Object'))
|
|
82
|
+
if (!(selector instanceof HTMLElement)) throw new Error(ErrorMessage.instanceOf('selector', 'HTMLElement'))
|
|
83
|
+
|
|
84
|
+
// Define the name of the event as classname:event
|
|
85
|
+
const eventName = `${this.#NAME}:${name}`
|
|
86
|
+
|
|
87
|
+
// Create a new CustomEvent
|
|
88
|
+
const newEvent = new CustomEvent(eventName, {
|
|
89
|
+
cancelable: true,
|
|
90
|
+
detail: data,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// Dispatch the event on the HTMLElement
|
|
94
|
+
selector.dispatchEvent(newEvent)
|
|
95
|
+
|
|
96
|
+
// Return the CustomEvent
|
|
97
|
+
return newEvent
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ------------------------------------------------------------------
|
|
3
|
+
* Cookie
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* This class create and manage the cookies
|
|
6
|
+
*
|
|
7
|
+
* @author Natacha Herth
|
|
8
|
+
* @version 0.0.1
|
|
9
|
+
* @copyright Natacha Herth, design & web development
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import ErrorMessage from "./_error"
|
|
13
|
+
|
|
14
|
+
export default class Cookie {
|
|
15
|
+
|
|
16
|
+
#NAME
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates an instance
|
|
20
|
+
*
|
|
21
|
+
* @param {string} name - The name of the cookie
|
|
22
|
+
* @constructor
|
|
23
|
+
*/
|
|
24
|
+
constructor(name) {
|
|
25
|
+
|
|
26
|
+
// Check for errors
|
|
27
|
+
if (typeof name !== 'string') throw new Error(ErrorMessage.typeOf('name', 'string'))
|
|
28
|
+
|
|
29
|
+
// Define the static private properties
|
|
30
|
+
this.#NAME = name
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get the cookie value
|
|
36
|
+
* Code from {@link https://www.w3schools.com/js/js_cookies.asp W3School}.
|
|
37
|
+
*
|
|
38
|
+
* @returns {object}
|
|
39
|
+
*/
|
|
40
|
+
get value() {
|
|
41
|
+
|
|
42
|
+
const name = `${this.#NAME}=`
|
|
43
|
+
let decodedCookie = decodeURIComponent(document.cookie)
|
|
44
|
+
let ca = decodedCookie.split(';')
|
|
45
|
+
for (let i = 0; i < ca.length; i++) {
|
|
46
|
+
let c = ca[i]
|
|
47
|
+
while (c.charAt(0) == ' ') {
|
|
48
|
+
c = c.substring(1)
|
|
49
|
+
}
|
|
50
|
+
if (c.indexOf(name) == 0) {
|
|
51
|
+
return JSON.parse(c.substring(name.length, c.length))
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return {}
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Set the object value as string in the cookie
|
|
60
|
+
*
|
|
61
|
+
* @param {object} value - Value of the cookie
|
|
62
|
+
*/
|
|
63
|
+
set(value) {
|
|
64
|
+
if (!(value instanceof Object)) throw new Error(ErrorMessage.instanceOf('value', 'Object'))
|
|
65
|
+
document.cookie = `${this.#NAME}=${JSON.stringify(value)};SameSite=Lax`
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if the cookie value has a specific key
|
|
70
|
+
*
|
|
71
|
+
* @param {string} key - The string to search
|
|
72
|
+
* @returns {boolean}
|
|
73
|
+
*/
|
|
74
|
+
has(key) {
|
|
75
|
+
if (typeof key !== 'string') throw new Error(ErrorMessage.typeOf('key', 'string'))
|
|
76
|
+
return this.value.hasOwnProperty(key)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get a specific cookie value per key
|
|
81
|
+
*
|
|
82
|
+
* @param {string} key - The string to search
|
|
83
|
+
* @returns
|
|
84
|
+
*/
|
|
85
|
+
get(key) {
|
|
86
|
+
if (typeof key !== 'string') throw new Error(ErrorMessage.typeOf('key', 'string'))
|
|
87
|
+
return this.has(key) ? this.value[key] : null
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Delete the cookie
|
|
92
|
+
*
|
|
93
|
+
*/
|
|
94
|
+
delete() {
|
|
95
|
+
document.cookie = this.#NAME + "=;SameSite=Lax; expires = Thu, 01 Jan 1970 00:00:00 GMT"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ------------------------------------------------------------------
|
|
3
|
+
* Error
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* This make global error message
|
|
6
|
+
*
|
|
7
|
+
* @author Natacha Herth
|
|
8
|
+
* @version 0.0.1
|
|
9
|
+
* @copyright Natacha Herth, design & web development
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export default class ErrorMessage {
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* For: !(parameter instanceof type)
|
|
16
|
+
*
|
|
17
|
+
* @param {string} parameter
|
|
18
|
+
* @param {string} type
|
|
19
|
+
* @returns {string}
|
|
20
|
+
*/
|
|
21
|
+
static instanceOf(parameter, type) {
|
|
22
|
+
return `Whoops, the "${parameter}" parameter must be an instance of ${type} !`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* For: typeof parameter !=== type
|
|
27
|
+
*
|
|
28
|
+
* @param {string} parameter
|
|
29
|
+
* @param {string} type
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
static typeOf(parameter, type) {
|
|
33
|
+
return `Whoops, the "${parameter}" parameter must be a ${type} !`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* For: ![values].includes(parameter)
|
|
38
|
+
*
|
|
39
|
+
* @param {string} parameter
|
|
40
|
+
* @param {string} type
|
|
41
|
+
* @returns {string}
|
|
42
|
+
*/
|
|
43
|
+
static enumOf(parameter, values) {
|
|
44
|
+
return `Whoops, the "${parameter}" parameter must be as ${values} !`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* For: !tag.length or document.querySelector(tag)
|
|
49
|
+
*
|
|
50
|
+
* @param {string} tag
|
|
51
|
+
* @returns {string}
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
static exist(tag) {
|
|
55
|
+
return `Whoops, there is no <${tag}> !`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* For: !document.getElementById(id)
|
|
60
|
+
*
|
|
61
|
+
* @param {string} tag
|
|
62
|
+
* @param {string} id
|
|
63
|
+
* @returns {string}
|
|
64
|
+
*/
|
|
65
|
+
static existById(tag, id) {
|
|
66
|
+
return `Whoops, there is no <${tag}> with the id "#${id}" !`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* For: !tag.hasAttribute(attribute)
|
|
71
|
+
*
|
|
72
|
+
* @param {string} tag
|
|
73
|
+
* @param {string} attribute
|
|
74
|
+
* @returns {string}
|
|
75
|
+
*/
|
|
76
|
+
static withAttribute(tag, attribute) {
|
|
77
|
+
return `Whoops, the <${tag}> must have the attribute [${attribute}] !`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
}
|