Skip to content

Commit 65f0b11

Browse files
authored
Merge pull request #20 from Visual-Regression-Tracker/47-comment
Add comment
2 parents 793cb74 + 6337a27 commit 65f0b11

File tree

9 files changed

+287
-114
lines changed

9 files changed

+287
-114
lines changed

package-lock.json

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"@testing-library/react": "^9.5.0",
1111
"@testing-library/user-event": "^7.2.1",
1212
"konva": "^4.2.2",
13+
"material-ui-popup-state": "^1.6.1",
1314
"qs": "^6.9.4",
1415
"react": "^16.13.1",
1516
"react-debounce-input": "^3.2.2",

src/components/CommentsPopper.tsx

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React from "react";
2+
import {
3+
Button,
4+
Popper,
5+
Fade,
6+
Paper,
7+
makeStyles,
8+
TextField,
9+
Badge,
10+
IconButton,
11+
} from "@material-ui/core";
12+
import {
13+
usePopupState,
14+
bindToggle,
15+
bindPopper,
16+
} from "material-ui-popup-state/hooks";
17+
import { Comment } from "@material-ui/icons";
18+
19+
const useStyles = makeStyles((theme) => ({
20+
popperContainer: {
21+
zIndex: 1400,
22+
},
23+
contentContainer: {
24+
padding: theme.spacing(2),
25+
},
26+
}));
27+
28+
interface IProps {
29+
text: string | undefined;
30+
onSave: (comment: string) => Promise<any>;
31+
}
32+
33+
export const CommentsPopper: React.FunctionComponent<IProps> = ({
34+
text,
35+
onSave,
36+
}) => {
37+
const classes = useStyles();
38+
const popupState = usePopupState({
39+
variant: "popper",
40+
popupId: "commentPopper",
41+
});
42+
const [comment, setComment] = React.useState("");
43+
44+
React.useEffect(() => setComment(text ? text : ""), [text]);
45+
46+
return (
47+
<React.Fragment>
48+
<IconButton {...bindToggle(popupState)}>
49+
<Badge
50+
color="secondary"
51+
variant="dot"
52+
invisible={!comment || comment === ""}
53+
>
54+
<Comment />
55+
</Badge>
56+
</IconButton>
57+
<Popper
58+
{...bindPopper(popupState)}
59+
transition
60+
disablePortal
61+
className={classes.popperContainer}
62+
>
63+
{({ TransitionProps }) => (
64+
<Fade {...TransitionProps} timeout={350}>
65+
<Paper className={classes.contentContainer}>
66+
<React.Fragment>
67+
<TextField
68+
id="comment"
69+
name="comment"
70+
variant="outlined"
71+
value={comment}
72+
placeholder={"Add any additional data here"}
73+
multiline
74+
rows={4}
75+
rowsMax={10}
76+
fullWidth
77+
onChange={(event) =>
78+
setComment((event.target as HTMLInputElement).value)
79+
}
80+
inputProps={{
81+
"data-testid": "comment",
82+
}}
83+
/>
84+
<Button
85+
onClick={() => {
86+
onSave(comment).then(() => popupState.close());
87+
}}
88+
>
89+
Save
90+
</Button>
91+
</React.Fragment>
92+
</Paper>
93+
</Fade>
94+
)}
95+
</Popper>
96+
</React.Fragment>
97+
);
98+
};

src/components/DrawArea.tsx

