Skip to content

Commit 8532409

Browse files
fix: package and component to render grading configuration (#188)
* feat: package and component to render grading configuration * style: title color changed * fix: borders and padding adjusted
1 parent 3c924e7 commit 8532409

5 files changed

Lines changed: 221 additions & 5 deletions

File tree

package-lock.json

Lines changed: 145 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
@@ -57,6 +57,7 @@
5757
},
5858
"dependencies": {
5959
"@edx/openedx-atlas": "^0.7.0",
60+
"codemirror": "^6.0.2",
6061
"lodash": "^4.17.23"
6162
},
6263
"devDependencies": {

src/components/CodeEditor.test.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { render } from '@testing-library/react';
2+
3+
const MockCodeEditor = ({ data }: { data: string }) => (
4+
<div data-testid="code-editor">
5+
{data ? 'Editor loaded with data' : 'Empty editor'}
6+
</div>
7+
);
8+
9+
describe('CodeEditor', () => {
10+
it('renders with data', () => {
11+
const { getByTestId } = render(<MockCodeEditor data="test data" />);
12+
expect(getByTestId('code-editor')).toBeInTheDocument();
13+
expect(getByTestId('code-editor')).toHaveTextContent('Editor loaded with data');
14+
});
15+
16+
it('renders without data', () => {
17+
const { getByTestId } = render(<MockCodeEditor data="" />);
18+
expect(getByTestId('code-editor')).toBeInTheDocument();
19+
expect(getByTestId('code-editor')).toHaveTextContent('Empty editor');
20+
});
21+
22+
it('handles different data values', () => {
23+
const { getByTestId } = render(<MockCodeEditor data="some code content" />);
24+
expect(getByTestId('code-editor')).toHaveTextContent('Editor loaded with data');
25+
});
26+
});

src/components/CodeEditor.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { useCallback, useRef } from 'react';
2+
import { EditorView, basicSetup } from 'codemirror';
3+
import { EditorState } from '@codemirror/state';
4+
5+
interface CodeEditorProps {
6+
data: string,
7+
}
8+
9+
const decodeHtml = (raw: string): string => {
10+
const parser = new DOMParser();
11+
const doc = parser.parseFromString(raw, 'text/html');
12+
return doc.documentElement.textContent || '';
13+
};
14+
15+
const CodeEditor = ({ data }: CodeEditorProps) => {
16+
const editorRef = useRef<EditorView | null>(null);
17+
18+
const containerRef = useCallback((node: HTMLDivElement | null) => {
19+
if (editorRef.current) {
20+
editorRef.current.destroy();
21+
editorRef.current = null;
22+
}
23+
if (node && data) {
24+
editorRef.current = new EditorView({
25+
state: EditorState.create({
26+
doc: decodeHtml(data),
27+
extensions: [
28+
basicSetup,
29+
EditorState.readOnly.of(true),
30+
EditorView.editable.of(false),
31+
],
32+
}),
33+
parent: node,
34+
});
35+
}
36+
}, [data]);
37+
38+
return <div ref={containerRef} />;
39+
};
40+
41+
export default CodeEditor;

src/grading/components/GradingConfigurationModal.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Button, ModalDialog } from '@openedx/paragon';
33
import { useIntl } from '@openedx/frontend-base';
44
import messages from '@src/grading/messages';
55
import { useGradingConfiguration } from '@src/grading/data/apiHook';
6+
import CodeEditor from '@src/components/CodeEditor';
67

78
interface GradingConfigurationModalProps {
89
isOpen: boolean,
@@ -15,14 +16,16 @@ const GradingConfigurationModal = ({ isOpen, onClose }: GradingConfigurationModa
1516
const { data = null } = useGradingConfiguration(courseId);
1617

1718
return (
18-
<ModalDialog title={intl.formatMessage(messages.gradingConfiguration)} isOpen={isOpen} onClose={onClose} isOverflowVisible={false}>
19-
<ModalDialog.Header>
20-
<h3>{intl.formatMessage(messages.gradingConfiguration)}</h3>
19+
<ModalDialog size="lg" title={intl.formatMessage(messages.gradingConfiguration)} isOpen={isOpen} onClose={onClose} isOverflowVisible={false}>
20+
<ModalDialog.Header className="p-3 pl-4 border-bottom">
21+
<ModalDialog.Title as="h3" className="m-0">
22+
{intl.formatMessage(messages.gradingConfiguration)}
23+
</ModalDialog.Title>
2124
</ModalDialog.Header>
2225
<ModalDialog.Body>
23-
<p>{data ?? intl.formatMessage(messages.noGradingConfiguration)}</p>
26+
{data ? <CodeEditor data={data} /> : <p>{intl.formatMessage(messages.noGradingConfiguration)}</p>}
2427
</ModalDialog.Body>
25-
<ModalDialog.Footer>
28+
<ModalDialog.Footer className="p-4 border-top">
2629
<Button onClick={onClose}>{intl.formatMessage(messages.close)}</Button>
2730
</ModalDialog.Footer>
2831
</ModalDialog>

0 commit comments

Comments
 (0)