@knowark/componarkjs 1.13.3 → 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.
Files changed (177) hide show
  1. package/lib/base/component/component.js +17 -1
  2. package/lib/base/component/component.test.js +475 -389
  3. package/lib/base/utils/define.js +28 -6
  4. package/lib/base/utils/define.test.js +129 -42
  5. package/lib/base/utils/format.test.js +16 -16
  6. package/lib/base/utils/helpers.js +11 -4
  7. package/lib/base/utils/helpers.test.js +134 -115
  8. package/lib/base/utils/slots.test.js +38 -38
  9. package/lib/base/utils/uuid.test.js +13 -13
  10. package/lib/components/audio/components/audio.js +22 -3
  11. package/lib/components/audio/components/audio.test.js +120 -90
  12. package/lib/components/camera/components/camera.js +8 -3
  13. package/lib/components/camera/components/camera.test.js +96 -91
  14. package/lib/components/capture/components/capture.js +33 -5
  15. package/lib/components/capture/components/capture.test.js +165 -97
  16. package/lib/components/droparea/components/droparea-preview.js +66 -15
  17. package/lib/components/droparea/components/droparea-preview.test.js +262 -78
  18. package/lib/components/droparea/components/droparea.js +47 -8
  19. package/lib/components/droparea/components/droparea.test.js +309 -298
  20. package/lib/components/emit/components/emit.js +24 -4
  21. package/lib/components/emit/components/emit.test.js +192 -134
  22. package/lib/components/index.js +1 -1
  23. package/lib/components/list/components/{list.item.js → item.js} +1 -1
  24. package/lib/components/list/components/item.test.js +70 -69
  25. package/lib/components/list/components/list.js +35 -5
  26. package/lib/components/list/components/list.test.js +358 -227
  27. package/lib/components/list/index.js +1 -1
  28. package/lib/components/paginator/components/paginator.js +3 -2
  29. package/lib/components/paginator/components/paginator.test.js +146 -143
  30. package/lib/components/spinner/components/spinner.js +1 -1
  31. package/lib/components/spinner/components/spinner.test.js +36 -41
  32. package/lib/components/splitview/components/splitview.detail.js +1 -1
  33. package/lib/components/splitview/components/splitview.detail.test.js +78 -74
  34. package/lib/components/splitview/components/splitview.js +40 -10
  35. package/lib/components/splitview/components/splitview.master.js +29 -3
  36. package/lib/components/splitview/components/splitview.master.test.js +52 -52
  37. package/lib/components/splitview/components/splitview.test.js +136 -32
  38. package/lib/components/translate/components/translate.js +32 -10
  39. package/lib/components/translate/components/translate.test.js +492 -133
  40. package/package.json +7 -27
  41. package/scripts/node-test-setup.js +94 -0
  42. package/showcase/components/index.html +1 -1
  43. package/{jsconfig.json → tsconfig.json} +6 -4
  44. package/types/base/component/component.d.ts +48 -0
  45. package/types/base/component/component.d.ts.map +1 -0
  46. package/types/base/component/component.test.d.ts +2 -0
  47. package/types/base/component/component.test.d.ts.map +1 -0
  48. package/types/base/component/index.d.ts +8 -0
  49. package/types/base/component/index.d.ts.map +1 -0
  50. package/types/base/index.d.ts +2 -0
  51. package/types/base/index.d.ts.map +1 -0
  52. package/types/base/styles/index.d.ts +3 -0
  53. package/types/base/styles/index.d.ts.map +1 -0
  54. package/types/base/styles/styles.d.ts +3 -0
  55. package/types/base/styles/styles.d.ts.map +1 -0
  56. package/types/base/utils/define.d.ts +5 -0
  57. package/types/base/utils/define.d.ts.map +1 -0
  58. package/types/base/utils/define.test.d.ts +2 -0
  59. package/types/base/utils/define.test.d.ts.map +1 -0
  60. package/types/base/utils/format.d.ts +13 -0
  61. package/types/base/utils/format.d.ts.map +1 -0
  62. package/types/base/utils/format.test.d.ts +2 -0
  63. package/types/base/utils/format.test.d.ts.map +1 -0
  64. package/types/base/utils/helpers.d.ts +11 -0
  65. package/types/base/utils/helpers.d.ts.map +1 -0
  66. package/types/base/utils/helpers.test.d.ts +2 -0
  67. package/types/base/utils/helpers.test.d.ts.map +1 -0
  68. package/types/base/utils/index.d.ts +6 -0
  69. package/types/base/utils/index.d.ts.map +1 -0
  70. package/types/base/utils/slots.d.ts +15 -0
  71. package/types/base/utils/slots.d.ts.map +1 -0
  72. package/types/base/utils/slots.test.d.ts +2 -0
  73. package/types/base/utils/slots.test.d.ts.map +1 -0
  74. package/types/base/utils/uuid.d.ts +3 -0
  75. package/types/base/utils/uuid.d.ts.map +1 -0
  76. package/types/base/utils/uuid.test.d.ts +2 -0
  77. package/types/base/utils/uuid.test.d.ts.map +1 -0
  78. package/types/components/audio/components/audio.d.ts +18 -0
  79. package/types/components/audio/components/audio.d.ts.map +1 -0
  80. package/types/components/audio/components/audio.test.d.ts +2 -0
  81. package/types/components/audio/components/audio.test.d.ts.map +1 -0
  82. package/types/components/audio/index.d.ts +2 -0
  83. package/types/components/audio/index.d.ts.map +1 -0
  84. package/types/components/audio/styles/ark.css.d.ts +3 -0
  85. package/types/components/audio/styles/ark.css.d.ts.map +1 -0
  86. package/types/components/audio/styles/index.d.ts +3 -0
  87. package/types/components/audio/styles/index.d.ts.map +1 -0
  88. package/types/components/camera/components/camera.d.ts +18 -0
  89. package/types/components/camera/components/camera.d.ts.map +1 -0
  90. package/types/components/camera/components/camera.test.d.ts +2 -0
  91. package/types/components/camera/components/camera.test.d.ts.map +1 -0
  92. package/types/components/camera/index.d.ts +2 -0
  93. package/types/components/camera/index.d.ts.map +1 -0
  94. package/types/components/camera/styles/ark.css.d.ts +3 -0
  95. package/types/components/camera/styles/ark.css.d.ts.map +1 -0
  96. package/types/components/camera/styles/index.d.ts +3 -0
  97. package/types/components/camera/styles/index.d.ts.map +1 -0
  98. package/types/components/capture/components/capture.d.ts +10 -0
  99. package/types/components/capture/components/capture.d.ts.map +1 -0
  100. package/types/components/capture/components/capture.test.d.ts +2 -0
  101. package/types/components/capture/components/capture.test.d.ts.map +1 -0
  102. package/types/components/capture/index.d.ts +2 -0
  103. package/types/components/capture/index.d.ts.map +1 -0
  104. package/types/components/droparea/components/droparea-preview.d.ts +21 -0
  105. package/types/components/droparea/components/droparea-preview.d.ts.map +1 -0
  106. package/types/components/droparea/components/droparea-preview.test.d.ts +2 -0
  107. package/types/components/droparea/components/droparea-preview.test.d.ts.map +1 -0
  108. package/types/components/droparea/components/droparea.d.ts +32 -0
  109. package/types/components/droparea/components/droparea.d.ts.map +1 -0
  110. package/types/components/droparea/components/droparea.test.d.ts +2 -0
  111. package/types/components/droparea/components/droparea.test.d.ts.map +1 -0
  112. package/types/components/droparea/index.d.ts +2 -0
  113. package/types/components/droparea/index.d.ts.map +1 -0
  114. package/types/components/droparea/styles/ark.css.d.ts +3 -0
  115. package/types/components/droparea/styles/ark.css.d.ts.map +1 -0
  116. package/types/components/droparea/styles/index.d.ts +3 -0
  117. package/types/components/droparea/styles/index.d.ts.map +1 -0
  118. package/types/components/emit/components/emit.d.ts +10 -0
  119. package/types/components/emit/components/emit.d.ts.map +1 -0
  120. package/types/components/emit/components/emit.test.d.ts +2 -0
  121. package/types/components/emit/components/emit.test.d.ts.map +1 -0
  122. package/types/components/emit/index.d.ts +2 -0
  123. package/types/components/emit/index.d.ts.map +1 -0
  124. package/types/components/index.d.ts +10 -0
  125. package/types/components/index.d.ts.map +1 -0
  126. package/types/components/list/components/item.d.ts +8 -0
  127. package/types/components/list/components/item.d.ts.map +1 -0
  128. package/types/components/list/components/item.test.d.ts +2 -0
  129. package/types/components/list/components/item.test.d.ts.map +1 -0
  130. package/types/components/list/components/list.d.ts +13 -0
  131. package/types/components/list/components/list.d.ts.map +1 -0
  132. package/types/components/list/components/list.test.d.ts +2 -0
  133. package/types/components/list/components/list.test.d.ts.map +1 -0
  134. package/types/components/list/index.d.ts +3 -0
  135. package/types/components/list/index.d.ts.map +1 -0
  136. package/types/components/paginator/components/paginator.d.ts +32 -0
  137. package/types/components/paginator/components/paginator.d.ts.map +1 -0
  138. package/types/components/paginator/components/paginator.test.d.ts +2 -0
  139. package/types/components/paginator/components/paginator.test.d.ts.map +1 -0
  140. package/types/components/paginator/index.d.ts +2 -0
  141. package/types/components/paginator/index.d.ts.map +1 -0
  142. package/types/components/paginator/styles/ark.css.d.ts +3 -0
  143. package/types/components/paginator/styles/ark.css.d.ts.map +1 -0
  144. package/types/components/paginator/styles/index.d.ts +3 -0
  145. package/types/components/paginator/styles/index.d.ts.map +1 -0
  146. package/types/components/spinner/components/spinner.d.ts +11 -0
  147. package/types/components/spinner/components/spinner.d.ts.map +1 -0
  148. package/types/components/spinner/components/spinner.test.d.ts +2 -0
  149. package/types/components/spinner/components/spinner.test.d.ts.map +1 -0
  150. package/types/components/spinner/index.d.ts +2 -0
  151. package/types/components/spinner/index.d.ts.map +1 -0
  152. package/types/components/spinner/styles/ark.css.d.ts +3 -0
  153. package/types/components/spinner/styles/ark.css.d.ts.map +1 -0
  154. package/types/components/spinner/styles/index.d.ts +3 -0
  155. package/types/components/spinner/styles/index.d.ts.map +1 -0
  156. package/types/components/splitview/components/splitview.d.ts +12 -0
  157. package/types/components/splitview/components/splitview.d.ts.map +1 -0
  158. package/types/components/splitview/components/splitview.detail.d.ts +10 -0
  159. package/types/components/splitview/components/splitview.detail.d.ts.map +1 -0
  160. package/types/components/splitview/components/splitview.detail.test.d.ts +2 -0
  161. package/types/components/splitview/components/splitview.detail.test.d.ts.map +1 -0
  162. package/types/components/splitview/components/splitview.master.d.ts +8 -0
  163. package/types/components/splitview/components/splitview.master.d.ts.map +1 -0
  164. package/types/components/splitview/components/splitview.master.test.d.ts +2 -0
  165. package/types/components/splitview/components/splitview.master.test.d.ts.map +1 -0
  166. package/types/components/splitview/components/splitview.test.d.ts +2 -0
  167. package/types/components/splitview/components/splitview.test.d.ts.map +1 -0
  168. package/types/components/splitview/index.d.ts +4 -0
  169. package/types/components/splitview/index.d.ts.map +1 -0
  170. package/types/components/translate/components/translate.d.ts +18 -0
  171. package/types/components/translate/components/translate.d.ts.map +1 -0
  172. package/types/components/translate/components/translate.test.d.ts +2 -0
  173. package/types/components/translate/components/translate.test.d.ts.map +1 -0
  174. package/types/components/translate/index.d.ts +2 -0
  175. package/types/components/translate/index.d.ts.map +1 -0
  176. package/types/index.d.ts +3 -0
  177. package/types/index.d.ts.map +1 -0
