Skip to content

Commit 6aca711

Browse files
committed
Refactor McqPanel
1 parent 0e29e52 commit 6aca711

File tree

2 files changed

+56
-175
lines changed

2 files changed

+56
-175
lines changed

src/commands/showPanel.tsx

Lines changed: 41 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from "vscode";
22
// Allow using JSX within this file by overriding our own createElement function
33
import React from "../utils/FakeReact";
4+
import ReactDOMServer from "react-dom/server";
45

56
import Messages, {
67
MessageType,
@@ -14,6 +15,7 @@ import { Editor } from "../utils/editor";
1415
import { FRONTEND_ELEMENT_ID } from "../constants";
1516
import { client } from "../extension";
1617
import _ from "lodash";
18+
import { McqPanelWithLogging as McqPanel } from "../webview/components/McqPanel";
1719

1820
let mcqPanel: vscode.WebviewPanel | null = null;
1921

@@ -44,37 +46,49 @@ async function handleMessage(
4446
case MessageTypeNames.ExtensionPing:
4547
sendToFrontend(panel, Messages.ExtensionPong(null));
4648
break;
47-
case MessageTypeNames.MCQQuestion:
48-
{
49-
if (mcqPanel === null) {
50-
mcqPanel = vscode.window.createWebviewPanel(
51-
"mcq-question-panel",
52-
`Question ${message.questionId + 1}`,
53-
vscode.ViewColumn.One,
54-
{ enableScripts: true, retainContextWhenHidden: true },
55-
);
56-
mcqPanel.onDidDispose(() => {
57-
mcqPanel = null;
58-
});
59-
}
60-
mcqPanel.title = `Question ${message.questionId + 1}`;
61-
mcqPanel.iconPath = vscode.Uri.joinPath(
62-
context.extensionUri,
63-
"assets",
64-
"icon.png",
49+
case MessageTypeNames.MCQQuestion: {
50+
if (mcqPanel === null) {
51+
mcqPanel = vscode.window.createWebviewPanel(
52+
"mcq-question-panel",
53+
`Question ${message.questionId + 1}`,
54+
vscode.ViewColumn.One,
55+
{ enableScripts: true, retainContextWhenHidden: true },
6556
);
6657

67-
// Cast message to ensure properties exist
68-
const mcqMsg = message as any;
69-
mcqPanel.webview.html = getMcqHtml(
70-
mcqPanel.webview,
71-
mcqMsg.question,
72-
mcqMsg.options,
73-
mcqMsg.questionId,
74-
);
75-
mcqPanel.reveal(vscode.ViewColumn.One);
58+
mcqPanel.onDidDispose(() => {
59+
mcqPanel = null;
60+
});
7661
}
62+
mcqPanel.title = `Question ${message.questionId + 1}`;
63+
mcqPanel.iconPath = vscode.Uri.joinPath(
64+
context.extensionUri,
65+
"assets",
66+
"icon.png",
67+
);
68+
let activePanel = await McqPanel({
69+
data: {
70+
assessmentName: message.assessmentName ?? "",
71+
questionId: message.questionId,
72+
question: message.question,
73+
choices: message.options,
74+
workspaceLocation: message.workspaceLocation ?? "assessment",
75+
},
76+
});
77+
setWebviewContent(
78+
mcqPanel,
79+
context,
80+
<div
81+
// @ts-ignore: Our FakeReact doesn't modify the style attribute
82+
style="width: 100%; height: calc(100vh - 10px)"
83+
id="mcq-panel"
84+
>
85+
{ReactDOMServer.renderToString(activePanel)}
86+
</div>,
87+
);
88+
mcqPanel.reveal(vscode.ViewColumn.One);
89+
7790
break;
91+
}
7892

7993
case MessageTypeNames.NewEditor:
8094
// console.log(message.questionType + " questionType \n");
@@ -172,7 +186,6 @@ export async function showPanel(context: vscode.ExtensionContext) {
172186

173187
const frontendUrl = new URL("/playground", config.frontendBaseUrl).href;
174188

175-
// equivalent to panel.webview.html = ...
176189
setWebviewContent(
177190
panel,
178191
context,
@@ -201,141 +214,6 @@ export async function showPanel(context: vscode.ExtensionContext) {
201214
);
202215
}
203216

204-
// TODO: Move this to a util file
205-
function getMcqHtml(
206-
_webview: vscode.Webview,
207-
question: string,
208-
options: string[],
209-
questionId: string,
210-
): string {
211-
return `<!DOCTYPE html>
212-
<html lang="en">
213-
<head>
214-
<meta charset="UTF-8" />
215-
<meta name="viewport" content="width=device-width, initial-scale=1" />
216-
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-eval' 'unsafe-inline' https://unpkg.com; style-src 'unsafe-inline' https://unpkg.com;" />
217-
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
218-
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
219-
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
220-
<script src="https://unpkg.com/marked@4.0.0/marked.min.js"></script>
221-
<script>window.marked.setOptions({ breaks: true });</script>
222-
<style>
223-
.mcq-option {
224-
color: black !important;
225-
}
226-
.mcq-option p {
227-
margin: 0;
228-
display: inline;
229-
}
230-
</style>
231-
</head>
232-
<body>
233-
<div id="root"></div>
234-
<script type="text/babel" data-type="module">
235-
const { useState } = React;
236-
237-
function McqPanel({ question, options, questionId }) {
238-
const [selected, setSelected] = useState(null);
239-
240-
const handleSubmit = (e) => {
241-
e.preventDefault();
242-
if (selected === null) return;
243-
const vscode = acquireVsCodeApi();
244-
vscode.postMessage({ type: 'answer', answer: selected });
245-
};
246-
247-
return (
248-
<div style={{
249-
padding: '1rem',
250-
fontFamily: 'sans-serif',
251-
maxWidth: '800px',
252-
margin: '0 auto'
253-
}}>
254-
<h3>Question {parseInt(questionId) + 1}</h3>
255-
<div dangerouslySetInnerHTML={{ __html: window.marked.parse(question) }} />
256-
<form onSubmit={handleSubmit}>
257-
<ul style={{
258-
listStyle: 'none',
259-
padding: 0,
260-
margin: '1rem 0 1.5rem 0'
261-
}}>
262-
{options.map((option, index) => (
263-
<li
264-
key={index}
265-
style={{
266-
margin: '0.5rem 0',
267-
padding: '0.75rem',
268-
border: '1px solid #e1e4e8',
269-
borderRadius: '6px',
270-
backgroundColor: selected === index ? '#f6f8fa' : 'white',
271-
cursor: 'pointer',
272-
transition: 'background-color 0.2s'
273-
}}
274-
onClick={() => setSelected(index)}
275-
>
276-
<label style={{
277-
display: 'flex',
278-
alignItems: 'center',
279-
cursor: 'pointer',
280-
margin: 0
281-
}}>
282-
<input
283-
type="radio"
284-
name="mcq-option"
285-
checked={selected === index}
286-
onChange={() => setSelected(index)}
287-
style={{
288-
marginRight: '0.75rem',
289-
width: '1.25rem',
290-
height: '1.25rem',
291-
cursor: 'pointer'
292-
}}
293-
/>
294-
<span
295-
className="mcq-option"
296-
dangerouslySetInnerHTML={{ __html: window.marked.parse(option) }}
297-
/>
298-
</label>
299-
</li>
300-
))}
301-
</ul>
302-
<button
303-
type="submit"
304-
disabled={selected === null}
305-
style={{
306-
padding: '0.5rem 1.5rem',
307-
backgroundColor: selected !== null ? '#2ea043' : '#94d3a2',
308-
color: 'white',
309-
border: 'none',
310-
borderRadius: '6px',
311-
cursor: selected !== null ? 'pointer' : 'not-allowed',
312-
fontSize: '1rem',
313-
fontWeight: '500',
314-
transition: 'background-color 0.2s',
315-
opacity: selected !== null ? 1 : 0.7
316-
}}
317-
>
318-
Submit Answer
319-
</button>
320-
</form>
321-
</div>
322-
);
323-
}
324-
325-
// Render the component
326-
const root = ReactDOM.createRoot(document.getElementById('root'));
327-
root.render(
328-
<McqPanel
329-
question="${question.replace(/"/g, "&quot;")}"
330-
questionId="${questionId}"
331-
options={${JSON.stringify(options)}}
332-
/>
333-
);
334-
</script>
335-
</body>
336-
</html>`;
337-
}
338-
339217
export async function sendToFrontendWrapped(message: MessageType) {
340218
if (!panel) {
341219
console.error("ERROR: panel is not set");

src/webview/components/McqPanel.tsx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import React, { useState } from "react";
2-
import Messages from "../../utils/messages";
2+
import Messages, { sendToWebview } from "../../utils/messages";
33

44
export interface McqData {
55
assessmentName: string;
66
questionId: number;
77
question: string;
88
choices: string[];
9+
workspaceLocation?: "assessment" | "playground";
910
}
1011

1112
interface McqPanelProps {
@@ -28,26 +29,28 @@ const McqPanel: React.FC<McqPanelProps> = ({ data, onAnswer }) => {
2829
name="mcq-choice"
2930
value={idx}
3031
checked={selected === idx}
31-
onChange={() => setSelected(idx)}
32+
onChange={() => {
33+
setSelected(idx);
34+
onAnswer(idx);
35+
}}
3236
style={{ marginRight: "0.5rem" }}
3337
/>
3438
{c}
3539
</label>
3640
</li>
3741
))}
3842
</ul>
39-
<button
40-
disabled={selected === null}
41-
onClick={() => {
42-
if (selected !== null) {
43-
onAnswer(selected);
44-
}
45-
}}
46-
>
47-
Submit
48-
</button>
4943
</div>
5044
);
5145
};
5246

5347
export default McqPanel;
48+
49+
export const McqPanelWithLogging: React.FC<{ data: McqData }> = ({ data }) => (
50+
<McqPanel
51+
data={data}
52+
onAnswer={(choiceIndex) => {
53+
console.log("Selected choice index:", choiceIndex);
54+
}}
55+
/>
56+
);

0 commit comments

Comments
 (0)