Skip to content

Commit 7e865b8

Browse files
committed
feat: add latex support
1 parent 5b1402e commit 7e865b8

File tree

9 files changed

+122
-13
lines changed

9 files changed

+122
-13
lines changed

public/index.html

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
<!doctype html>
1+
<!DOCTYPE html>
22
<html lang="en-us">
33
<head>
4-
<title>Open Response Assessment| <%= process.env.SITE_NAME %></title>
5-
<meta charset="utf-8">
6-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7-
<link rel="shortcut icon" href="<%=htmlWebpackPlugin.options.FAVICON_URL%>" type="image/x-icon" />
4+
<title>Open Response Assessment| <%= process.env.SITE_NAME %></title>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<link rel="shortcut icon" href="<%=htmlWebpackPlugin.options.FAVICON_URL%>" type="image/x-icon" />
88
<!-- Hotjar Tracking Code for https://www.edx.org/ -->
99
<script>
1010
const isMobile = window.navigator.userAgent.includes("org.edx.mobile");
@@ -19,7 +19,57 @@
1919
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
2020
}
2121
</script>
22-
</head>
22+
<script type="text/x-mathjax-config">
23+
MathJax.Hub.Config({
24+
styles: {
25+
'.MathJax_SVG>svg': { 'max-width': '100%', },
26+
// This is to resolve for people who use center mathjax with tables
27+
'table>tbody>tr>td>.MathJax_SVG>svg': { 'max-width': 'inherit'},
28+
},
29+
CommonHTML: { linebreaks: { automatic: true } },
30+
SVG: { linebreaks: { automatic: true } },
31+
"HTML-CSS": { linebreaks: { automatic: true } },
32+
tex2jax: {inlineMath: [ ['$','$'], ["\\(","\\)"]],
33+
displayMath: [ ['$$','$$'], ["\\[","\\]"]],
34+
processEscapes: true},
35+
});
36+
</script>
37+
<script type="text/javascript">
38+
// Activating Mathjax accessibility files
39+
window.MathJax = {
40+
menuSettings: {
41+
collapsible: true,
42+
autocollapse: false,
43+
explorer: true,
44+
},
45+
};
46+
window.addEventListener('resize', MJrenderer);
47+
48+
let t = -1;
49+
let delay = 1000;
50+
let oldWidth = document.documentElement.scrollWidth;
51+
function MJrenderer() {
52+
// don't rerender if the window is the same size as before
53+
if (t >= 0) {
54+
window.clearTimeout(t);
55+
}
56+
if (oldWidth !== document.documentElement.scrollWidth) {
57+
t = window.setTimeout(function () {
58+
oldWidth = document.documentElement.scrollWidth;
59+
MathJax.Hub.Queue(['Rerender', MathJax.Hub]);
60+
t = -1;
61+
}, delay);
62+
}
63+
}
64+
</script>
65+
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates.
66+
It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of
67+
MathJax extension libraries -->
68+
<script
69+
type="text/javascript"
70+
src="https://cdn.jsdelivr.net/npm/[email protected]/MathJax.js?config=TeX-MML-AM_SVG"
71+
></script>
72+
</head>
2373
<body>
2474
<div id="root"></div>
2575
</body>

src/components/Prompt/index.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { useViewStep } from 'hooks/routing';
1010
import messages from './messages';
1111
import usePromptHooks from './hooks';
1212

13+
import './index.scss';
14+
1315
const Prompt = ({ prompt, defaultOpen }) => {
1416
const { open, toggleOpen } = usePromptHooks({ defaultOpen });
1517
const { formatMessage } = useIntl();
@@ -33,7 +35,7 @@ const Prompt = ({ prompt, defaultOpen }) => {
3335
.replaceAll(staticRegex.link, `a href="${process.env.LMS_BASE_URL}/${baseAssetUrl}$1"`);
3436
return (
3537
<Collapsible title={(<h3 className="py-3">{title}</h3>)} open={open} onToggle={toggleOpen}>
36-
<div dangerouslySetInnerHTML={{ __html: promptWithStaticAssets }} />
38+
<div className='prompt' dangerouslySetInnerHTML={{ __html: promptWithStaticAssets }} />
3739
</Collapsible>
3840
);
3941
};

src/components/Prompt/index.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.prompt > pre {
2+
text-wrap: wrap;
3+
}

src/components/TextResponse/index.jsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useEffect } from 'react';
22
import PropTypes from 'prop-types';
33