@@ -1,105 +1,289 @@
1
- import { jest } from '@jest/globals'
1
+ import { it, mock } from 'node:test'
2
+ import assert from 'node:assert/strict'
2
3
  import './droparea-preview.js'
3
4
 
4
- describe('Droparea', () => {
5
- const createBubbledEvent = (type, props = {}) => {
6
- const event = new Event(type, {
7
- bubbles: true
8
- })
9
- Object.assign(event, props)
10
- return event
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
- global.URL.createObjectURL = jest.fn()
14
- global.document.elementFromPoint = jest.fn()
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()))
15
21
 
16
- let container = null
22
+ let container = null
17
23
 
18
- beforeEach(() => {
19
- container = document.createElement('div')
20
- document.body.appendChild(container)
21
- })
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
+ }
22
32
 
23
- afterEach(() => {
24
- container.remove()
25
- container = null
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
+ }
26
64
  })
27
65
 
28
- it('can be instantiated', () => {
29
- container.innerHTML = /* html */ `
30
- <ark-droparea></ark-droparea>
31
- `
32
- const droparea = container.querySelector('ark-droparea')
33
- const preview = droparea.querySelector('ark-droparea-preview')
34
- expect(preview).toBe(preview.init())
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
+ }
35
92
  })
36
93
 
