Skip to content

Commit d1e3dbc

Browse files
authored
Merge pull request #117 from guanquann/qn-service-preview
Update question details page
2 parents 394921a + 5f85c7f commit d1e3dbc

File tree

8 files changed

+249
-63
lines changed

8 files changed

+249
-63
lines changed

frontend/src/components/QuestionCodeTemplates/index.tsx

Lines changed: 31 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,41 @@ import {
33
Box,
44
IconButton,
55
Stack,
6-
TextField,
76
ToggleButton,
87
ToggleButtonGroup,
98
Tooltip,
109
Typography,
1110
} from "@mui/material";
11+
import CodeMirror from "@uiw/react-codemirror";
12+
import { EditorView } from "@codemirror/view";
13+
import { indentUnit } from "@codemirror/language";
14+
import { langs } from "@uiw/codemirror-extensions-langs";
15+
import { basicSetup } from "@uiw/codemirror-extensions-basic-setup";
1216
import { useState } from "react";
1317
import { CODE_TEMPLATES_TOOLTIP_MESSAGE } from "../../utils/constants";
1418

1519
interface QuestionCodeTemplatesProps {
1620
codeTemplates: {
1721
[key: string]: string;
1822
};
19-
setCodeTemplates: React.Dispatch<
23+
setCodeTemplates?: React.Dispatch<
2024
React.SetStateAction<{
2125
[key: string]: string;
2226
}>
2327
>;
28+
isEditable: boolean;
2429
}
2530

