Skip to content

Commit ccbe353

Browse files
Merge branch 'main' into wgu-jesse-stewart/instructor_dashboard_certificates_bulk
2 parents be347d0 + 8532409 commit ccbe353

34 files changed

Lines changed: 2529 additions & 80 deletions

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/certificates/components/GrantExceptionsModal.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,6 @@ describe('GrantExceptionsModal', () => {
112112
// Check for CSV upload elements
113113
expect(screen.getByText(messages.csvFileLabel.defaultMessage)).toBeInTheDocument();
114114
expect(screen.getByText(messages.csvInstructions.defaultMessage)).toBeInTheDocument();
115+
expect(mockOnSubmit).toHaveBeenCalledWith(['user1', 'user2', 'user3'], '');
115116
});
116117
});

src/certificates/components/GrantExceptionsModal.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState } from 'react';
22
import { useIntl } from '@openedx/frontend-base';
33
import { ActionRow, Button, Dropzone, Form, Hyperlink, Icon, ModalDialog, OverlayTrigger, Tab, Tabs, Tooltip } from '@openedx/paragon';
44
import { InfoOutline } from '@openedx/paragon/icons';
5+
import LearnerActionModal from '@src/certificates/components/LearnerActionModal';
56
import messages from '@src/certificates/messages';
67

78
interface GrantExceptionsModalProps {

src/certificates/components/InvalidateCertificateModal.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ describe('InvalidateCertificateModal', () => {
5959
await user.click(saveButton);
6060

6161
expect(mockOnSubmit).toHaveBeenCalledWith(
62-
['user1@example.com'],
62+
['user1@example.com', 'user2@example.com'],
6363
'Certificate invalidated due to violation'
6464
);
6565
});

src/certificates/components/InvalidateCertificateModal.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useState } from 'react';
22
import { useIntl } from '@openedx/frontend-base';
33
import { ActionRow, Button, Form, ModalDialog } from '@openedx/paragon';
4+
import LearnerActionModal from '@src/certificates/components/LearnerActionModal';
45
import messages from '@src/certificates/messages';
56

67
interface InvalidateCertificateModalProps {

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;

0 commit comments

Comments
 (0)