Skip to content

Commit 25142d8

Browse files
committed
chore(test): Add tests
1 parent 19f88a8 commit 25142d8

File tree

5 files changed

+163
-28
lines changed

5 files changed

+163
-28
lines changed

.travis.yml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
language: node_js
2+
node_js:
3+
- "10"
4+
cache:
5+
directories:
6+
- node_modules
7+
script:
8+
- npm test
9+
- npm run build

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# React Multple Image Input
22

3+
[![Build Status](https://travis-ci.org/codenaz/react-multiple-image-input.svg?branch=master)](https://travis-ci.org/codenaz/react-multiple-image-input)
4+
35
![demo](https://s5.gifyu.com/images/upload-demo.gif)
46

57
A library for adding a simple multiple image upload and cropping feature to your react app.

src/__tests__/MultiImageInput.test.js

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React from 'react';
2+
import '@testing-library/jest-dom/extend-expect';
3+
import { render, fireEvent } from '@testing-library/react';
4+
import MultiImageInput from '../lib';
5+
6+
const crop = {
7+
unit: '%',
8+
aspect: 4 / 3,
9+
width: '100'
10+
};
11+
let images = {};
12+
const setImages = imageUpdate => (image = imageUpdate);
13+
14+
test('renders correctly', () => {
15+
let images = {};
16+
const setImages = imageUpdate => (image = imageUpdate);
17+
18+
const { baseElement } = render(
19+
<MultiImageInput
20+
images={images}
21+
setImages={setImages}
22+
cropConfig={{ crop, ruleOfThirds: true }}
23+
/>
24+
);
25+
expect(baseElement).toMatchSnapshot();
26+
});
27+
28+
test('renders the right number of images', () => {
29+
let images = {
30+
0: 'https://dababy.com/image.png',
31+
1: 'https://larajean.com/image.png'
32+
};
33+
const setImages = imageUpdate => (image = imageUpdate);
34+
const { getAllByAltText } = render(
35+
<MultiImageInput
36+
images={images}
37+
setImages={setImages}
38+
cropConfig={{ crop, ruleOfThirds: true }}
39+
/>
40+
);
41+
const selectedImages = getAllByAltText(/^uploaded image\d/);
42+
expect(selectedImages.length).toBe(2);
43+
});
44+
45+
test('renders the uploadImage element', () => {
46+
let images = {
47+
0: 'https://dababy.com/image.png',
48+
1: 'https://larajean.com/image.png'
49+
};
50+
const setImages = imageUpdate => (image = imageUpdate);
51+
const { getByText } = render(
52+
<MultiImageInput
53+
images={images}
54+
setImages={setImages}
55+
cropConfig={{ crop, ruleOfThirds: true }}
56+
/>
57+
);
58+
const uploadImage = getByText('ADD IMAGE');
59+
expect(uploadImage).not.toBeNull();
60+
});
61+
62+
test('deletes an image', () => {
63+
let images = {
64+
0: 'https://dababy.com/image.png',
65+
1: 'https://larajean.com/image.png',
66+
2: 'hhh'
67+
};
68+
const setImages = jest.fn(imageUpdate => (images = imageUpdate));
69+
const { getAllByRole } = render(
70+
<MultiImageInput
71+
images={images}
72+
setImages={setImages}
73+
cropConfig={{ crop, ruleOfThirds: true }}
74+
/>
75+
);
76+
const deleteButton = getAllByRole('button', { label: 'Delete Image 0' });
77+
fireEvent.click(deleteButton[1]);
78+
expect(setImages).toBeCalled();
79+
expect(Object.keys(images).length).toBe(2);
80+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`renders correctly 1`] = `
4+
<body>
5+
<div>
6+
<div
7+
class="sc-AxirZ sc-AxiKw lfdOxN"
8+
>
9+
<div
10+
class="sc-AxirZ sc-AxhCb cMEKOH"
11+
>
12+
<div
13+
role="button"
14+
style="cursor: pointer;"
15+
>
16+
<svg
17+
fill="none"
18+
height="46"
19+
style="margin-bottom: 0.5rem;"
20+
viewBox="0 0 58 46"
21+
width="58"
22+
>
23+
<path
24+
d="M57 0H1a1 1 0 00-1 1v44a1 1 0 001 1h56a1 1 0 001-1V1a1 1 0 00-1-1zm-1 44H2V2h54v42z"
25+
fill="rgba(255,255,255,0.6)"
26+
fill-opacity="0.6"
27+
/>
28+
<path
29+
d="M16 22.138a5.575 5.575 0 005.57-5.568A5.575 5.575 0 0016 11a5.575 5.575 0 00-5.569 5.569 5.575 5.575 0 005.57 5.569zM16 13a3.574 3.574 0 013.57 3.569A3.574 3.574 0 0116 20.138a3.573 3.573 0 01-3.569-3.568 3.574 3.574 0 013.57-3.57zM7 40c.234 0 .47-.082.66-.25L23.973 25.39l10.302 10.3a.999.999 0 101.414-1.413l-4.807-4.807 9.181-10.054 11.261 10.323a1 1 0 001.351-1.475l-12-11a1.031 1.031 0 00-.72-.262 1.002 1.002 0 00-.694.325l-9.794 10.727-4.743-4.743a1 1 0 00-1.368-.044L6.339 38.249A1 1 0 007 39.999z"
30+
fill="rgba(255,255,255,0.6)"
31+
fill-opacity="0.6"
32+
/>
33+
</svg>
34+
<span
35+
class="sc-Axmtr ekbNmN"
36+
color="outlineColor"
37+
font-size="small"
38+
style="display: block;"
39+
>
40+
ADD IMAGE
41+
</span>
42+
</div>
43+
<input
44+
accept="image/*"
45+
style="visibility: hidden;"
46+
type="file"
47+
/>
48+
</div>
49+
</div>
50+
</div>
51+
</body>
52+
`;

src/lib/multi-image-input.js

+20-28
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ export default function MultiImageInput({
2020
allowCrop,
2121
...props
2222
}) {
23-
const [numberOfImages, setNumberOfImages] = useState(1);
23+
const [numberOfImages, setNumberOfImages] = useState(
24+
Object.keys(files).length < max ? Object.keys(files).length : max
25+
);
2426

2527
const [fileUploadRefs, setFileUploadRefs] = useState({});
2628

27-
const [imagePreviews, setImagePreviews] = useState({});
28-
2929
const [currentImage, setCurrentImage] = useState(null);
3030

3131
const currentFile = useRef(null);
@@ -55,10 +55,11 @@ export default function MultiImageInput({
5555
}, [numberOfImages]);
5656

5757
useEffect(() => {
58-
if (numberOfImages < max && files[numberOfImages - 1]) {
59-
setNumberOfImages(n => n + 1);
58+
let imageCount = Object.keys(files).length;
59+
if (imageCount < max) {
60+
setNumberOfImages(Object.keys(files).length + 1);
6061
}
61-
}, [files, max, numberOfImages]);
62+
}, [files, max]);
6263

6364
const handleFileChange = (e, index) => {
6465
e.preventDefault();
@@ -71,11 +72,6 @@ export default function MultiImageInput({
7172

7273
reader.onloadend = () => {
7374
if (!allowCrop) {
74-
setImagePreviews({
75-
...imagePreviews,
76-
[index]: reader.result
77-
});
78-
7975
setFiles({ ...files, [index]: reader.result });
8076
return;
8177
}
@@ -100,11 +96,6 @@ export default function MultiImageInput({
10096
if (imageRef && imageRef.width && imageRef.height) {
10197
const base64Image = getCroppedImage(imageRef, crop);
10298

103-
setImagePreviews({
104-
...imagePreviews,
105-
[currentFileInputIndex.current]: base64Image
106-
});
107-
10899
setFiles({ ...files, [currentFileInputIndex.current]: base64Image });
109100
}
110101
};
@@ -151,25 +142,23 @@ export default function MultiImageInput({
151142
const removeImage = (e, index) => {
152143
fileUploadRefs[index].current.value = '';
153144

154-
delete files[index];
155-
delete imagePreviews[index];
156-
157145
const reIndexedFiles = {};
158-
const reIndexedPreviews = {};
159146

160147
for (let i = index - 1; i >= 0; i--) {
161148
reIndexedFiles[i] = files[i];
162-
reIndexedPreviews[i] = imagePreviews[i];
163149
}
164150

165-
for (let i = index; i < numberOfImages - 1; i++) {
166-
reIndexedFiles[i] = files[i + 1];
167-
reIndexedPreviews[i] = imagePreviews[i + 1];
151+
if (Object.keys(files).length === max) {
152+
for (let i = index; i < numberOfImages - 1; i++) {
153+
reIndexedFiles[i] = files[i + 1];
154+
}
155+
} else {
156+
for (let i = index; i < numberOfImages - 2; i++) {
157+
reIndexedFiles[i] = files[i + 1];
158+
}
168159
}
169160

170-
setImagePreviews(reIndexedPreviews);
171161
setFiles(reIndexedFiles);
172-
setNumberOfImages(n => n - 1);
173162

174163
exitCrop();
175164

@@ -184,21 +173,24 @@ export default function MultiImageInput({
184173
.fill()
185174
.map((_, index) => (
186175
<ImageInput key={index}>
187-
{imagePreviews[index] ? (
176+
{files[index] ? (
188177
<>
189178
<DeleteImageButton
179+
aria-label={`Delete Image ${index}`}
190180
onClick={e => removeImage(e, index)}
191181
type="button"
192182
/>
193183
<ImageOverlay>
194184
<Image
195-
src={imagePreviews[index]}
185+
alt={`uploaded image${index}`}
186+
src={files[index]}
196187
onClick={() => fileUploadRefs[index].current.click()}
197188
/>
198189
</ImageOverlay>
199190
</>
200191
) : (
201192
<div
193+
role="button"
202194
onClick={() => fileUploadRefs[index].current.click()}
203195
style={{ cursor: 'pointer' }}
204196
>

0 commit comments

Comments
 (0)