37
- it('Item can be removed', () => {
38
- container.innerHTML = /* html */ `
39
- <ark-droparea></ark-droparea>
40
- `
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)
41
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
+ `
42
177
  const droparea = container.querySelector('ark-droparea')
43
- const preview = droparea.querySelector('[data-preview-list]')
44
178
  const dropZone = droparea.querySelector('.ark-droparea__form')
45
- const myFile = new File(['image'], 'Doggy.png', {
46
- type: 'image/png'
47
- })
48
- const myFile2 = new File(['image'], 'Scooby.png', {
49
- type: 'image/png'
179
+
180
+ const firstBatch = createBubbledEvent('drop', {
181
+ dataTransfer: { files: [new File(['image'], 'Doggy.png', { type: 'image/png' })] }
50
182
  })
51
- const dropEvent = createBubbledEvent('drop', {
52
- clientX: 0,
53
- clientY: 1,
54
- dataTransfer: {
55
- files: [myFile, myFile2]
56
- }
183
+ const secondBatch = createBubbledEvent('drop', {
184
+ dataTransfer: { files: [new File(['image'], 'Scooby.png', { type: 'image/png' })] }
57
185
  })
58
186
 
59
- dropZone.dispatchEvent(dropEvent)
60
- preview.querySelector('button').click()
61
- preview.querySelector('button').click()
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'
62
209
  })
63
210
 
64
- it('Can drag previews and sort a new list', () => {
65
- container.innerHTML = /* html */ `
66
- <ark-droparea></ark-droparea>
67
- `
211
+ dropZone.dispatchEvent(createBubbledEvent('drop', {
212
+ dataTransfer: { files: [myFile] }
213
+ }))
68
214
 
69
- const droparea = container.querySelector('ark-droparea')
70
- const preview = droparea.querySelector('[data-preview-list]')
71
- const dropZone = droparea.querySelector('.ark-droparea__form')
72
- const myFile = new File(['image'], 'Doggy.png', {
73
- type: 'image/png'
74
- })
75
- const myFile2 = new File(['image'], 'Scooby.png', {
76
- type: 'image/png'
77
- })
78
- const dropEvent = createBubbledEvent('drop', {
79
- clientX: 0,
80
- clientY: 1,
81
- dataTransfer: {
82
- files: [myFile, myFile2]
83
- }
84
- })
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' })
85
257
 
86
- dropZone.dispatchEvent(dropEvent)
258
+ assert.doesNotThrow(() => {
259
+ preview.removeFile(unknownFile, { target: button })
260
+ })
261
+ assert.ok(preview.select('[data-preview-list]').contains(frame))
262
+ })
87
263
 
88
- preview.handleDrag = jest.fn()
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
+ }
89
275
 
90
- const getThumbnails = () =>
91
- Array.from(preview.querySelectorAll('.ark-droparea-preview__frame'))
276
+ preview.enableDragItem(item)
92
277
 
93
- const thumbnails = getThumbnails()
278
+ assert.deepStrictEqual(calls, 0)
279
+ })
94
280
 
95
- const startingNode = thumbnails[0]
96
- const endingNode = thumbnails[1]
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' })
97
285
 
98
- startingNode.dispatchEvent(
99
- createBubbledEvent('dragstart', { clientX: 0, clientY: 0 })
100
- )
101
- endingNode.dispatchEvent(
102
- createBubbledEvent('dragend', { clientX: 0, clientY: 1 })
103
- )
286
+ assert.doesNotThrow(() => {
287
+ preview.revokeFile(file)
104
288
  })
105
289
  })
@@ -1,10 +1,18 @@
1
- import { Component } from '../../../base/component/index.js'
2
- import './droparea-preview.js'
1
+ import { Component } from '#base/index.js'
3
2
  import styles from '../styles/index.js'
4
- // @ts-ignore
3
+ import './droparea-preview.js'
4
+
5
+ /** @import {DropareaPreview} from './droparea-preview.js' */
6
+
5
7
  const tag = 'ark-droparea'
6
8
 
7
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
+
8
16
  init (context = {}) {
9
17
  this.fileList = []
10
18
  this.contextFiles = context.contextFiles || this.contextFiles || []
@@ -40,6 +48,8 @@ export class Droparea extends Component {
40
48
  }
41
49
 
42
50
  async load () {
51
+ this._detachListeners()
52
+
43
53
  this.dragDropEvents.forEach((eventName) => {
44
54
  this.addEventListener(eventName, this.preventDefaults, false)
45
55
  })
@@ -53,8 +63,8 @@ export class Droparea extends Component {
53
63
  })
54
64
 
55
65
  this.addEventListener('drop', this.handleDrop, false)
56
- this._input.addEventListener('change', this.onChange.bind(this))
57
- this.openButton.addEventListener('click', this.openInput.bind(this))
66
+ this._input?.addEventListener('change', this._onChange)
67
+ this.openButton?.addEventListener('click', this._onOpenInput)
58
68
 
59
69
  /* istanbul ignore else */
60
70
  if (this.contextFiles) {
@@ -62,6 +72,35 @@ export class Droparea extends Component {
62
72
  }
63
73
  }
64
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
+
65
104
  openInput (event) {
66
105
  event.stopPropagation()
67
106
  const input = this.select('[data-input]')
@@ -104,8 +143,9 @@ export class Droparea extends Component {
104
143
  !this.preview.fileExists(files[0]) &&
105
144
  this.maxSizeValidate(files[0])
106
145
  ) {
146
+ this.fileList[0] && this.preview.revokeFile(this.fileList[0])
107
147
  this.fileList[0] = files[0]
108
- this.preview.querySelector('[data-preview-list]').innerHTML = ''
148
+ this.preview.clearPreview()
109
149
  this.preview.previewFile(files[0])
110
150
  }
111
151
  } else {
@@ -153,7 +193,6 @@ export class Droparea extends Component {
153
193
  _grabSlots() {
154
194
  const [fileInput] = [this.slots.general].flat()
155
195
  this.fileInput = this.fileInput ?? fileInput
156
-
157
196
  }
158
197
 
159
198
  _buildFileInput (element) {
@@ -169,7 +208,7 @@ export class Droparea extends Component {
169
208
  }
170
209
 
171
210
  get preview () {
172
- return this.select('ark-droparea-preview')
211
+ return /** @type {DropareaPreview} */ (this.select('ark-droparea-preview'))
173
212
  }
174
213
 
175
214
  get mediaList () {