Lines changed: 106 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export const DrawArea: FunctionComponent<IDrawArea> = ({
7575

7676
const [isDrawMode, setIsDrawMode] = drawModeState;
7777
const [isDrawing, setIsDrawing] = React.useState(isDrawMode);
78-
const [image, loading] = useImage(staticService.getImage(imageName));
78+
const [image, imageStatus] = useImage(staticService.getImage(imageName));
7979

8080
const handleContentMousedown = (e: any) => {
8181
if (!isDrawMode) return;
@@ -126,124 +126,122 @@ export const DrawArea: FunctionComponent<IDrawArea> = ({
126126
<Grid item>
127127
<ImageDetails type={type} imageName={imageName} />
128128
</Grid>
129-
{imageName ? (
130-
loading === "loading" ? (
131-
<Grid
132-
container
133-
direction="column"
134-
alignItems="center"
135-
justify="center"
136-
className={classes.progressContainer}
137-
>
138-
<Grid item>
139-
<CircularProgress />
140-
</Grid>
129+
{imageStatus === "loading" && (
130+
<Grid
131+
container
132+
direction="column"
133+
alignItems="center"
134+
justify="center"
135+
className={classes.progressContainer}
136+
>
137+
<Grid item>
138+
<CircularProgress />
141139
</Grid>
142-
) : (
143-
<Grid item className={classes.canvasBackground}>
140+
</Grid>
141+
)}
142+
{(!imageName || imageStatus === "failed") && <NoImagePlaceholder />}
143+
{imageName && imageStatus === "loaded" && (
144+
<Grid item className={classes.canvasBackground}>
145+
<div
146+
className={classes.canvasContainer}
147+
style={{
148+
height: image && image?.height * stageScale,
149+
}}
150+
>
144151
<div
145-
className={classes.canvasContainer}
146152
style={{
147-
height: image && image?.height * stageScale,
153+
transform: `translate3d(${stagePos.x}px, ${stagePos.y}px, 0px)`,
154+
}}
155+
onMouseMove={(event) => {
156+
if (!isDrawMode && isDrag && !selectedRectId) {
157+
event.preventDefault();
158+
setStagePos({
159+
x: event.clientX - stageInitPos.x,
160+
y: event.clientY - stageInitPos.y,
161+
});
162+
setStageOffset(stagePos);
163+
}
164+
}}
165+
onMouseUp={(event) => {
166+
setIsDrag(false);
167+
setStageInitPos(stagePos);
168+
}}
169+
onMouseLeave={(event) => {
170+
setIsDrag(false);
171+
setStageInitPos(stagePos);
172+
}}
173+
onMouseDown={(event) => {
174+
setIsDrag(true);
175+
setStageInitPos({
176+
x: event.clientX - stageOffset.x,
177+
y: event.clientY - stageOffset.y,
178+
});
148179
}}
149180
>
150-
<div
181+
<Stage
182+
width={image && image.width}
183+
height={image && image.height}
184+
onMouseDown={onStageClick}
151185
style={{
152-
transform: `translate3d(${stagePos.x}px, ${stagePos.y}px, 0px)`,
153-
}}
154-
onMouseMove={(event) => {
155-
if (!isDrawMode && isDrag && !selectedRectId) {
156-
event.preventDefault();
157-
setStagePos({
158-
x: event.clientX - stageInitPos.x,
159-
y: event.clientY - stageInitPos.y,
160-
});
161-
setStageOffset(stagePos);
162-
}
163-
}}
164-
onMouseUp={(event) => {
165-
setIsDrag(false);
166-
setStageInitPos(stagePos);
167-
}}
168-
onMouseLeave={(event) => {
169-
setIsDrag(false);
170-
setStageInitPos(stagePos);
171-
}}
172-
onMouseDown={(event) => {
173-
setIsDrag(true);
174-
setStageInitPos({
175-
x: event.clientX - stageOffset.x,
176-
y: event.clientY - stageOffset.y,
177-
});
186+
transform: `scale(${stageScale})`,
187+
transformOrigin: "top left",
178188
}}
189+
onContentMousedown={handleContentMousedown}
190+
onContentMouseup={handleContentMouseup}
191+
onContentMouseMove={handleContentMouseMove}
179192
>
180-
<Stage
181-
width={image && image.width}
182-
height={image && image.height}
183-
onMouseDown={onStageClick}
184-
style={{
185-
transform: `scale(${stageScale})`,
186-
transformOrigin: "top left",
187-
}}
188-
onContentMousedown={handleContentMousedown}
189-
onContentMouseup={handleContentMouseup}
190-
onContentMouseMove={handleContentMouseMove}
191-
>
192-
<Layer>
193-
<Image
194-
image={image}
195-
onMouseOver={(event) => {
196-
document.body.style.cursor = isDrawMode
197-
? "crosshair"
198-
: "grab";
199-
}}
200-
onMouseDown={(event) => {
201-
document.body.style.cursor = "grabbing";
202-
}}
203-
onMouseUp={(event) => {
204-
document.body.style.cursor = "grab";
205-
}}
206-
onMouseLeave={(event) => {
207-
document.body.style.cursor = "default";
208-
}}
209-
/>
210-
{ignoreAreas.map((rect, i) => {
211-
return (
212-
<Rectangle
213-
key={rect.id}
214-
shapeProps={{
215-
x: rect.x,
216-
y: rect.y,
217-
width: rect.width,
218-
height: rect.height,
219-
}}
220-
isSelected={rect.id === selectedRectId}
221-
onSelect={() => setSelectedRectId(rect.id)}
222-
onChange={(newAttrs: RectConfig) => {
223-
const rects = ignoreAreas.slice();
193+
<Layer>
194+
<Image
195+
image={image}
196+
onMouseOver={(event) => {
197+
document.body.style.cursor = isDrawMode
198+
? "crosshair"
199+
: "grab";
200+
}}
201+
onMouseDown={(event) => {
202+
document.body.style.cursor = "grabbing";
203+
}}
204+
onMouseUp={(event) => {
205+
document.body.style.cursor = "grab";
206+
}}
207+
onMouseLeave={(event) => {
208+
document.body.style.cursor = "default";
209+
}}
210+
/>
211+
{ignoreAreas.map((rect, i) => {
212+
return (
213+
<Rectangle
214+
key={rect.id}
215+
shapeProps={{
216+
x: rect.x,
217+
y: rect.y,
218+
width: rect.width,
219+
height: rect.height,
220+
}}
221+
isSelected={rect.id === selectedRectId}
222+
onSelect={() => setSelectedRectId(rect.id)}
223+
onChange={(newAttrs: RectConfig) => {
224+
const rects = ignoreAreas.slice();
224225

225-
rects[i].x = Math.round(newAttrs.x || 0);
226-
rects[i].y = Math.round(newAttrs.y || 0);
227-
rects[i].width = Math.round(
228-
newAttrs.width || MIN_RECT_SIDE_PIXEL
229-
);
230-
rects[i].height = Math.round(
231-
newAttrs.height || MIN_RECT_SIDE_PIXEL
232-
);
226+
rects[i].x = Math.round(newAttrs.x || 0);
227+
rects[i].y = Math.round(newAttrs.y || 0);
228+
rects[i].width = Math.round(
229+
newAttrs.width || MIN_RECT_SIDE_PIXEL
230+
);
231+
rects[i].height = Math.round(
232+
newAttrs.height || MIN_RECT_SIDE_PIXEL
233+
);
233234

234-
setIgnoreAreas(rects);
235-
}}
236-
/>
237-
);
238-
})}
239-
</Layer>
240-
</Stage>
241-
</div>
235+
setIgnoreAreas(rects);
236+
}}
237+
/>
238+
);
239+
})}
240+
</Layer>
241+
</Stage>
242242
</div>
243-
</Grid>
244-
)
245-
) : (
246-
<NoImagePlaceholder />
243+
</div>
244+
</Grid>
247245
)}
248246
</Grid>
249247
</React.Fragment>

0 commit comments

Comments
 (0)