@knowark/componarkjs 1.13.4 → 1.14.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/lib/base/component/component.js +17 -1
- package/lib/base/component/component.test.js +475 -389
- package/lib/base/utils/define.js +28 -6
- package/lib/base/utils/define.test.js +129 -42
- package/lib/base/utils/format.test.js +16 -16
- package/lib/base/utils/helpers.js +11 -4
- package/lib/base/utils/helpers.test.js +134 -115
- package/lib/base/utils/slots.test.js +38 -38
- package/lib/base/utils/uuid.test.js +13 -13
- package/lib/components/audio/components/audio.js +19 -1
- package/lib/components/audio/components/audio.test.js +120 -90
- package/lib/components/camera/components/camera.js +5 -0
- package/lib/components/camera/components/camera.test.js +96 -91
- package/lib/components/capture/components/capture.js +30 -2
- package/lib/components/capture/components/capture.test.js +165 -97
- package/lib/components/droparea/components/droparea-preview.js +58 -8
- package/lib/components/droparea/components/droparea-preview.test.js +262 -80
- package/lib/components/droparea/components/droparea.js +41 -4
- package/lib/components/droparea/components/droparea.test.js +309 -299
- package/lib/components/emit/components/emit.js +23 -3
- package/lib/components/emit/components/emit.test.js +192 -134
- package/lib/components/list/components/item.test.js +69 -68
- package/lib/components/list/components/list.js +33 -3
- package/lib/components/list/components/list.test.js +358 -227
- package/lib/components/paginator/components/paginator.test.js +146 -143
- package/lib/components/spinner/components/spinner.test.js +36 -41
- package/lib/components/splitview/components/splitview.detail.test.js +75 -73
- package/lib/components/splitview/components/splitview.js +36 -8
- package/lib/components/splitview/components/splitview.master.js +27 -2
- package/lib/components/splitview/components/splitview.master.test.js +52 -52
- package/lib/components/splitview/components/splitview.test.js +135 -31
- package/lib/components/translate/components/translate.js +28 -8
- package/lib/components/translate/components/translate.test.js +492 -133
- package/package.json +3 -27
- package/scripts/node-test-setup.js +94 -0
|
@@ -1,107 +1,289 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { it, mock } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
2
3
|
import './droparea-preview.js'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
5
|
+
const createBubbledEvent = (type, props = {}) => {
|
|
6
|
+
const event = new Event(type, {
|
|
7
|
+
bubbles: true
|
|
8
|
+
})
|
|
9
|
+
Object.assign(event, props)
|
|
10
|
+
return event
|
|
11
|
+
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
let objectURLCount = 0
|
|
14
|
+
global.URL.createObjectURL = (
|
|
15
|
+
/** @type {(obj: Blob | MediaSource) => string} */ (
|
|
16
|
+
mock.fn(() => `mock://data/url/${objectURLCount++}`)))
|
|
17
|
+
global.URL.revokeObjectURL = (
|
|
18
|
+
/** @type {(url: string) => void} */ (mock.fn()))
|
|
19
|
+
global.document.elementFromPoint = (
|
|
20
|
+
/** @type {(x: number, y: number) => Element} */ (mock.fn()))
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
let container = null
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
const setup = () => {
|
|
25
|
+
document.body.innerHTML = ''
|
|
26
|
+
objectURLCount = 0
|
|
27
|
+
global.URL.createObjectURL.mock.resetCalls()
|
|
28
|
+
global.URL.revokeObjectURL.mock.resetCalls()
|
|
29
|
+
container = document.createElement('div')
|
|
30
|
+
document.body.appendChild(container)
|
|
31
|
+
}
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
it('can be instantiated', () => {
|
|
34
|
+
setup()
|
|
35
|
+
container.innerHTML = /* html */ `
|
|
36
|
+
<ark-droparea></ark-droparea>
|
|
37
|
+
`
|
|
38
|
+
const droparea = container.querySelector('ark-droparea')
|
|
39
|
+
const preview = droparea.querySelector('ark-droparea-preview')
|
|
40
|
+
assert.strictEqual(preview, preview.init())
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('Item can be removed', () => {
|
|
44
|
+
setup()
|
|
45
|
+
container.innerHTML = /* html */ `
|
|
46
|
+
<ark-droparea></ark-droparea>
|
|
47
|
+
`
|
|
48
|
+
|
|
49
|
+
const droparea = container.querySelector('ark-droparea')
|
|
50
|
+
const preview = droparea.querySelector('[data-preview-list]')
|
|
51
|
+
const dropZone = droparea.querySelector('.ark-droparea__form')
|
|
52
|
+
const myFile = new File(['image'], 'Doggy.png', {
|
|
53
|
+
type: 'image/png'
|
|
54
|
+
})
|
|
55
|
+
const myFile2 = new File(['image'], 'Scooby.png', {
|
|
56
|
+
type: 'image/png'
|
|
57
|
+
})
|
|
58
|
+
const dropEvent = createBubbledEvent('drop', {
|
|
59
|
+
clientX: 0,
|
|
60
|
+
clientY: 1,
|
|
61
|
+
dataTransfer: {
|
|
62
|
+
files: [myFile, myFile2]
|
|
63
|
+
}
|
|
28
64
|
})
|
|
29
65
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
66
|
+
dropZone.dispatchEvent(dropEvent)
|
|
67
|
+
preview.querySelector('button').click()
|
|
68
|
+
preview.querySelector('button').click()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('Can drag previews and sort a new list', () => {
|
|
72
|
+
setup()
|
|
73
|
+
container.innerHTML = /* html */ `
|
|
74
|
+
<ark-droparea></ark-droparea>
|
|
75
|
+
`
|
|
76
|
+
|
|
77
|
+
const droparea = container.querySelector('ark-droparea')
|
|
78
|
+
const preview = droparea.querySelector('[data-preview-list]')
|
|
79
|
+
const dropZone = droparea.querySelector('.ark-droparea__form')
|
|
80
|
+
const myFile = new File(['image'], 'Doggy.png', {
|
|
81
|
+
type: 'image/png'
|
|
82
|
+
})
|
|
83
|
+
const myFile2 = new File(['image'], 'Scooby.png', {
|
|
84
|
+
type: 'image/png'
|
|
85
|
+
})
|
|
86
|
+
const dropEvent = createBubbledEvent('drop', {
|
|
87
|
+
clientX: 0,
|
|
88
|
+
clientY: 1,
|
|
89
|
+
dataTransfer: {
|
|
90
|
+
files: [myFile, myFile2]
|
|
91
|
+
}
|
|
37
92
|
})
|
|
38
93
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
94
|
+
dropZone.dispatchEvent(dropEvent)
|
|
95
|
+
|
|
96
|
+
preview.handleDrag = mock.fn()
|
|
97
|
+
|
|
98
|
+
const getThumbnails = () =>
|
|
99
|
+
Array.from(preview.querySelectorAll('.ark-droparea-preview__frame'))
|
|
100
|
+
|
|
101
|
+
const thumbnails = getThumbnails()
|
|
102
|
+
|
|
103
|
+
const startingNode = thumbnails[0]
|
|
104
|
+
const endingNode = thumbnails[1]
|
|
105
|
+
|
|
106
|
+
startingNode.dispatchEvent(
|
|
107
|
+
createBubbledEvent('dragstart', { clientX: 0, clientY: 0 })
|
|
108
|
+
)
|
|
109
|
+
endingNode.dispatchEvent(
|
|
110
|
+
createBubbledEvent('dragend', { clientX: 0, clientY: 1 })
|
|
111
|
+
)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('handleDrag keeps the selected item when elementFromPoint returns null', () => {
|
|
115
|
+
setup()
|
|
116
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
117
|
+
const list = document.createElement('ul')
|
|
118
|
+
const firstItem = document.createElement('li')
|
|
119
|
+
const secondItem = document.createElement('li')
|
|
120
|
+
list.append(firstItem, secondItem)
|
|
121
|
+
preview.appendChild(list)
|
|
122
|
+
document.body.appendChild(preview)
|
|
123
|
+
|
|
124
|
+
const originalElementFromPoint = document.elementFromPoint
|
|
125
|
+
document.elementFromPoint = () => null
|
|
126
|
+
|
|
127
|
+
preview.handleDrag(firstItem, { clientX: 0, clientY: 0 })
|
|
128
|
+
|
|
129
|
+
assert.ok(firstItem.classList.contains('drag-sort-active'))
|
|
130
|
+
assert.strictEqual(list.firstElementChild, firstItem)
|
|
131
|
+
|
|
132
|
+
document.elementFromPoint = originalElementFromPoint
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('handleDrag moves the selected item after its next sibling', () => {
|
|
136
|
+
setup()
|
|
137
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
138
|
+
const list = document.createElement('ul')
|
|
139
|
+
const firstItem = document.createElement('li')
|
|
140
|
+
const secondItem = document.createElement('li')
|
|
141
|
+
const thirdItem = document.createElement('li')
|
|
142
|
+
list.append(firstItem, secondItem, thirdItem)
|
|
143
|
+
preview.appendChild(list)
|
|
144
|
+
document.body.appendChild(preview)
|
|
145
|
+
|
|
146
|
+
const originalElementFromPoint = document.elementFromPoint
|
|
147
|
+
document.elementFromPoint = () => secondItem
|
|
148
|
+
|
|
149
|
+
preview.handleDrag(firstItem, { clientX: 1, clientY: 1 })
|
|
150
|
+
|
|
151
|
+
assert.strictEqual(list.children[0], secondItem)
|
|
152
|
+
assert.strictEqual(list.children[1], firstItem)
|
|
153
|
+
assert.strictEqual(list.children[2], thirdItem)
|
|
43
154
|
|
|
155
|
+
document.elementFromPoint = originalElementFromPoint
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('does not add duplicate drag listeners to existing previews', () => {
|
|
159
|
+
setup()
|
|
160
|
+
const counts = new WeakMap()
|
|
161
|
+
const originalAddEventListener = Element.prototype.addEventListener
|
|
162
|
+
Element.prototype.addEventListener = function (type, listener, options) {
|
|
163
|
+
const isDragEvent = type === 'drag' || type === 'dragend'
|
|
164
|
+
if (this.tagName === 'LI' && isDragEvent) {
|
|
165
|
+
const itemCounts = counts.get(this) || { drag: 0, dragend: 0 }
|
|
166
|
+
itemCounts[type] += 1
|
|
167
|
+
counts.set(this, itemCounts)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return originalAddEventListener.call(this, type, listener, options)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
container.innerHTML = /* html */ `
|
|
175
|
+
<ark-droparea></ark-droparea>
|
|
176
|
+
`
|
|
44
177
|
const droparea = container.querySelector('ark-droparea')
|
|
45
|
-
const preview = droparea.querySelector('[data-preview-list]')
|
|
46
178
|
const dropZone = droparea.querySelector('.ark-droparea__form')
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const myFile2 = new File(['image'], 'Scooby.png', {
|
|
51
|
-
type: 'image/png'
|
|
179
|
+
|
|
180
|
+
const firstBatch = createBubbledEvent('drop', {
|
|
181
|
+
dataTransfer: { files: [new File(['image'], 'Doggy.png', { type: 'image/png' })] }
|
|
52
182
|
})
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
clientY: 1,
|
|
56
|
-
dataTransfer: {
|
|
57
|
-
files: [myFile, myFile2]
|
|
58
|
-
}
|
|
183
|
+
const secondBatch = createBubbledEvent('drop', {
|
|
184
|
+
dataTransfer: { files: [new File(['image'], 'Scooby.png', { type: 'image/png' })] }
|
|
59
185
|
})
|
|
60
186
|
|
|
61
|
-
dropZone.dispatchEvent(
|
|
62
|
-
|
|
63
|
-
|
|
187
|
+
dropZone.dispatchEvent(firstBatch)
|
|
188
|
+
dropZone.dispatchEvent(secondBatch)
|
|
189
|
+
|
|
190
|
+
const firstItem = droparea.querySelector('.ark-droparea-preview__frame')
|
|
191
|
+
const itemCounts = counts.get(firstItem)
|
|
192
|
+
|
|
193
|
+
assert.deepStrictEqual(itemCounts.drag, 1)
|
|
194
|
+
assert.deepStrictEqual(itemCounts.dragend, 1)
|
|
195
|
+
} finally {
|
|
196
|
+
Element.prototype.addEventListener = originalAddEventListener
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('reuses object URLs for the media list and revokes on file removal', () => {
|
|
201
|
+
setup()
|
|
202
|
+
container.innerHTML = /* html */ `
|
|
203
|
+
<ark-droparea></ark-droparea>
|
|
204
|
+
`
|
|
205
|
+
const droparea = container.querySelector('ark-droparea')
|
|
206
|
+
const dropZone = droparea.querySelector('.ark-droparea__form')
|
|
207
|
+
const myFile = new File(['image'], 'Doggy.png', {
|
|
208
|
+
type: 'image/png'
|
|
64
209
|
})
|
|
65
210
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`
|
|
211
|
+
dropZone.dispatchEvent(createBubbledEvent('drop', {
|
|
212
|
+
dataTransfer: { files: [myFile] }
|
|
213
|
+
}))
|
|
70
214
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
215
|
+
const callsBeforeMediaRead = global.URL.createObjectURL.mock.calls.length
|
|
216
|
+
const mediaUrl = droparea.mediaList[0].url
|
|
217
|
+
const mediaUrlSecondRead = droparea.mediaList[0].url
|
|
218
|
+
|
|
219
|
+
assert.deepStrictEqual(mediaUrl, mediaUrlSecondRead)
|
|
220
|
+
assert.deepStrictEqual(
|
|
221
|
+
global.URL.createObjectURL.mock.calls.length,
|
|
222
|
+
callsBeforeMediaRead
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
droparea.querySelector('.ark-droparea__remove').click()
|
|
226
|
+
assert.deepStrictEqual(
|
|
227
|
+
global.URL.revokeObjectURL.mock.calls[0].arguments[0],
|
|
228
|
+
mediaUrl
|
|
229
|
+
)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('returns early when handleDrop runs outside a droparea component', () => {
|
|
233
|
+
setup()
|
|
234
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
235
|
+
const target = document.createElement('li')
|
|
236
|
+
preview.appendChild(target)
|
|
237
|
+
document.body.appendChild(preview)
|
|
238
|
+
|
|
239
|
+
assert.doesNotThrow(() => {
|
|
240
|
+
preview.handleDrop({ target })
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('returns early when removing a file that does not exist', () => {
|
|
245
|
+
setup()
|
|
246
|
+
container.innerHTML = /* html */ `
|
|
247
|
+
<ark-droparea></ark-droparea>
|
|
248
|
+
`
|
|
249
|
+
const droparea = container.querySelector('ark-droparea')
|
|
250
|
+
const preview = droparea.preview
|
|
251
|
+
const button = document.createElement('button')
|
|
252
|
+
const frame = document.createElement('li')
|
|
253
|
+
frame.appendChild(button)
|
|
254
|
+
preview.select('[data-preview-list]').appendChild(frame)
|
|
255
|
+
|
|
256
|
+
const unknownFile = new File(['image'], 'unknown.png', { type: 'image/png' })
|
|
87
257
|
|
|
88
|
-
|
|
258
|
+
assert.doesNotThrow(() => {
|
|
259
|
+
preview.removeFile(unknownFile, { target: button })
|
|
260
|
+
})
|
|
261
|
+
assert.ok(preview.select('[data-preview-list]').contains(frame))
|
|
262
|
+
})
|
|
89
263
|
|
|
90
|
-
|
|
264
|
+
it('skips drag listeners for already-enabled items', () => {
|
|
265
|
+
setup()
|
|
266
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
267
|
+
const item = document.createElement('li')
|
|
268
|
+
item.setAttribute('data-drag-enabled', '')
|
|
269
|
+
let calls = 0
|
|
270
|
+
const originalAddEventListener = item.addEventListener
|
|
271
|
+
item.addEventListener = function (...args) {
|
|
272
|
+
calls += 1
|
|
273
|
+
return originalAddEventListener.apply(this, args)
|
|
274
|
+
}
|
|
91
275
|
|
|
92
|
-
|
|
93
|
-
Array.from(preview.querySelectorAll('.ark-droparea-preview__frame'))
|
|
276
|
+
preview.enableDragItem(item)
|
|
94
277
|
|
|
95
|
-
|
|
278
|
+
assert.deepStrictEqual(calls, 0)
|
|
279
|
+
})
|
|
96
280
|
|
|
97
|
-
|
|
98
|
-
|
|
281
|
+
it('does nothing when revoking a file without an object URL', () => {
|
|
282
|
+
setup()
|
|
283
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
284
|
+
const file = new File(['image'], 'Snoopy.png', { type: 'image/png' })
|
|
99
285
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
-
endingNode.dispatchEvent(
|
|
104
|
-
createBubbledEvent('dragend', { clientX: 0, clientY: 1 })
|
|
105
|
-
)
|
|
286
|
+
assert.doesNotThrow(() => {
|
|
287
|
+
preview.revokeFile(file)
|
|
106
288
|
})
|
|
107
289
|
})
|
|
@@ -7,6 +7,12 @@ import './droparea-preview.js'
|
|
|
7
7
|
const tag = 'ark-droparea'
|
|
8
8
|
|
|
9
9
|
export class Droparea extends Component {
|
|
10
|
+
constructor () {
|
|
11
|
+
super()
|
|
12
|
+
this._onChange = this.onChange.bind(this)
|
|
13
|
+
this._onOpenInput = this.openInput.bind(this)
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
init (context = {}) {
|
|
11
17
|
this.fileList = []
|
|
12
18
|
this.contextFiles = context.contextFiles || this.contextFiles || []
|
|
@@ -42,6 +48,8 @@ export class Droparea extends Component {
|
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
async load () {
|
|
51
|
+
this._detachListeners()
|
|
52
|
+
|
|
45
53
|
this.dragDropEvents.forEach((eventName) => {
|
|
46
54
|
this.addEventListener(eventName, this.preventDefaults, false)
|
|
47
55
|
})
|
|
@@ -55,8 +63,8 @@ export class Droparea extends Component {
|
|
|
55
63
|
})
|
|
56
64
|
|
|
57
65
|
this.addEventListener('drop', this.handleDrop, false)
|
|
58
|
-
this._input
|
|
59
|
-
this.openButton
|
|
66
|
+
this._input?.addEventListener('change', this._onChange)
|
|
67
|
+
this.openButton?.addEventListener('click', this._onOpenInput)
|
|
60
68
|
|
|
61
69
|
/* istanbul ignore else */
|
|
62
70
|
if (this.contextFiles) {
|
|
@@ -64,6 +72,35 @@ export class Droparea extends Component {
|
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
74
|
|
|
75
|
+
disconnectedCallback () {
|
|
76
|
+
this._detachListeners()
|
|
77
|
+
super.disconnectedCallback()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
_detachListeners () {
|
|
81
|
+
if (this.dragDropEvents) {
|
|
82
|
+
this.dragDropEvents.forEach((eventName) => {
|
|
83
|
+
this.removeEventListener(eventName, this.preventDefaults, false)
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this.dragEvents) {
|
|
88
|
+
this.dragEvents.forEach((eventName) => {
|
|
89
|
+
this.removeEventListener(eventName, this.highlight, false)
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (this.dropEvents) {
|
|
94
|
+
this.dropEvents.forEach((eventName) => {
|
|
95
|
+
this.removeEventListener(eventName, this.unhighlight, false)
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.removeEventListener('drop', this.handleDrop, false)
|
|
100
|
+
this._input?.removeEventListener('change', this._onChange)
|
|
101
|
+
this.openButton?.removeEventListener('click', this._onOpenInput)
|
|
102
|
+
}
|
|
103
|
+
|
|
67
104
|
openInput (event) {
|
|
68
105
|
event.stopPropagation()
|
|
69
106
|
const input = this.select('[data-input]')
|
|
@@ -106,8 +143,9 @@ export class Droparea extends Component {
|
|
|
106
143
|
!this.preview.fileExists(files[0]) &&
|
|
107
144
|
this.maxSizeValidate(files[0])
|
|
108
145
|
) {
|
|
146
|
+
this.fileList[0] && this.preview.revokeFile(this.fileList[0])
|
|
109
147
|
this.fileList[0] = files[0]
|
|
110
|
-
this.preview.
|
|
148
|
+
this.preview.clearPreview()
|
|
111
149
|
this.preview.previewFile(files[0])
|
|
112
150
|
}
|
|
113
151
|
} else {
|
|
@@ -155,7 +193,6 @@ export class Droparea extends Component {
|
|
|
155
193
|
_grabSlots() {
|
|
156
194
|
const [fileInput] = [this.slots.general].flat()
|
|
157
195
|
this.fileInput = this.fileInput ?? fileInput
|
|
158
|
-
|
|
159
196
|
}
|
|
160
197
|
|
|
161
198
|
_buildFileInput (element) {
|