• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wger-project / react / 25595441085

09 May 2026 07:32AM UTC coverage: 78.159% (+0.007%) from 78.152%
25595441085

push

github

rolandgeider
Refactor the image upload workflow

We now show the image form first and then allow users to upload the new image,
which can now be done via drag-and-drop

2235 of 3149 branches covered (70.97%)

Branch coverage included in aggregate %.

21 of 34 new or added lines in 4 files covered. (61.76%)

1 existing line in 1 file now uncovered.

4836 of 5898 relevant lines covered (81.99%)

33.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.46
/src/components/Exercises/forms/ImageDropZone.tsx
1
import AddPhotoAlternateIcon from '@mui/icons-material/AddPhotoAlternate';
24✔
2
import { Box, Stack, Typography } from "@mui/material";
3
import { ImageFormData } from "@/components/Exercises/models/exercise";
4
import { useFormikContext } from "formik";
5
import * as React from 'react';
6
import { useTranslation } from "react-i18next";
7

8
export function ImageDropZone() {
9
    const [t] = useTranslation();
268✔
10
    const { values, setFieldValue } = useFormikContext<ImageFormData>();
268✔
11
    const [isDragOver, setIsDragOver] = React.useState(false);
268✔
12
    const inputRef = React.useRef<HTMLInputElement>(null);
268✔
13

14
    const handleFile = (file: File) => {
268✔
15
        if (!file.type.startsWith('image/')) {
2!
NEW
16
            return;
×
17
        }
18
        setFieldValue('file', file);
2✔
19
        setFieldValue('url', URL.createObjectURL(file));
2✔
20
    };
21

22
    const handleDrop = (e: React.DragEvent<HTMLElement>) => {
268✔
NEW
23
        e.preventDefault();
×
NEW
24
        setIsDragOver(false);
×
NEW
25
        const file = e.dataTransfer.files?.[0];
×
NEW
26
        if (file) {
×
NEW
27
            handleFile(file);
×
28
        }
29
    };
30

31
    return (
268✔
32
        <Box
33
            onDragOver={(e) => {
NEW
34
                e.preventDefault();
×
NEW
35
                setIsDragOver(true);
×
36
            }}
NEW
37
            onDragLeave={() => setIsDragOver(false)}
×
38
            onDrop={handleDrop}
39
            onClick={() => inputRef.current?.click()}
7,031✔
40
            data-testid="image-dropzone"
41
            sx={{
42
                position: 'relative',
43
                border: '2px dashed',
44
                borderColor: isDragOver ? 'primary.main' : 'divider',
268!
45
                borderRadius: 1,
46
                cursor: 'pointer',
47
                aspectRatio: '1',
48
                display: 'flex',
49
                alignItems: 'center',
50
                justifyContent: 'center',
51
                overflow: 'hidden',
52
                bgcolor: isDragOver ? 'action.hover' : 'transparent',
268!
53
                // Visible children must not capture drag events; they would cause flicker.
54
                // The hidden file input is excluded so it remains clickable via the input ref.
55
                '& > *:not(input)': { pointerEvents: 'none' },
56
            }}
57
        >
58
            {values.url ? (
268✔
59
                <img
60
                    style={{ width: '100%', height: '100%', objectFit: 'cover' }}
61
                    src={values.url}
62
                    alt="Preview"
63
                />
64
            ) : (
65
                <Stack spacing={1} sx={{ alignItems: 'center', px: 1, textAlign: 'center' }}>
66
                    <AddPhotoAlternateIcon sx={{ fontSize: 48, color: 'text.secondary' }} />
67
                    <Typography variant="caption" color="text.secondary">
68
                        {t('exercises.dropOrClickImage')}
69
                    </Typography>
70
                </Stack>
71
            )}
72
            <input
73
                ref={inputRef}
74
                type="file"
75
                accept="image/*"
76
                style={{ display: 'none' }}
77
                data-testid="image-dropzone-input"
78
                onChange={(e) => {
79
                    const file = e.target.files?.[0];
2✔
80
                    if (file) {
2!
81
                        handleFile(file);
2✔
82
                    }
83
                    e.target.value = '';
2✔
84
                }}
85
            />
86
        </Box>
87
    );
88
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc