Skip to content

Commit fd48fef

Browse files
feat: edit Text components within content libraries [FC-0062] (#1240)
1 parent 9b61037 commit fd48fef

36 files changed

+724
-233
lines changed

src/CourseAuthoringRoutes.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ const CourseAuthoringRoutes = () => {
8888
/>
8989
<Route
9090
path="editor/:blockType/:blockId?"
91-
element={<PageWrap><EditorContainer courseId={courseId} /></PageWrap>}
91+
element={<PageWrap><EditorContainer learningContextId={courseId} /></PageWrap>}
9292
/>
9393
<Route
9494
path="settings/details"

src/editors/EditorContainer.jsx

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/editors/EditorContainer.test.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jest.mock('react-router', () => ({
1010
}),
1111
}));
1212

13-
const props = { courseId: 'cOuRsEId' };
13+
const props = { learningContextId: 'cOuRsEId' };
1414

1515
describe('Editor Container', () => {
1616
describe('snapshots', () => {

src/editors/EditorContainer.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from 'react';
2+
import { useParams } from 'react-router-dom';
3+
import { getConfig } from '@edx/frontend-platform';
4+
5+
import EditorPage from './EditorPage';
6+
7+
interface Props {
8+
/** Course ID or Library ID */
9+
learningContextId: string;
10+
/** Event handler for when user cancels out of the editor page */
11+
onClose?: () => void;
12+
/** Event handler called after when user saves their changes using an editor */
13+
afterSave?: () => (newData: Record<string, any>) => void;
14+
}
15+
16+
const EditorContainer: React.FC<Props> = ({
17+
learningContextId,
18+
onClose,
19+
afterSave,
20+
}) => {
21+
const { blockType, blockId } = useParams();
22+
if (blockType === undefined || blockId === undefined) {
23+
// istanbul ignore next - This shouldn't be possible; it's just here to satisfy the type checker.
24+
return <div>Error: missing URL parameters</div>;
25+
}
26+
if (!!onClose !== !!afterSave) {
27+
/* istanbul ignore next */
28+
throw new Error('You must specify both onClose and afterSave or neither.');
29+
// These parameters are a bit messy so I'm trying to help make it more
30+
// consistent here. For example, if you specify onClose, then returnFunction
31+
// is only called if the save is successful. But if you leave onClose
32+
// undefined, then returnFunction is called in either case, and with
33+
// different arguments. The underlying EditorPage should be refactored to
34+
// have more clear events like onCancel and onSaveSuccess
35+
}
36+
return (
37+
<div className="editor-page">
38+
<EditorPage
39+
courseId={learningContextId}
40+
blockType={blockType}
41+
blockId={blockId}
42+
studioEndpointUrl={getConfig().STUDIO_BASE_URL}
43+
lmsEndpointUrl={getConfig().LMS_BASE_URL}
44+
onClose={onClose}
45+
returnFunction={afterSave}
46+
/>
47+
</div>
48+
);
49+
};
50+
51+
export default EditorContainer;

src/editors/containers/EditorContainer/__snapshots__/index.test.jsx.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ exports[`EditorContainer component render snapshot: initialized. enable save and
5151
/>
5252
</h2>
5353
<IconButton
54+
alt="Exit the editor"
5455
iconAs="Icon"
5556
onClick={[MockFunction openCancelConfirmModal]}
5657
src={[MockFunction icons.Close]}
@@ -132,6 +133,7 @@ exports[`EditorContainer component render snapshot: not initialized. disable sav
132133
/>
133134
</h2>
134135
<IconButton
136+
alt="Exit the editor"
135137
iconAs="Icon"
136138
onClick={[MockFunction openCancelConfirmModal]}
137139
src={[MockFunction icons.Close]}

src/editors/containers/EditorContainer/index.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const EditorContainer = ({
6363
src={Close}
6464
iconAs={Icon}
6565
onClick={openCancelConfirmModal}
66+
alt={intl.formatMessage(messages.exitButtonAlt)}
6667
/>
6768
</div>
6869
</ModalDialog.Header>

src/editors/containers/EditorContainer/messages.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ const messages = defineMessages({
1212
defaultMessage: 'Are you sure you want to exit the editor? Any unsaved changes will be lost.',
1313
description: 'Description text for modal confirming cancellation',
1414
},
15+
exitButtonAlt: {
16+
id: 'authoring.editorContainer.exitButton.alt',
17+
defaultMessage: 'Exit the editor',
18+
description: 'Alt text for the Exit button',
19+
},
1520
okButtonLabel: {
1621
id: 'authoring.editorContainer.okButton.label',
1722
defaultMessage: 'OK',

src/editors/containers/TextEditor/__snapshots__/index.test.jsx.snap

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,7 @@ exports[`TextEditor snapshots block failed to load, Toast is shown 1`] = `
2424
onClose={[MockFunction hooks.nullMethod]}
2525
show={true}
2626
>
27-
<FormattedMessage
28-
defaultMessage="Error: Could Not Load Text Content"
29-
description="Error Message Dispayed When HTML content fails to Load"
30-
id="authoring.texteditor.load.error"
31-
/>
27+
Error: Could Not Load Text Content
3228
</Toast>
3329
<TinyMceWidget
3430
disabled={false}
@@ -81,11 +77,7 @@ exports[`TextEditor snapshots loaded, raw editor 1`] = `
8177
onClose={[MockFunction hooks.nullMethod]}
8278
show={false}
8379
>
84-
<FormattedMessage
85-
defaultMessage="Error: Could Not Load Text Content"
86-
description="Error Message Dispayed When HTML content fails to Load"
87-
id="authoring.texteditor.load.error"
88-
/>
80+
Error: Could Not Load Text Content
8981
</Toast>
9082
<RawEditor
9183
content={
@@ -132,11 +124,7 @@ exports[`TextEditor snapshots not yet loaded, Spinner appears 1`] = `
132124
onClose={[MockFunction hooks.nullMethod]}
133125
show={false}
134126
>
135-
<FormattedMessage
136-
defaultMessage="Error: Could Not Load Text Content"
137-
description="Error Message Dispayed When HTML content fails to Load"
138-
id="authoring.texteditor.load.error"
139-
/>
127+
Error: Could Not Load Text Content
140128
</Toast>
141129
<div
142130
className="text-center p-6"
@@ -175,11 +163,7 @@ exports[`TextEditor snapshots renders as expected with default behavior 1`] = `
175163
onClose={[MockFunction hooks.nullMethod]}
176164
show={false}
177165
>
178-
<FormattedMessage
179-
defaultMessage="Error: Could Not Load Text Content"
180-
description="Error Message Dispayed When HTML content fails to Load"
181-
id="authoring.texteditor.load.error"
182-
/>
166+
Error: Could Not Load Text Content
183167
</Toast>
184168
<TinyMceWidget
185169
disabled={false}
@@ -232,11 +216,7 @@ exports[`TextEditor snapshots renders static images with relative paths 1`] = `
232216
onClose={[MockFunction hooks.nullMethod]}
233217
show={false}
234218
>
235-
<FormattedMessage
236-
defaultMessage="Error: Could Not Load Text Content"
237-
description="Error Message Dispayed When HTML content fails to Load"
238-
id="authoring.texteditor.load.error"
239-
/>
219+
Error: Could Not Load Text Content
240220
</Toast>
241221
<TinyMceWidget
242222
disabled={false}

src/editors/containers/TextEditor/index.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
Spinner,
77
Toast,
88
} from '@openedx/paragon';
9-
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
9+
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
1010

1111
import { actions, selectors } from '../../data/redux';
1212
import { RequestKeys } from '../../data/constants/requests';
@@ -78,7 +78,7 @@ const TextEditor = ({
7878
>
7979
<div className="editor-body h-75 overflow-auto">
8080
<Toast show={blockFailed} onClose={hooks.nullMethod}>
81-
<FormattedMessage {...messages.couldNotLoadTextContext} />
81+
{ intl.formatMessage(messages.couldNotLoadTextContext) }
8282
</Toast>
8383

8484
{(!blockFinished)
@@ -111,7 +111,7 @@ TextEditor.propTypes = {
111111
initializeEditor: PropTypes.func.isRequired,
112112
showRawEditor: PropTypes.bool.isRequired,
113113
blockFinished: PropTypes.bool,
114-
learningContextId: PropTypes.string.isRequired,
114+
learningContextId: PropTypes.string, // This should be required but is NULL when the store is in initial state :/
115115
images: PropTypes.shape({}).isRequired,
116116
isLibrary: PropTypes.bool.isRequired,
117117
// inject

src/editors/data/redux/app/selectors.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,9 @@ export const returnUrl = createSelector(
4242

4343
export const isInitialized = createSelector(
4444
[
45-
module.simpleSelectors.unitUrl,
4645
module.simpleSelectors.blockValue,
4746
],
48-
(unitUrl, blockValue) => !!(unitUrl && blockValue),
47+
(blockValue) => !!(blockValue),
4948
);
5049

5150
export const displayTitle = createSelector(

0 commit comments

Comments
 (0)