Skip to content

Commit bfaabac

Browse files
authored
feat: allow multiple files picker on inline and array field
1 parent 7cb87d7 commit bfaabac

File tree

7 files changed

+82
-34
lines changed

7 files changed

+82
-34
lines changed

image_uploader_widget/static/image_uploader_widget/js/image-uploader-inline.js

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -138,49 +138,27 @@ function cloneFromEmptyTemplate(root) {
138138
}
139139

140140
function handleAddNewImage(root, tempFileInput, inputFile = null) {
141-
file = inputFile ||(tempFileInput.files || [null])[0];
142-
if (!file) {
143-
return;
144-
}
145-
if (!file.type.startsWith('image/')) {
141+
files = inputFile ? [inputFile] : (tempFileInput.files || []);
142+
if (!files.length) {
146143
return;
147144
}
148-
const row = cloneFromEmptyTemplate(root);
149-
const img = document.createElement('img');
150-
img.src = URL.createObjectURL(file);
151-
row.appendChild(img);
152-
const rowFileInput = row.querySelector('input[type=file]');
153-
const parent = rowFileInput.parentElement;
145+
for (const file of files) {
146+
if (!file.type.startsWith('image/')) {
147+
continue;
148+
}
149+
const row = cloneFromEmptyTemplate(root);
150+
const img = document.createElement('img');
151+
img.src = URL.createObjectURL(file);
152+
row.appendChild(img);
153+
const rowFileInput = row.querySelector('input[type=file]');
154+
// const parent = rowFileInput.parentElement;
154155

155-
if (!tempFileInput) {
156156
const dataTransferList = new DataTransfer();
157157
dataTransferList.items.add(file);
158158
rowFileInput.files = dataTransferList.files;
159-
160-
updateAllIndexes(root);
161-
return;
162159
}
163160

164-
const className = rowFileInput.className;
165-
const name = rowFileInput.getAttribute('name');
166-
parent.removeChild(rowFileInput);
167-
168-
clonedInput = tempFileInput.cloneNode(true)
169-
clonedInput.className = className;
170-
clonedInput.setAttribute('name', name || '');
171-
172-
//
173-
// TODO: Safari not clone files inside the input.
174-
//
175-
//
176-
const dataTransferList = new DataTransfer();
177-
dataTransferList.items.add(file);
178-
clonedInput.files = dataTransferList.files;
179-
180161
tempFileInput.value = null
181-
182-
parent.appendChild(clonedInput);
183-
184162
updateAllIndexes(root);
185163
}
186164

image_uploader_widget/templates/image_uploader_widget/admin/inline_image_uploader.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
type="file"
115115
class="temp_file"
116116
accept="{{ inline_admin_formset.formset.accept }}"
117+
multiple
117118
style="display: none;"
118119
/>
119120
</div>

image_uploader_widget/templates/image_uploader_widget/postgres/image_array.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
type="file"
145145
class="temp_file"
146146
accept="image/*"
147+
multiple
147148
style="display: none;"
148149
/>
149150
</div>

tests/e2e/inline_base.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ def test_choose_same_file_two_times(self):
4343
thumbs = self.inline_po.get_visible_thumbnails()
4444
self.assertEqual(len(thumbs), 2)
4545

46+
def test_choose_multiple_images(self):
47+
self.goto_add_page()
48+
49+
images = ["image1.png", "image2.png", "image3.png"]
50+
self.inline_po.execute_select_multiple_images(images)
51+
52+
thumbs = self.inline_po.get_visible_thumbnails()
53+
self.assertEqual(len(thumbs), 3)
54+
55+
self.inline_po.execute_select_image("image1.png")
56+
thumbs = self.inline_po.get_visible_thumbnails()
57+
self.assertEqual(len(thumbs), 4)
58+
4659
def test_initial_state(self):
4760
self.goto_add_page()
4861
time.sleep(0.3)

tests/e2e/tests_array_field/tests_e2e_widget_array_field.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,32 @@ def test_choose_files_and_save(self):
6262
for url in item.images:
6363
self.assertIsNotNone(url)
6464

65+
def test_choose_multiple_files_and_save(self):
66+
self.assertEqual(len(models.TestWithArrayField.objects.all()), 0)
67+
self.goto_add_page()
68+
69+
thumbs = self.inline_po.get_visible_thumbnails()
70+
self.assertEqual(len(thumbs), 0)
71+
72+
self.inline_po.execute_select_multiple_images(["image1.png", "image2.png"])
73+
thumbs = self.inline_po.get_visible_thumbnails()
74+
self.assertEqual(len(thumbs), 2)
75+
76+
self.inline_po.execute_select_multiple_images(["image2.png", "image3.png"])
77+
thumbs = self.inline_po.get_visible_thumbnails()
78+
self.assertEqual(len(thumbs), 4)
79+
80+
for thumb in thumbs:
81+
self.assertTrue(thumb.is_valid(required=False))
82+
83+
self.admin_po.change_form.submit_form()
84+
85+
item = models.TestWithArrayField.objects.first()
86+
self.assertIsNotNone(item)
87+
self.assertEqual(len(item.images), 4)
88+
for url in item.images:
89+
self.assertIsNotNone(url)
90+
6591
def test_remove_unsaved_image(self):
6692
self.assertEqual(len(models.TestWithArrayField.objects.all()), 0)
6793
self.goto_add_page()

tests/e2e/tests_inline/tests_e2e_inline.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,31 @@ def test_choose_files_and_save(self):
5555
for item in items:
5656
self.assertIsNotNone(item.image)
5757

58+
def test_choose_multiple_files_and_save(self):
59+
self.assertEqual(len(models.InlineItem.objects.all()), 0)
60+
self.goto_add_page()
61+
62+
thumbs = self.inline_po.get_visible_thumbnails()
63+
self.assertEqual(len(thumbs), 0)
64+
65+
self.inline_po.execute_select_multiple_images(["image1.png", "image2.png"])
66+
thumbs = self.inline_po.get_visible_thumbnails()
67+
self.assertEqual(len(thumbs), 2)
68+
69+
self.inline_po.execute_select_multiple_images(["image2.png", "image3.png"])
70+
thumbs = self.inline_po.get_visible_thumbnails()
71+
self.assertEqual(len(thumbs), 4)
72+
73+
for thumb in thumbs:
74+
self.assertTrue(thumb.is_valid(required=False))
75+
76+
self.admin_po.change_form.submit_form()
77+
78+
items = models.InlineItem.objects.all()
79+
self.assertEqual(len(items), 4)
80+
for item in items:
81+
self.assertIsNotNone(item.image)
82+
5883
def test_remove_non_saved_itens(self):
5984
self.assertEqual(len(models.InlineItem.objects.all()), 0)
6085
self.goto_add_page()

tests/pom/component/inline_po.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ def execute_select_image(self, image: str):
2929
image = get_mock_image(image)
3030
self.page_elements.temp_file_input.set_input_files(image)
3131

32+
def execute_select_multiple_images(self, images):
33+
images = [get_mock_image(img) for img in images]
34+
self.page_elements.temp_file_input.set_input_files(images)
35+
3236
def is_add_button_visible(self):
3337
return self.page_elements.add_image_button.is_visible()
3438

0 commit comments

Comments
 (0)