31+
const languageSupport = {
32+
python: langs.python(),
33+
java: langs.java(),
34+
c: langs.c(),
35+
};
36+
2637
const QuestionCodeTemplates: React.FC<QuestionCodeTemplatesProps> = ({
2738
codeTemplates,
2839
setCodeTemplates,
40+
isEditable,
2941
}) => {
3042
const [selectedLanguage, setSelectedLanguage] = useState<string>("python");
3143

@@ -38,32 +50,12 @@ const QuestionCodeTemplates: React.FC<QuestionCodeTemplatesProps> = ({
3850
}
3951
};
4052

41-
const handleCodeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
42-
const { value } = event.target;
43-
setCodeTemplates((prevTemplates) => ({
44-
...prevTemplates,
45-
[selectedLanguage]: value,
46-
}));
47-
};
48-
49-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
50-
const handleTabKeys = (event: any) => {
51-
const { value } = event.target;
52-
53-
if (event.key === "Tab") {
54-
event.preventDefault();
55-
56-
const cursorPosition = event.target.selectionStart;
57-
const cursorEndPosition = event.target.selectionEnd;
58-
const tab = "\t";
59-
60-
event.target.value =
61-
value.substring(0, cursorPosition) +
62-
tab +
63-
value.substring(cursorEndPosition);
64-
65-
event.target.selectionStart = cursorPosition + 1;
66-
event.target.selectionEnd = cursorPosition + 1;
53+
const handleCodeChange = (value: string) => {
54+
if (setCodeTemplates) {
55+
setCodeTemplates((prevTemplates) => ({
56+
...prevTemplates,
57+
[selectedLanguage]: value,
58+
}));
6759
}
6860
};
6961

@@ -104,27 +96,19 @@ const QuestionCodeTemplates: React.FC<QuestionCodeTemplatesProps> = ({
10496
<ToggleButton value="c">C</ToggleButton>
10597
</ToggleButtonGroup>
10698

107-
<TextField
108-
label={
109-
codeTemplates[selectedLanguage]
110-
? ``
111-
: `${
112-
selectedLanguage.charAt(0).toUpperCase() +
113-
selectedLanguage.slice(1)
114-
} Code Template`
115-
}
116-
variant="outlined"
117-
multiline
118-
rows={8}
119-
sx={{
120-
"& .MuiOutlinedInput-root": {
121-
fontFamily: "monospace",
122-
},
123-
}}
99+
<CodeMirror
100+
style={{ fontSize: "14px" }}
101+
basicSetup={false}
102+
id="codeEditor"
124103
value={codeTemplates[selectedLanguage]}
104+
extensions={[
105+
indentUnit.of("\t"),
106+
basicSetup(),
107+
languageSupport[selectedLanguage as keyof typeof languageSupport],
108+
EditorView.lineWrapping,
109+
EditorView.editable.of(isEditable),
110+
]}
125111
onChange={handleCodeChange}
126-
onKeyDown={handleTabKeys}
127-
fullWidth
128112
/>
129113
</Box>
130114
);

frontend/src/components/QuestionDetail/QuestionDetail.test.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,24 @@ describe("Question details", () => {
1818
const complexity = "Easy";
1919
const categories = ["Algorithms", "Data Structures"];
2020
const description = "# Test description";
21+
const pythonTemplate = "Python template";
22+
const javaTemplate = "Java template";
23+
const cTemplate = "C template";
24+
const inputTestCases = ["1", "2"];
25+
const outputTestCases = ["1", "2"];
2126
render(
2227
<QuestionDetail
2328
title={title}
2429
complexity={complexity}
2530
categories={categories}
2631
description={description}
32+
pythonTemplate={pythonTemplate}
33+
javaTemplate={javaTemplate}
34+
cTemplate={cTemplate}
35+
inputTestCases={inputTestCases}
36+
outputTestCases={outputTestCases}
37+
showCodeTemplate={true}
38+
showTestCases={true}
2739
/>
2840
);
2941
expect(screen.getByText(title)).toBeInTheDocument();
@@ -34,12 +46,24 @@ describe("Question details", () => {
3446
const complexity = "Easy";
3547
const categories = ["Algorithms", "Data Structures"];
3648
const description = "# Test description";
49+
const pythonTemplate = "Python template";
50+
const javaTemplate = "Java template";
51+
const cTemplate = "C template";
52+
const inputTestCases = ["1", "2"];
53+
const outputTestCases = ["1", "2"];
3754
render(
3855
<QuestionDetail
3956
title={title}
4057
complexity={complexity}
4158
categories={categories}
4259
description={description}
60+
pythonTemplate={pythonTemplate}
61+
javaTemplate={javaTemplate}
62+
cTemplate={cTemplate}
63+
inputTestCases={inputTestCases}
64+
outputTestCases={outputTestCases}
65+
showCodeTemplate={true}
66+
showTestCases={true}
4367
/>
4468
);
4569
expect(screen.getByText(complexity)).toBeInTheDocument();
@@ -50,12 +74,24 @@ describe("Question details", () => {
5074
const complexity = "Easy";
5175
const categories = ["Algorithms", "Data Structures"];
5276
const description = "# Test description";
77+
const pythonTemplate = "Python template";
78+
const javaTemplate = "Java template";
79+
const cTemplate = "C template";
80+
const inputTestCases = ["1", "2"];
81+
const outputTestCases = ["1", "2"];
5382
render(
5483
<QuestionDetail
5584
title={title}
5685
complexity={complexity}
5786
categories={categories}
5887
description={description}
88+
pythonTemplate={pythonTemplate}
89+
javaTemplate={javaTemplate}
90+
cTemplate={cTemplate}
91+
inputTestCases={inputTestCases}
92+
outputTestCases={outputTestCases}
93+
showCodeTemplate={true}
94+
showTestCases={true}
5995
/>
6096
);
6197
expect(screen.getByText(categories[0])).toBeInTheDocument();

frontend/src/components/QuestionDetail/index.tsx

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,53 @@
1-
import { Box, Chip, List, ListItem, Stack, Typography } from "@mui/material";
1+
import {
2+
Box,
3+
Chip,
4+
List,
5+
ListItem,
6+
Stack,
7+
Typography,
8+
styled,
9+
} from "@mui/material";
210
import MDEditor from "@uiw/react-md-editor";
11+
import QuestionCodeTemplates from "../QuestionCodeTemplates";
12+
import theme from "../../theme";
313

414
interface QuestionDetailProps {
515
title: string;
616
complexity: string | null;
717
categories: string[];
818
description: string;
19+
cTemplate: string;
20+
javaTemplate: string;
21+
pythonTemplate: string;
22+
inputTestCases: string[];
23+
outputTestCases: string[];
24+
showCodeTemplate: boolean;
25+
showTestCases: boolean;
926
}
1027

28+
const StyledBox = styled(Box)(({ theme }) => ({
29+
margin: theme.spacing(2, 0),
30+
}));
31+
32+
const StyledTypography = styled(Typography)(({ theme }) => ({
33+
background: theme.palette.divider,
34+
padding: theme.spacing(1, 2),
35+
borderRadius: theme.spacing(1),
36+
whiteSpace: "pre-line",
37+
}));
38+
1139
const QuestionDetail: React.FC<QuestionDetailProps> = ({
1240
title,
1341
complexity,
1442
categories,
1543
description,
44+
cTemplate,
45+
javaTemplate,
46+
pythonTemplate,
47+
inputTestCases,
48+
outputTestCases,
49+
showCodeTemplate,
50+
showTestCases,
1651
}) => {
1752
return (
1853
<Box
@@ -107,6 +142,41 @@ const QuestionDetail: React.FC<QuestionDetailProps> = ({
107142
}}
108143
/>
109144
</Stack>
145+
146+
{showTestCases && (
147+
<Stack marginTop={theme.spacing(4)}>
148+
<Typography variant="h6">Test Cases</Typography>
149+
{Array.from({
150+
length: Math.max(inputTestCases.length, outputTestCases.length),
151+
}).map((_, index) => (
152+
<Box key={index}>
153+
<StyledBox>
154+
<Typography variant="overline">Input</Typography>
155+
<StyledTypography fontFamily={"monospace"}>
156+
{inputTestCases[index] || "\u00A0"}
157+
</StyledTypography>
158+
</StyledBox>
159+
<StyledBox>
160+
<Typography variant="overline">Output</Typography>
161+
<StyledTypography fontFamily={"monospace"}>
162+
{outputTestCases[index] || "\u00A0"}
163+
</StyledTypography>
164+
</StyledBox>
165+
</Box>
166+
))}
167+
</Stack>
168+
)}
169+
170+
{showCodeTemplate && (
171+
<QuestionCodeTemplates
172+
codeTemplates={{
173+
python: pythonTemplate,
174+
java: javaTemplate,
175+
c: cTemplate,
176+
}}
177+
isEditable={false}
178+
/>
179+
)}
110180
</Box>
111181
);
112182
};

frontend/src/pages/CollabSandbox/index.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,22 @@ const CollabSandbox: React.FC = () => {
106106
return (
107107
<AppMargin classname={`${classes.fullheight} ${classes.flex}`}>
108108
<Grid2 container sx={{ flexGrow: 1 }} spacing={4}>
109-
<Grid2 sx={{ flexGrow: 1 }} size={6}>
109+
<Grid2
110+
sx={{ flexGrow: 1, overflowY: "auto", maxHeight: "90vh" }}
111+
size={6}
112+
>
110113
<QuestionDetailComponent
111114
title={selectedQuestion.title}
112115
description={selectedQuestion.description}
113116
complexity={selectedQuestion.complexity}
114117
categories={selectedQuestion.categories}
118+
cTemplate={selectedQuestion.cTemplate}
119+
javaTemplate={selectedQuestion.javaTemplate}
120+
pythonTemplate={selectedQuestion.pythonTemplate}
121+
inputTestCases={selectedQuestion.inputs}
122+
outputTestCases={selectedQuestion.outputs}
123+
showCodeTemplate={false}
124+
showTestCases={false}
115125
/>
116126
</Grid2>
117127
<Grid2

0 commit comments

Comments
 (0)