44
import { useSubmissionConfig } from 'hooks/app';
@@ -7,11 +7,18 @@ import './index.scss';
77

88
const TextResponse = ({ response }) => {
99
const { textResponseConfig } = useSubmissionConfig();
10+
const textResponseRef = React.useRef(null);
11+
12+
useEffect(() => {
13+
if (textResponseConfig.allowLatexPreview) {
14+
MathJax.Hub.Queue(['Typeset', MathJax.Hub, textResponseRef.current]);
15+
}
16+
}, [response]);
1017

1118
return (
12-
<div className="my-2 p-2 bg-white">
19+
<div ref={textResponseRef} className="my-2 p-2 bg-white">
1320
{textResponseConfig.editorType === 'text' ? (
14-
<pre className="pre-like-textarea p-1">{response}</pre>
21+
<div className="div-textarea p-1" dangerouslySetInnerHTML={{ __html: response }} />
1522
) : (
1623
<div dangerouslySetInnerHTML={{ __html: response }} />
1724
)}

src/components/TextResponse/index.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.pre-like-textarea {
1+
.div-textarea {
22
white-space: pre-wrap;
33
word-wrap: break-word;
44
word-break: break-all;

src/data/services/lms/types/blockInfo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export interface TextResponseConfig {
1616
enabled: boolean,
1717
optional: boolean,
1818
editorType: 'text' | 'tinymce',
19-
allowLatexPreviews: boolean,
19+
allowLatexPreview: boolean,
2020
}
2121

2222
export interface FileResponseConfig {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React, { useEffect } from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
const LatexPreview = ({ latexValue }) => {
5+
const latexPreviewEl = React.useRef(null);
6+
7+
useEffect(() => {
8+
MathJax.Hub.Queue(['Typeset', MathJax.Hub, latexPreviewEl.current]);
9+
}, [latexValue]);
10+
11+
return (
12+
<div ref={latexPreviewEl} className='mt-2'>
13+
<div dangerouslySetInnerHTML={{ __html: latexValue }} />
14+
</div>
15+
);
16+
};
17+
18+
LatexPreview.propTypes = {
19+
latexValue: PropTypes.string.isRequired,
20+
};
21+
22+
export default LatexPreview;

src/views/SubmissionView/TextResponseEditor/index.jsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
1-
import React from 'react';
1+
import React, { useCallback } from 'react';
22
import PropTypes from 'prop-types';
33

4+
import { Button } from '@edx/paragon';
5+
import { useIntl } from '@edx/frontend-platform/i18n';
46
import { useSubmissionConfig } from 'hooks/app';
57

68
import TextEditor from './TextEditor';
79
import RichTextEditor from './RichTextEditor';
10+
import LatexPreview from './LaTexPreview';
11+
import messages from './messages';
812

913
const TextResponseEditor = ({ value, onChange }) => {
1014
const { textResponseConfig } = useSubmissionConfig();
1115
const {
1216
optional,
1317
enabled,
1418
editorType,
19+
allowLatexPreview,
1520
} = textResponseConfig || {};
21+
const { formatMessage } = useIntl();
22+
23+
const [latexValue, setLatexValue] = React.useState('');
24+
25+
const previewLaTex = useCallback(() => {
26+
setLatexValue(value);
27+
}, [value]);
1628

1729
if (!enabled) {
1830
return null;
@@ -23,6 +35,14 @@ const TextResponseEditor = ({ value, onChange }) => {
2335
return (
2436
<div className="mt-2">
2537
<EditorComponent {...{ optional, value, onChange }} />
38+
{
39+
allowLatexPreview && (
40+
<div>
41+
<Button className="btn btn-primary btn-sm mt-2" onClick={previewLaTex}>{formatMessage(messages.previewLaTexButton)}</Button>
42+
<LatexPreview latexValue={latexValue} />
43+
</div>
44+
)
45+
}
2646
</div>
2747
);
2848
};

src/views/SubmissionView/TextResponseEditor/messages.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ const messages = defineMessages({
2121
description: 'Label for the optional indicator',
2222
id: 'frontend-app-ora.TextResponse.optional',
2323
},
24+
previewLaTexButton: {
25+
defaultMessage: 'Preview in LaTeX',
26+
description: 'Label for the preview LaTeX button',
27+
id: 'frontend-app-ora.TextResponse.previewLaTexButton',
28+
}
2429
});
2530

2631
export default messages;

0 commit comments

Comments
 (0)