fix: tinymce render outside of editors (#1254)
This commit is contained in:
@@ -25,6 +25,9 @@ const AnswerOption = ({
|
||||
intl,
|
||||
// redux
|
||||
problemType,
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const removeAnswer = hooks.removeAnswer({ answer, dispatch });
|
||||
@@ -47,6 +50,11 @@ const AnswerOption = ({
|
||||
setContent={setAnswerTitle}
|
||||
placeholder={intl.formatMessage(messages.answerTextboxPlaceholder)}
|
||||
id={`answer-${answer.id}`}
|
||||
{...{
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -106,6 +114,11 @@ const AnswerOption = ({
|
||||
setSelectedFeedback={setSelectedFeedback}
|
||||
setUnselectedFeedback={setUnselectedFeedback}
|
||||
intl={intl}
|
||||
{...{
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}}
|
||||
/>
|
||||
</Collapsible.Body>
|
||||
</div>
|
||||
@@ -135,10 +148,16 @@ AnswerOption.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
// redux
|
||||
problemType: PropTypes.string.isRequired,
|
||||
images: PropTypes.shape({}).isRequired,
|
||||
learningContextId: PropTypes.string.isRequired,
|
||||
isLibrary: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
problemType: selectors.problem.problemType(state),
|
||||
images: selectors.app.images(state),
|
||||
isLibrary: selectors.app.isLibrary(state),
|
||||
learningContextId: selectors.app.learningContextId(state),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {};
|
||||
|
||||
@@ -10,12 +10,18 @@ jest.mock('../../../../../data/redux', () => ({
|
||||
default: jest.fn(),
|
||||
selectors: {
|
||||
problem: {
|
||||
answers: jest.fn(state => ({ answers: state })),
|
||||
problemType: jest.fn(state => ({ problemType: state })),
|
||||
},
|
||||
app: {
|
||||
images: jest.fn(state => ({ images: state })),
|
||||
isLibrary: jest.fn(state => ({ isLibrary: state })),
|
||||
learningContextId: jest.fn(state => ({ learningContextId: state })),
|
||||
},
|
||||
},
|
||||
thunkActions: {
|
||||
video: jest.fn(),
|
||||
video: {
|
||||
importTranscripts: jest.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -49,6 +55,9 @@ describe('AnswerOption', () => {
|
||||
intl: { formatMessage },
|
||||
// redux
|
||||
problemType: 'multiplechoiceresponse',
|
||||
images: {},
|
||||
isLibrary: false,
|
||||
learningContextId: 'course+org+run',
|
||||
};
|
||||
describe('render', () => {
|
||||
test('snapshot: renders correct option with feedback', () => {
|
||||
@@ -72,5 +81,20 @@ describe('AnswerOption', () => {
|
||||
mapStateToProps(testState).problemType,
|
||||
).toEqual(selectors.problem.problemType(testState));
|
||||
});
|
||||
test('images from app.images', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).images,
|
||||
).toEqual(selectors.app.images(testState));
|
||||
});
|
||||
test('learningContextId from app.learningContextId', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).learningContextId,
|
||||
).toEqual(selectors.app.learningContextId(testState));
|
||||
});
|
||||
test('isLibrary from app.isLibrary', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).isLibrary,
|
||||
).toEqual(selectors.app.isLibrary(testState));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,6 +30,9 @@ exports[`AnswerOption render snapshot: renders correct option with feedback 1`]
|
||||
error={false}
|
||||
errorMessage={null}
|
||||
id="answer-A"
|
||||
images={{}}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
placeholder="Enter an answer"
|
||||
setContent={[Function]}
|
||||
value="Answer 1"
|
||||
@@ -44,11 +47,14 @@ exports[`AnswerOption render snapshot: renders correct option with feedback 1`]
|
||||
"title": "Answer 1",
|
||||
}
|
||||
}
|
||||
images={{}}
|
||||
intl={
|
||||
{
|
||||
"formatMessage": [Function],
|
||||
}
|
||||
}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
problemType="multiplechoiceresponse"
|
||||
setSelectedFeedback={[Function]}
|
||||
setUnselectedFeedback={[Function]}
|
||||
@@ -121,11 +127,14 @@ exports[`AnswerOption render snapshot: renders correct option with numeric input
|
||||
"title": "Answer 1",
|
||||
}
|
||||
}
|
||||
images={{}}
|
||||
intl={
|
||||
{
|
||||
"formatMessage": [Function],
|
||||
}
|
||||
}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
problemType="numericalresponse"
|
||||
setSelectedFeedback={[Function]}
|
||||
setUnselectedFeedback={[Function]}
|
||||
@@ -213,11 +222,14 @@ exports[`AnswerOption render snapshot: renders correct option with numeric input
|
||||
"unselectedFeedback": "unselected feedback",
|
||||
}
|
||||
}
|
||||
images={{}}
|
||||
intl={
|
||||
{
|
||||
"formatMessage": [Function],
|
||||
}
|
||||
}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
problemType="numericalresponse"
|
||||
setSelectedFeedback={[Function]}
|
||||
setUnselectedFeedback={[Function]}
|
||||
@@ -276,6 +288,9 @@ exports[`AnswerOption render snapshot: renders correct option with selected unse
|
||||
error={false}
|
||||
errorMessage={null}
|
||||
id="answer-A"
|
||||
images={{}}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
placeholder="Enter an answer"
|
||||
setContent={[Function]}
|
||||
value="Answer 1"
|
||||
@@ -291,11 +306,14 @@ exports[`AnswerOption render snapshot: renders correct option with selected unse
|
||||
"unselectedFeedback": "unselected feedback",
|
||||
}
|
||||
}
|
||||
images={{}}
|
||||
intl={
|
||||
{
|
||||
"formatMessage": [Function],
|
||||
}
|
||||
}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
problemType="choiceresponse"
|
||||
setSelectedFeedback={[Function]}
|
||||
setUnselectedFeedback={[Function]}
|
||||
|
||||
@@ -12,12 +12,18 @@ export const FeedbackBox = ({
|
||||
problemType,
|
||||
setSelectedFeedback,
|
||||
setUnselectedFeedback,
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
// injected
|
||||
intl,
|
||||
}) => {
|
||||
const props = {
|
||||
answer,
|
||||
intl,
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
};
|
||||
|
||||
return ((problemType === ProblemTypeKeys.MULTISELECT) ? (
|
||||
@@ -61,6 +67,9 @@ FeedbackBox.propTypes = {
|
||||
setAnswer: PropTypes.func.isRequired,
|
||||
setSelectedFeedback: PropTypes.func.isRequired,
|
||||
setUnselectedFeedback: PropTypes.func.isRequired,
|
||||
images: PropTypes.shape({}).isRequired,
|
||||
learningContextId: PropTypes.string.isRequired,
|
||||
isLibrary: PropTypes.bool.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ const answerWithFeedback = {
|
||||
selectedFeedback: 'some feedback',
|
||||
unselectedFeedback: 'unselectedFeedback',
|
||||
problemType: 'sOMepRObleM',
|
||||
images: {},
|
||||
isLibrary: false,
|
||||
learningContextId: 'course+org+run',
|
||||
};
|
||||
|
||||
const props = {
|
||||
|
||||
@@ -15,6 +15,9 @@ const FeedbackControl = ({
|
||||
answer,
|
||||
intl,
|
||||
type,
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}) => (
|
||||
<Form.Group>
|
||||
<Form.Label className="mb-3">
|
||||
@@ -31,6 +34,11 @@ const FeedbackControl = ({
|
||||
value={feedback}
|
||||
setContent={onChange}
|
||||
placeholder={intl.formatMessage(messages.feedbackPlaceholder)}
|
||||
{...{
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}}
|
||||
/>
|
||||
</Form.Group>
|
||||
);
|
||||
@@ -41,6 +49,9 @@ FeedbackControl.propTypes = {
|
||||
labelMessageBoldUnderline: PropTypes.string.isRequired,
|
||||
answer: answerOptionProps.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
images: PropTypes.shape({}).isRequired,
|
||||
learningContextId: PropTypes.string.isRequired,
|
||||
isLibrary: PropTypes.bool.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ const props = {
|
||||
onChange: jest.fn(),
|
||||
labelMessage: 'msg',
|
||||
labelMessageBoldUnderline: 'msg',
|
||||
images: {},
|
||||
isLibrary: false,
|
||||
learningContextId: 'course+org+run',
|
||||
};
|
||||
|
||||
describe('FeedbackControl component', () => {
|
||||
|
||||
@@ -9,6 +9,9 @@ exports[`FeedbackBox component renders as expected with a multi select problem 1
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"images": {},
|
||||
"isLibrary": false,
|
||||
"learningContextId": "course+org+run",
|
||||
"problemType": "sOMepRObleM",
|
||||
"selectedFeedback": "some feedback",
|
||||
"title": "Answer 1",
|
||||
@@ -39,6 +42,9 @@ exports[`FeedbackBox component renders as expected with a multi select problem 1
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"images": {},
|
||||
"isLibrary": false,
|
||||
"learningContextId": "course+org+run",
|
||||
"problemType": "sOMepRObleM",
|
||||
"selectedFeedback": "some feedback",
|
||||
"title": "Answer 1",
|
||||
@@ -76,6 +82,9 @@ exports[`FeedbackBox component renders as expected with a numeric input problem
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"images": {},
|
||||
"isLibrary": false,
|
||||
"learningContextId": "course+org+run",
|
||||
"problemType": "sOMepRObleM",
|
||||
"selectedFeedback": "some feedback",
|
||||
"title": "Answer 1",
|
||||
@@ -113,6 +122,9 @@ exports[`FeedbackBox component renders as expected with default props 1`] = `
|
||||
{
|
||||
"correct": true,
|
||||
"id": "A",
|
||||
"images": {},
|
||||
"isLibrary": false,
|
||||
"learningContextId": "course+org+run",
|
||||
"problemType": "sOMepRObleM",
|
||||
"selectedFeedback": "some feedback",
|
||||
"title": "Answer 1",
|
||||
|
||||
@@ -29,6 +29,9 @@ exports[`FeedbackControl component renders 1`] = `
|
||||
error={false}
|
||||
errorMessage={null}
|
||||
id="undefinedFeedback-A"
|
||||
images={{}}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
placeholder={null}
|
||||
setContent={[MockFunction]}
|
||||
value="feedback"
|
||||
|
||||
@@ -42,22 +42,22 @@ export const setAnswerTitle = ({
|
||||
dispatch(actions.problem.updateAnswer({ id: answer.id, hasSingleAnswer, title }));
|
||||
};
|
||||
|
||||
export const setSelectedFeedback = ({ answer, hasSingleAnswer, dispatch }) => (e) => {
|
||||
if (e.target) {
|
||||
export const setSelectedFeedback = ({ answer, hasSingleAnswer, dispatch }) => (value) => {
|
||||
if (value) {
|
||||
dispatch(actions.problem.updateAnswer({
|
||||
id: answer.id,
|
||||
hasSingleAnswer,
|
||||
selectedFeedback: e.target.value,
|
||||
selectedFeedback: value,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
export const setUnselectedFeedback = ({ answer, hasSingleAnswer, dispatch }) => (e) => {
|
||||
if (e.target) {
|
||||
export const setUnselectedFeedback = ({ answer, hasSingleAnswer, dispatch }) => (value) => {
|
||||
if (value) {
|
||||
dispatch(actions.problem.updateAnswer({
|
||||
id: answer.id,
|
||||
hasSingleAnswer,
|
||||
unselectedFeedback: e.target.value,
|
||||
unselectedFeedback: value,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -130,12 +130,12 @@ describe('Answer Options Hooks', () => {
|
||||
const answer = { id: 'A' };
|
||||
const hasSingleAnswer = false;
|
||||
const dispatch = useDispatch();
|
||||
const e = { target: { value: 'string' } };
|
||||
module.setSelectedFeedback({ answer, hasSingleAnswer, dispatch })(e);
|
||||
const value = 'string';
|
||||
module.setSelectedFeedback({ answer, hasSingleAnswer, dispatch })(value);
|
||||
expect(dispatch).toHaveBeenCalledWith(actions.problem.updateAnswer({
|
||||
id: answer.id,
|
||||
hasSingleAnswer,
|
||||
selectedFeedback: e.target.value,
|
||||
selectedFeedback: value,
|
||||
}));
|
||||
});
|
||||
});
|
||||
@@ -144,12 +144,12 @@ describe('Answer Options Hooks', () => {
|
||||
const answer = { id: 'A' };
|
||||
const hasSingleAnswer = false;
|
||||
const dispatch = useDispatch();
|
||||
const e = { target: { value: 'string' } };
|
||||
module.setUnselectedFeedback({ answer, hasSingleAnswer, dispatch })(e);
|
||||
const value = 'string';
|
||||
module.setUnselectedFeedback({ answer, hasSingleAnswer, dispatch })(value);
|
||||
expect(dispatch).toHaveBeenCalledWith(actions.problem.updateAnswer({
|
||||
id: answer.id,
|
||||
hasSingleAnswer,
|
||||
unselectedFeedback: e.target.value,
|
||||
unselectedFeedback: value,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,13 +22,21 @@ exports[`SolutionWidget render snapshot: renders correct default 1`] = `
|
||||
id="authoring.problemEditor.solutionwidget.solutionDescriptionText"
|
||||
/>
|
||||
</div>
|
||||
<[object Object]
|
||||
<TinyMceWidget
|
||||
disabled={false}
|
||||
editorContentHtml="This is my solution"
|
||||
editorRef={null}
|
||||
editorType="solution"
|
||||
id="solution"
|
||||
images={{}}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
lmsEndpointUrl=""
|
||||
minHeight={150}
|
||||
onChange={[Function]}
|
||||
placeholder="Enter your explanation"
|
||||
setEditorRef={[MockFunction prepareEditorRef.setEditorRef]}
|
||||
studioEndpointUrl=""
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -12,6 +12,8 @@ const ExplanationWidget = ({
|
||||
// redux
|
||||
settings,
|
||||
learningContextId,
|
||||
images,
|
||||
isLibrary,
|
||||
// injected
|
||||
intl,
|
||||
}) => {
|
||||
@@ -39,6 +41,11 @@ const ExplanationWidget = ({
|
||||
setEditorRef={setEditorRef}
|
||||
minHeight={150}
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
{...{
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -49,12 +56,16 @@ ExplanationWidget.propTypes = {
|
||||
// eslint-disable-next-line
|
||||
settings: PropTypes.any.isRequired,
|
||||
learningContextId: PropTypes.string.isRequired,
|
||||
images: PropTypes.shape({}).isRequired,
|
||||
isLibrary: PropTypes.bool.isRequired,
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
export const mapStateToProps = (state) => ({
|
||||
settings: selectors.problem.settings(state),
|
||||
learningContextId: selectors.app.learningContextId(state),
|
||||
images: selectors.app.images(state),
|
||||
isLibrary: selectors.app.isLibrary(state),
|
||||
});
|
||||
|
||||
export const ExplanationWidgetInternal = ExplanationWidget; // For testing only
|
||||
|
||||
@@ -14,6 +14,8 @@ jest.mock('../../../../../data/redux', () => ({
|
||||
},
|
||||
app: {
|
||||
learningContextId: jest.fn(state => ({ learningContextId: state })),
|
||||
images: jest.fn(state => ({ images: state })),
|
||||
isLibrary: jest.fn(state => ({ isLibrary: state })),
|
||||
},
|
||||
},
|
||||
thunkActions: {
|
||||
@@ -35,6 +37,8 @@ describe('SolutionWidget', () => {
|
||||
const props = {
|
||||
settings: { solutionExplanation: 'This is my solution' },
|
||||
learningContextId: 'course+org+run',
|
||||
images: {},
|
||||
isLibrary: false,
|
||||
// injected
|
||||
intl: { formatMessage },
|
||||
};
|
||||
@@ -51,5 +55,15 @@ describe('SolutionWidget', () => {
|
||||
test('learningContextId from app.learningContextId', () => {
|
||||
expect(mapStateToProps(testState).learningContextId).toEqual(selectors.app.learningContextId(testState));
|
||||
});
|
||||
test('images from app.images', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).images,
|
||||
).toEqual(selectors.app.images(testState));
|
||||
});
|
||||
test('isLibrary from app.isLibrary', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).isLibrary,
|
||||
).toEqual(selectors.app.isLibrary(testState));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,13 +13,21 @@ exports[`QuestionWidget render snapshot: renders correct default 1`] = `
|
||||
id="authoring.questionwidget.question.questionWidgetTitle"
|
||||
/>
|
||||
</div>
|
||||
<[object Object]
|
||||
<TinyMceWidget
|
||||
disabled={false}
|
||||
editorContentHtml="This is my question"
|
||||
editorRef={null}
|
||||
editorType="question"
|
||||
id="question"
|
||||
images={{}}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
lmsEndpointUrl=""
|
||||
minHeight={150}
|
||||
onChange={[Function]}
|
||||
placeholder="Enter your question"
|
||||
setEditorRef={[MockFunction prepareEditorRef.setEditorRef]}
|
||||
studioEndpointUrl=""
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -12,6 +12,8 @@ const QuestionWidget = ({
|
||||
// redux
|
||||
question,
|
||||
learningContextId,
|
||||
images,
|
||||
isLibrary,
|
||||
// injected
|
||||
intl,
|
||||
}) => {
|
||||
@@ -36,6 +38,11 @@ const QuestionWidget = ({
|
||||
setEditorRef={setEditorRef}
|
||||
minHeight={150}
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
{...{
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -45,12 +52,16 @@ QuestionWidget.propTypes = {
|
||||
// redux
|
||||
question: PropTypes.string.isRequired,
|
||||
learningContextId: PropTypes.string.isRequired,
|
||||
images: PropTypes.shape({}).isRequired,
|
||||
isLibrary: PropTypes.bool.isRequired,
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
export const mapStateToProps = (state) => ({
|
||||
question: selectors.problem.question(state),
|
||||
learningContextId: selectors.app.learningContextId(state),
|
||||
images: selectors.app.images(state),
|
||||
isLibrary: selectors.app.isLibrary(state),
|
||||
});
|
||||
|
||||
export const QuestionWidgetInternal = QuestionWidget; // For testing only
|
||||
|
||||
@@ -16,6 +16,8 @@ jest.mock('../../../../../data/redux', () => ({
|
||||
selectors: {
|
||||
app: {
|
||||
learningContextId: jest.fn(state => ({ learningContextId: state })),
|
||||
images: jest.fn(state => ({ images: state })),
|
||||
isLibrary: jest.fn(state => ({ isLibrary: state })),
|
||||
},
|
||||
problem: {
|
||||
question: jest.fn(state => ({ question: state })),
|
||||
@@ -41,6 +43,8 @@ describe('QuestionWidget', () => {
|
||||
question: 'This is my question',
|
||||
updateQuestion: jest.fn(),
|
||||
learningContextId: 'course+org+run',
|
||||
images: {},
|
||||
isLibrary: false,
|
||||
// injected
|
||||
intl: { formatMessage },
|
||||
};
|
||||
@@ -57,5 +61,15 @@ describe('QuestionWidget', () => {
|
||||
test('learningContextId from app.learningContextId', () => {
|
||||
expect(mapStateToProps(testState).learningContextId).toEqual(selectors.app.learningContextId(testState));
|
||||
});
|
||||
test('images from app.images', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).images,
|
||||
).toEqual(selectors.app.images(testState));
|
||||
});
|
||||
test('isLibrary from app.isLibrary', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).isLibrary,
|
||||
).toEqual(selectors.app.isLibrary(testState));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,6 +22,9 @@ exports[`SettingsWidget snapshot snapshot: renders Settings widget for Advanced
|
||||
className="mt-3"
|
||||
>
|
||||
<HintsCard
|
||||
images={{}}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
problemType="stringresponse"
|
||||
/>
|
||||
</div>
|
||||
@@ -106,6 +109,9 @@ exports[`SettingsWidget snapshot snapshot: renders Settings widget page 1`] = `
|
||||
className="mt-3"
|
||||
>
|
||||
<HintsCard
|
||||
images={{}}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
problemType="stringresponse"
|
||||
/>
|
||||
</div>
|
||||
@@ -190,6 +196,9 @@ exports[`SettingsWidget snapshot snapshot: renders Settings widget page advanced
|
||||
className="mt-3"
|
||||
>
|
||||
<HintsCard
|
||||
images={{}}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
problemType="stringresponse"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -36,6 +36,9 @@ const SettingsWidget = ({
|
||||
updateField,
|
||||
updateAnswer,
|
||||
defaultSettings,
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}) => {
|
||||
const { isAdvancedCardsVisible, showAdvancedCards } = showAdvancedSettingsCards();
|
||||
|
||||
@@ -85,7 +88,16 @@ const SettingsWidget = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
<HintsCard problemType={problemType} hints={settings.hints} updateSettings={updateSettings} />
|
||||
<HintsCard
|
||||
problemType={problemType}
|
||||
hints={settings.hints}
|
||||
updateSettings={updateSettings}
|
||||
{...{
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{feedbackCard()}
|
||||
<div>
|
||||
@@ -172,6 +184,9 @@ SettingsWidget.propTypes = {
|
||||
showResetButton: PropTypes.bool,
|
||||
rerandomize: PropTypes.string,
|
||||
}).isRequired,
|
||||
images: PropTypes.shape({}).isRequired,
|
||||
learningContextId: PropTypes.string.isRequired,
|
||||
isLibrary: PropTypes.bool.isRequired,
|
||||
// eslint-disable-next-line
|
||||
settings: PropTypes.any.isRequired,
|
||||
};
|
||||
@@ -183,6 +198,9 @@ const mapStateToProps = (state) => ({
|
||||
blockTitle: selectors.app.blockTitle(state),
|
||||
correctAnswerCount: selectors.problem.correctAnswerCount(state),
|
||||
defaultSettings: selectors.problem.defaultSettings(state),
|
||||
images: selectors.app.images(state),
|
||||
isLibrary: selectors.app.isLibrary(state),
|
||||
learningContextId: selectors.app.learningContextId(state),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
|
||||
@@ -30,6 +30,9 @@ describe('SettingsWidget', () => {
|
||||
showanswer: 'finished',
|
||||
showResetButton: false,
|
||||
},
|
||||
images: {},
|
||||
isLibrary: false,
|
||||
learningContextId: 'course+org+run',
|
||||
};
|
||||
|
||||
describe('behavior', () => {
|
||||
|
||||
@@ -16,6 +16,9 @@ const HintRow = ({
|
||||
handleChange,
|
||||
handleDelete,
|
||||
id,
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
// injected
|
||||
intl,
|
||||
}) => (
|
||||
@@ -26,6 +29,11 @@ const HintRow = ({
|
||||
setContent={handleChange}
|
||||
placeholder={intl.formatMessage(messages.hintInputLabel)}
|
||||
id={`hint-${id}`}
|
||||
{...{
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}}
|
||||
/>
|
||||
</Container>
|
||||
<div className="d-flex flex-row flex-nowrap">
|
||||
@@ -45,6 +53,9 @@ HintRow.propTypes = {
|
||||
handleChange: PropTypes.func.isRequired,
|
||||
handleDelete: PropTypes.func.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
images: PropTypes.shape({}).isRequired,
|
||||
learningContextId: PropTypes.string.isRequired,
|
||||
isLibrary: PropTypes.bool.isRequired,
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
@@ -11,6 +11,9 @@ describe('HintRow', () => {
|
||||
handleDelete: jest.fn(),
|
||||
id: '0',
|
||||
intl: { formatMessage },
|
||||
images: {},
|
||||
isLibrary: false,
|
||||
learningContextId: 'course+org+run',
|
||||
};
|
||||
|
||||
describe('snapshot', () => {
|
||||
|
||||
@@ -12,6 +12,9 @@ const HintsCard = ({
|
||||
hints,
|
||||
problemType,
|
||||
updateSettings,
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
// inject
|
||||
intl,
|
||||
}) => {
|
||||
@@ -31,7 +34,12 @@ const HintsCard = ({
|
||||
key={hint.id}
|
||||
id={hint.id}
|
||||
value={hint.value}
|
||||
{...hintsRowHooks(hint.id, hints, updateSettings)}
|
||||
{...{
|
||||
...hintsRowHooks(hint.id, hints, updateSettings),
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<Button
|
||||
@@ -54,6 +62,9 @@ HintsCard.propTypes = {
|
||||
})).isRequired,
|
||||
problemType: PropTypes.string.isRequired,
|
||||
updateSettings: PropTypes.func.isRequired,
|
||||
images: PropTypes.shape({}).isRequired,
|
||||
learningContextId: PropTypes.string.isRequired,
|
||||
isLibrary: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export const HintsCardInternal = HintsCard; // For testing only
|
||||
|
||||
@@ -26,6 +26,9 @@ describe('HintsCard', () => {
|
||||
const hintsRowHooksProps = {
|
||||
handleChange: jest.fn().mockName('hintsRowHooks.handleChange'),
|
||||
handleDelete: jest.fn().mockName('hintsRowHooks.handleDelete'),
|
||||
images: {},
|
||||
isLibrary: false,
|
||||
learningContextId: 'course+org+run',
|
||||
};
|
||||
hintsRowHooks.mockReturnValue(hintsRowHooksProps);
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ exports[`HintRow snapshot snapshot: renders hints row 1`] = `
|
||||
error={false}
|
||||
errorMessage={null}
|
||||
id="hint-0"
|
||||
images={{}}
|
||||
isLibrary={false}
|
||||
learningContextId="course+org+run"
|
||||
placeholder="Hint"
|
||||
setContent={[MockFunction]}
|
||||
value="hint_1"
|
||||
|
||||
@@ -30,7 +30,8 @@ exports[`TextEditor snapshots block failed to load, Toast is shown 1`] = `
|
||||
id="authoring.texteditor.load.error"
|
||||
/>
|
||||
</Toast>
|
||||
<[object Object]
|
||||
<TinyMceWidget
|
||||
disabled={false}
|
||||
editorContentHtml="eDiTablE Text"
|
||||
editorRef={
|
||||
{
|
||||
@@ -41,9 +42,16 @@ exports[`TextEditor snapshots block failed to load, Toast is shown 1`] = `
|
||||
}
|
||||
editorType="text"
|
||||
height="100%"
|
||||
id={null}
|
||||
images={{}}
|
||||
initializeEditor={[MockFunction args.intializeEditor]}
|
||||
isLibrary={null}
|
||||
learningContextId="course+org+run"
|
||||
lmsEndpointUrl=""
|
||||
minHeight={500}
|
||||
onChange={[Function]}
|
||||
setEditorRef={[MockFunction hooks.prepareEditorRef.setEditorRef]}
|
||||
studioEndpointUrl=""
|
||||
/>
|
||||
</div>
|
||||
</EditorContainer>
|
||||
@@ -173,7 +181,8 @@ exports[`TextEditor snapshots renders as expected with default behavior 1`] = `
|
||||
id="authoring.texteditor.load.error"
|
||||
/>
|
||||
</Toast>
|
||||
<[object Object]
|
||||
<TinyMceWidget
|
||||
disabled={false}
|
||||
editorContentHtml="eDiTablE Text"
|
||||
editorRef={
|
||||
{
|
||||
@@ -184,9 +193,16 @@ exports[`TextEditor snapshots renders as expected with default behavior 1`] = `
|
||||
}
|
||||
editorType="text"
|
||||
height="100%"
|
||||
id={null}
|
||||
images={{}}
|
||||
initializeEditor={[MockFunction args.intializeEditor]}
|
||||
isLibrary={null}
|
||||
learningContextId="course+org+run"
|
||||
lmsEndpointUrl=""
|
||||
minHeight={500}
|
||||
onChange={[Function]}
|
||||
setEditorRef={[MockFunction hooks.prepareEditorRef.setEditorRef]}
|
||||
studioEndpointUrl=""
|
||||
/>
|
||||
</div>
|
||||
</EditorContainer>
|
||||
@@ -222,7 +238,8 @@ exports[`TextEditor snapshots renders static images with relative paths 1`] = `
|
||||
id="authoring.texteditor.load.error"
|
||||
/>
|
||||
</Toast>
|
||||
<[object Object]
|
||||
<TinyMceWidget
|
||||
disabled={false}
|
||||
editorContentHtml="eDiTablE Text with <img src="/asset+org+run+type@asset+block@img.jpg" />"
|
||||
editorRef={
|
||||
{
|
||||
@@ -233,9 +250,16 @@ exports[`TextEditor snapshots renders static images with relative paths 1`] = `
|
||||
}
|
||||
editorType="text"
|
||||
height="100%"
|
||||
id={null}
|
||||
images={{}}
|
||||
initializeEditor={[MockFunction args.intializeEditor]}
|
||||
isLibrary={null}
|
||||
learningContextId="course+org+run"
|
||||
lmsEndpointUrl=""
|
||||
minHeight={500}
|
||||
onChange={[Function]}
|
||||
setEditorRef={[MockFunction hooks.prepareEditorRef.setEditorRef]}
|
||||
studioEndpointUrl=""
|
||||
/>
|
||||
</div>
|
||||
</EditorContainer>
|
||||
|
||||
@@ -28,6 +28,8 @@ const TextEditor = ({
|
||||
initializeEditor,
|
||||
blockFinished,
|
||||
learningContextId,
|
||||
images,
|
||||
isLibrary,
|
||||
// inject
|
||||
intl,
|
||||
}) => {
|
||||
@@ -59,6 +61,11 @@ const TextEditor = ({
|
||||
minHeight={500}
|
||||
height="100%"
|
||||
initializeEditor={initializeEditor}
|
||||
{...{
|
||||
images,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -105,6 +112,8 @@ TextEditor.propTypes = {
|
||||
showRawEditor: PropTypes.bool.isRequired,
|
||||
blockFinished: PropTypes.bool,
|
||||
learningContextId: PropTypes.string.isRequired,
|
||||
images: PropTypes.shape({}).isRequired,
|
||||
isLibrary: PropTypes.bool.isRequired,
|
||||
// inject
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
@@ -115,6 +124,8 @@ export const mapStateToProps = (state) => ({
|
||||
showRawEditor: selectors.app.showRawEditor(state),
|
||||
blockFinished: selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchBlock }),
|
||||
learningContextId: selectors.app.learningContextId(state),
|
||||
images: selectors.app.images(state),
|
||||
isLibrary: selectors.app.isLibrary(state),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
|
||||
@@ -57,6 +57,7 @@ jest.mock('../../data/redux', () => ({
|
||||
lmsEndpointUrl: jest.fn(state => ({ lmsEndpointUrl: state })),
|
||||
studioEndpointUrl: jest.fn(state => ({ studioEndpointUrl: state })),
|
||||
showRawEditor: jest.fn(state => ({ showRawEditor: state })),
|
||||
images: jest.fn(state => ({ images: state })),
|
||||
isLibrary: jest.fn(state => ({ isLibrary: state })),
|
||||
learningContextId: jest.fn(state => ({ learningContextId: state })),
|
||||
},
|
||||
@@ -82,6 +83,7 @@ describe('TextEditor', () => {
|
||||
showRawEditor: false,
|
||||
blockFinished: true,
|
||||
learningContextId: 'course+org+run',
|
||||
images: {},
|
||||
// inject
|
||||
intl: { formatMessage },
|
||||
};
|
||||
@@ -129,6 +131,11 @@ describe('TextEditor', () => {
|
||||
mapStateToProps(testState).learningContextId,
|
||||
).toEqual(selectors.app.learningContextId(testState));
|
||||
});
|
||||
test('images from app.images', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).images,
|
||||
).toEqual(selectors.app.images(testState));
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapDispatchToProps', () => {
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TinyMceWidget snapshots ImageUploadModal is not rendered 1`] = `
|
||||
<Provider
|
||||
store={
|
||||
{
|
||||
"dispatch": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
Symbol(Symbol.observable): [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<Fragment>
|
||||
<SourceCodeModal
|
||||
close={[MockFunction modal.closeModal]}
|
||||
editorRef={
|
||||
@@ -61,21 +51,11 @@ exports[`TinyMceWidget snapshots ImageUploadModal is not rendered 1`] = `
|
||||
id="sOMeiD"
|
||||
onEditorChange={[Function]}
|
||||
/>
|
||||
</Provider>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
exports[`TinyMceWidget snapshots SourcecodeModal is not rendered 1`] = `
|
||||
<Provider
|
||||
store={
|
||||
{
|
||||
"dispatch": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
Symbol(Symbol.observable): [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<Fragment>
|
||||
<ImageUploadModal
|
||||
clearSelection={[MockFunction hooks.selectedImage.clearSelection]}
|
||||
close={[MockFunction modal.closeModal]}
|
||||
@@ -97,7 +77,7 @@ exports[`TinyMceWidget snapshots SourcecodeModal is not rendered 1`] = `
|
||||
}
|
||||
}
|
||||
isOpen={false}
|
||||
lmsEndpointUrl="sOmEvaLue.cOm"
|
||||
lmsEndpointUrl="http://localhost:18000"
|
||||
selection="hooks.selectedImage.selection"
|
||||
setSelection={[MockFunction hooks.selectedImage.setSelection]}
|
||||
/>
|
||||
@@ -139,21 +119,11 @@ exports[`TinyMceWidget snapshots SourcecodeModal is not rendered 1`] = `
|
||||
id="sOMeiD"
|
||||
onEditorChange={[Function]}
|
||||
/>
|
||||
</Provider>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
exports[`TinyMceWidget snapshots renders as expected with default behavior 1`] = `
|
||||
<Provider
|
||||
store={
|
||||
{
|
||||
"dispatch": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
Symbol(Symbol.observable): [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<Fragment>
|
||||
<ImageUploadModal
|
||||
clearSelection={[MockFunction hooks.selectedImage.clearSelection]}
|
||||
close={[MockFunction modal.closeModal]}
|
||||
@@ -175,7 +145,7 @@ exports[`TinyMceWidget snapshots renders as expected with default behavior 1`] =
|
||||
}
|
||||
}
|
||||
isOpen={false}
|
||||
lmsEndpointUrl="sOmEvaLue.cOm"
|
||||
lmsEndpointUrl="http://localhost:18000"
|
||||
selection="hooks.selectedImage.selection"
|
||||
setSelection={[MockFunction hooks.selectedImage.setSelection]}
|
||||
/>
|
||||
@@ -228,5 +198,5 @@ exports[`TinyMceWidget snapshots renders as expected with default behavior 1`] =
|
||||
id="sOMeiD"
|
||||
onEditorChange={[Function]}
|
||||
/>
|
||||
</Provider>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { getLocale, isRtl } from '@edx/frontend-platform/i18n';
|
||||
import { a11ycheckerCss } from 'frontend-components-tinymce-advanced-plugins';
|
||||
import { isEmpty } from 'lodash';
|
||||
@@ -234,8 +235,6 @@ export const editorConfig = ({
|
||||
setEditorRef,
|
||||
editorContentHtml,
|
||||
images,
|
||||
lmsEndpointUrl,
|
||||
studioEndpointUrl,
|
||||
isLibrary,
|
||||
placeholder,
|
||||
initializeEditor,
|
||||
@@ -247,6 +246,8 @@ export const editorConfig = ({
|
||||
minHeight,
|
||||
learningContextId,
|
||||
}) => {
|
||||
const lmsEndpointUrl = getConfig().LMS_BASE_URL;
|
||||
const studioEndpointUrl = getConfig().STUDIO_BASE_URL;
|
||||
const {
|
||||
toolbar,
|
||||
config,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'CourseAuthoring/editors/setupEditorTest';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { MockUseState } from '../../testUtils';
|
||||
|
||||
import * as tinyMCE from '../../data/constants/tinyMCE';
|
||||
@@ -125,7 +126,7 @@ describe('TinyMceEditor hooks', () => {
|
||||
const setImage = jest.fn();
|
||||
const updateContent = jest.fn();
|
||||
const editorType = 'expandable';
|
||||
const lmsEndpointUrl = 'sOmEvaLue.cOm';
|
||||
const lmsEndpointUrl = getConfig().LMS_BASE_URL;
|
||||
const editor = {
|
||||
ui: { registry: { addButton, addToggleButton, addIcon } },
|
||||
on: jest.fn(),
|
||||
@@ -190,7 +191,7 @@ describe('TinyMceEditor hooks', () => {
|
||||
describe('replaceStaticWithAsset', () => {
|
||||
const initialContent = `<img src="/static/soMEImagEURl1.jpeg"/><a href="/assets/v1/${baseAssetUrl}/test.pdf">test</a><img src="/${baseAssetUrl}@correct.png" /><img src="/${baseAssetUrl}/correct.png" />`;
|
||||
const learningContextId = 'course-v1:org+test+run';
|
||||
const lmsEndpointUrl = 'sOmEvaLue.cOm';
|
||||
const lmsEndpointUrl = getConfig().LMS_BASE_URL;
|
||||
it('returns updated src for text editor to update content', () => {
|
||||
const expected = `<img src="/${baseAssetUrl}@soMEImagEURl1.jpeg"/><a href="/${baseAssetUrl}@test.pdf">test</a><img src="/${baseAssetUrl}@correct.png" /><img src="/${baseAssetUrl}@correct.png" />`;
|
||||
const actual = module.replaceStaticWithAsset({ initialContent, learningContextId });
|
||||
@@ -216,7 +217,7 @@ describe('TinyMceEditor hooks', () => {
|
||||
describe('setAssetToStaticUrl', () => {
|
||||
it('returns content with updated img links', () => {
|
||||
const editorValue = `<img src="/${baseAssetUrl}/soME_ImagE_URl1"/> <a href="/${baseAssetUrl}@soMEImagEURl">testing link</a>`;
|
||||
const lmsEndpointUrl = 'sOmEvaLue.cOm';
|
||||
const lmsEndpointUrl = getConfig().LMS_BASE_URL;
|
||||
const content = module.setAssetToStaticUrl({ editorValue, lmsEndpointUrl });
|
||||
expect(content).toEqual('<img src="/static/soME_ImagE_URl1"/> <a href="/static/soMEImagEURl">testing link</a>');
|
||||
});
|
||||
@@ -226,8 +227,8 @@ describe('TinyMceEditor hooks', () => {
|
||||
const props = {
|
||||
editorContentHtml: null,
|
||||
editorType: 'text',
|
||||
lmsEndpointUrl: 'sOmEuRl.cOm',
|
||||
studioEndpointUrl: 'sOmEoThEruRl.cOm',
|
||||
lmsEndpointUrl: getConfig().LMS_BASE_URL,
|
||||
studioEndpointUrl: getConfig().STUDIO_BASE_URL,
|
||||
images: mockImagesRef,
|
||||
isLibrary: false,
|
||||
learningContextId: 'course+org+run',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Provider, connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Editor } from '@tinymce/tinymce-react';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
|
||||
import 'tinymce';
|
||||
import 'tinymce/themes/silver';
|
||||
@@ -9,8 +9,6 @@ import 'tinymce/skins/ui/oxide/skin.css';
|
||||
import 'tinymce/icons/default';
|
||||
import 'frontend-components-tinymce-advanced-plugins';
|
||||
|
||||
import store from '../../data/store';
|
||||
import { selectors } from '../../data/redux';
|
||||
import ImageUploadModal from '../ImageUploadModal';
|
||||
import SourceCodeModal from '../SourceCodeModal';
|
||||
import * as hooks from './hooks';
|
||||
@@ -42,41 +40,37 @@ const TinyMceWidget = ({
|
||||
disabled,
|
||||
id,
|
||||
editorContentHtml, // editorContent in html form
|
||||
// redux
|
||||
learningContextId,
|
||||
images,
|
||||
isLibrary,
|
||||
lmsEndpointUrl,
|
||||
studioEndpointUrl,
|
||||
onChange,
|
||||
...editorConfig
|
||||
}) => {
|
||||
const { isImgOpen, openImgModal, closeImgModal } = hooks.imgModalToggle();
|
||||
const { isSourceCodeOpen, openSourceCodeModal, closeSourceCodeModal } = hooks.sourceCodeModalToggle(editorRef);
|
||||
const { imagesRef } = hooks.useImages({ images, editorContentHtml });
|
||||
|
||||
const imageSelection = hooks.selectedImage(null);
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
{isLibrary ? null : (
|
||||
<>
|
||||
{!isLibrary && (
|
||||
<ImageUploadModal
|
||||
isOpen={isImgOpen}
|
||||
close={closeImgModal}
|
||||
editorRef={editorRef}
|
||||
images={imagesRef}
|
||||
editorType={editorType}
|
||||
lmsEndpointUrl={lmsEndpointUrl}
|
||||
lmsEndpointUrl={getConfig().LMS_BASE_URL}
|
||||
{...imageSelection}
|
||||
/>
|
||||
)}
|
||||
{editorType === 'text' ? (
|
||||
{editorType === 'text' && (
|
||||
<SourceCodeModal
|
||||
isOpen={isSourceCodeOpen}
|
||||
close={closeSourceCodeModal}
|
||||
editorRef={editorRef}
|
||||
/>
|
||||
) : null}
|
||||
)}
|
||||
<Editor
|
||||
id={id}
|
||||
disabled={disabled}
|
||||
@@ -89,8 +83,6 @@ const TinyMceWidget = ({
|
||||
editorRef,
|
||||
isLibrary,
|
||||
learningContextId,
|
||||
lmsEndpointUrl,
|
||||
studioEndpointUrl,
|
||||
images: imagesRef,
|
||||
editorContentHtml,
|
||||
...imageSelection,
|
||||
@@ -98,15 +90,15 @@ const TinyMceWidget = ({
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Provider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
TinyMceWidget.defaultProps = {
|
||||
isLibrary: null,
|
||||
editorType: null,
|
||||
editorRef: null,
|
||||
lmsEndpointUrl: null,
|
||||
studioEndpointUrl: null,
|
||||
lmsEndpointUrl: '',
|
||||
studioEndpointUrl: '',
|
||||
images: null,
|
||||
id: null,
|
||||
disabled: false,
|
||||
@@ -116,7 +108,7 @@ TinyMceWidget.defaultProps = {
|
||||
...editorConfigDefaultProps,
|
||||
};
|
||||
TinyMceWidget.propTypes = {
|
||||
learningContextId: PropTypes.string,
|
||||
learningContextId: PropTypes.string.isRequired,
|
||||
editorType: PropTypes.string,
|
||||
isLibrary: PropTypes.bool,
|
||||
images: PropTypes.shape({}),
|
||||
@@ -131,13 +123,5 @@ TinyMceWidget.propTypes = {
|
||||
...editorConfigPropTypes,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
images: selectors.app.images(state),
|
||||
lmsEndpointUrl: selectors.app.lmsEndpointUrl(state),
|
||||
studioEndpointUrl: selectors.app.studioEndpointUrl(state),
|
||||
isLibrary: selectors.app.isLibrary(state),
|
||||
learningContextId: selectors.app.learningContextId(state),
|
||||
});
|
||||
|
||||
export const TinyMceWidgetInternal = TinyMceWidget; // For testing only
|
||||
export default (connect(mapStateToProps)(TinyMceWidget));
|
||||
export default TinyMceWidget;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React from 'react';
|
||||
import { shallow } from '@edx/react-unit-test-utils';
|
||||
import { selectors } from '../../data/redux';
|
||||
import SourceCodeModal from '../SourceCodeModal';
|
||||
import ImageUploadModal from '../ImageUploadModal';
|
||||
import { imgModalToggle, sourceCodeModalToggle } from './hooks';
|
||||
import { TinyMceWidgetInternal as TinyMceWidget, mapStateToProps } from '.';
|
||||
import { TinyMceWidgetInternal as TinyMceWidget } from '.';
|
||||
|
||||
const staticUrl = '/assets/sOmEaSsET';
|
||||
|
||||
@@ -22,20 +21,6 @@ jest.mock('@tinymce/tinymce-react', () => {
|
||||
jest.mock('../ImageUploadModal', () => 'ImageUploadModal');
|
||||
jest.mock('../SourceCodeModal', () => 'SourceCodeModal');
|
||||
|
||||
jest.mock('../../data/redux', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
selectors: {
|
||||
app: {
|
||||
lmsEndpointUrl: jest.fn(state => ({ lmsEndpointUrl: state })),
|
||||
studioEndpointUrl: jest.fn(state => ({ studioEndpointUrl: state })),
|
||||
isLibrary: jest.fn(state => ({ isLibrary: state })),
|
||||
images: jest.fn(state => ({ images: state })),
|
||||
learningContextId: jest.fn(state => ({ learningContextId: state })),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('./hooks', () => ({
|
||||
editorConfig: jest.fn(args => ({ editorConfig: args })),
|
||||
imgModalToggle: jest.fn(() => ({
|
||||
@@ -56,15 +41,6 @@ jest.mock('./hooks', () => ({
|
||||
useImages: jest.fn(() => ({ imagesRef: { current: [{ externalUrl: staticUrl }] } })),
|
||||
}));
|
||||
|
||||
jest.mock('react-redux', () => ({
|
||||
Provider: 'Provider',
|
||||
connect: (mapStateToProp, mapDispatchToProps) => (component) => ({
|
||||
mapStateToProp,
|
||||
mapDispatchToProps,
|
||||
component,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('TinyMceWidget', () => {
|
||||
const props = {
|
||||
editorType: 'text',
|
||||
@@ -103,32 +79,4 @@ describe('TinyMceWidget', () => {
|
||||
expect(wrapper.instance.findByType(ImageUploadModal).length).toBe(0);
|
||||
});
|
||||
});
|
||||
describe('mapStateToProps', () => {
|
||||
const testState = { A: 'pple', B: 'anana', C: 'ucumber' };
|
||||
test('lmsEndpointUrl from app.lmsEndpointUrl', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).lmsEndpointUrl,
|
||||
).toEqual(selectors.app.lmsEndpointUrl(testState));
|
||||
});
|
||||
test('studioEndpointUrl from app.studioEndpointUrl', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).studioEndpointUrl,
|
||||
).toEqual(selectors.app.studioEndpointUrl(testState));
|
||||
});
|
||||
test('images from app.images', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).images,
|
||||
).toEqual(selectors.app.images(testState));
|
||||
});
|
||||
test('isLibrary from app.isLibrary', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).isLibrary,
|
||||
).toEqual(selectors.app.isLibrary(testState));
|
||||
});
|
||||
test('learningContextId from app.learningContextId', () => {
|
||||
expect(
|
||||
mapStateToProps(testState).learningContextId,
|
||||
).toEqual(selectors.app.learningContextId(testState));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,32 +1,18 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect, Provider, useSelector } from 'react-redux';
|
||||
import { createStore } from 'redux';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { useSelector } from 'react-redux';
|
||||
import TinyMceWidget, { prepareEditorRef } from '../editors/sharedComponents/TinyMceWidget';
|
||||
|
||||
import { DEFAULT_EMPTY_WYSIWYG_VALUE } from '../constants';
|
||||
|
||||
const store = createStore(() => ({}));
|
||||
|
||||
export const SUPPORTED_TEXT_EDITORS = {
|
||||
text: 'text',
|
||||
expandable: 'expandable',
|
||||
};
|
||||
|
||||
const mapStateToProps = () => ({
|
||||
images: {},
|
||||
lmsEndpointUrl: getConfig().LMS_BASE_URL,
|
||||
studioEndpointUrl: getConfig().STUDIO_BASE_URL,
|
||||
isLibrary: true,
|
||||
onEditorChange: () => ({}),
|
||||
});
|
||||
const Editor = connect(mapStateToProps)(TinyMceWidget);
|
||||
|
||||
export const WysiwygEditor = ({
|
||||
initialValue, editorType, onChange, minHeight,
|
||||
}) => {
|
||||
// const courseId = "course+test+test+test"
|
||||
const { editorRef, refReady, setEditorRef } = prepareEditorRef();
|
||||
const { courseId } = useSelector((state) => state.courseDetail);
|
||||
const isEquivalentCodeExtraSpaces = (first, second) => {
|
||||
@@ -61,20 +47,21 @@ export const WysiwygEditor = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<Editor
|
||||
textValue={initialValue}
|
||||
editorRef={editorRef}
|
||||
editorType={editorType}
|
||||
initialValue={initialValue}
|
||||
minHeight={minHeight}
|
||||
editorContentHtml={initialValue}
|
||||
setEditorRef={setEditorRef}
|
||||
onChange={handleUpdate}
|
||||
initializeEditor={() => ({})}
|
||||
learningContextId={courseId}
|
||||
/>
|
||||
</Provider>
|
||||
<TinyMceWidget
|
||||
textValue={initialValue}
|
||||
editorRef={editorRef}
|
||||
editorType={editorType}
|
||||
initialValue={initialValue}
|
||||
minHeight={minHeight}
|
||||
editorContentHtml={initialValue}
|
||||
setEditorRef={setEditorRef}
|
||||
onChange={handleUpdate}
|
||||
initializeEditor={() => ({})}
|
||||
learningContextId={courseId}
|
||||
images={{}}
|
||||
isLibrary
|
||||
onEditorChange={() => ({})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ initialize({
|
||||
SUPPORT_URL: process.env.SUPPORT_URL || null,
|
||||
SUPPORT_EMAIL: process.env.SUPPORT_EMAIL || null,
|
||||
LEARNING_BASE_URL: process.env.LEARNING_BASE_URL,
|
||||
LMS_BASE_URL: process.env.LMS_BASE_URL || null,
|
||||
EXAMS_BASE_URL: process.env.EXAMS_BASE_URL || null,
|
||||
CALCULATOR_HELP_URL: process.env.CALCULATOR_HELP_URL || null,
|
||||
ENABLE_PROGRESS_GRAPH_SETTINGS: process.env.ENABLE_PROGRESS_GRAPH_SETTINGS || 'false',
|
||||
|
||||
@@ -43,6 +43,7 @@ mergeConfig({
|
||||
ENABLE_TEAM_TYPE_SETTING: process.env.ENABLE_TEAM_TYPE_SETTING === 'true',
|
||||
ENABLE_CHECKLIST_QUALITY: process.env.ENABLE_CHECKLIST_QUALITY || 'true',
|
||||
STUDIO_BASE_URL: process.env.STUDIO_BASE_URL || null,
|
||||
LMS_BASE_URL: process.env.LMS_BASE_URL || null,
|
||||
LIBRARY_MODE: process.env.LIBRARY_MODE || 'v1 only',
|
||||
}, 'CourseAuthoringConfig');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user