Skip to content
This repository was archived by the owner on Feb 14, 2025. It is now read-only.

Commit ef662f9

Browse files
author
mashal-m
committed
Merge branch 'main' of https://github.com/openedx/frontend-lib-content-components into mashal-m/react-upgrade-to-v17
2 parents 530183a + 8a2c725 commit ef662f9

File tree

18 files changed

+401
-127
lines changed

18 files changed

+401
-127
lines changed

.github/workflows/lockfileversion-check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ on:
1010

1111
jobs:
1212
version-check:
13-
uses: edx/.github/.github/workflows/lockfileversion-check-v3.yml@master
13+
uses: openedx/.github/.github/workflows/lockfileversion-check-v3.yml@master

package-lock.json

Lines changed: 52 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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@
6767
"@codemirror/lint": "^6.2.1",
6868
"@codemirror/state": "^6.0.0",
6969
"@codemirror/view": "^6.0.0",
70+
"@dnd-kit/core": "^6.0.8",
71+
"@dnd-kit/sortable": "^7.0.2",
72+
"@dnd-kit/utilities": "^3.2.1",
7073
"@edx/browserslist-config": "^1.1.1",
7174
"@reduxjs/toolkit": "^1.8.1",
7275
"@tinymce/tinymce-react": "^3.14.0",

src/editors/containers/ProblemEditor/data/OLXParser.js

Lines changed: 56 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ export const nonQuestionKeys = [
2828
'textline',
2929
];
3030

31+
export const richTextFormats = [
32+
'h1',
33+
'h2',
34+
'h3',
35+
'h4',
36+
'h5',
37+
'h6',
38+
'div',
39+
'p',
40+
'pre',
41+
];
42+
3143
export const responseKeys = [
3244
'multiplechoiceresponse',
3345
'numericalresponse',
@@ -62,57 +74,52 @@ export const stripNonTextTags = ({ input, tag }) => {
6274

6375
export class OLXParser {
6476
constructor(olxString) {
65-
this.problem = {};
66-
this.questionData = {};
67-
this.richTextProblem = {};
68-
const richTextOptions = {
77+
// There are two versions of the parsed XLM because the fields using tinymce require the order
78+
// of the parsed data and spacing values to be preserved. However, all the other widgets need
79+
// the data grouped by the wrapping tag. Examples of the parsed format can be found here:
80+
// https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/docs/v4/2.XMLparseOptions.md
81+
const baseParserOptions = {
6982
ignoreAttributes: false,
70-
alwaysCreateTextNode: true,
7183
numberParseOptions: {
7284
leadingZeros: false,
7385
hex: false,
7486
},
75-
preserveOrder: true,
7687
processEntities: false,
7788
};
89+
90+
// Base Parser
91+
this.problem = {};
7892
const parserOptions = {
79-
ignoreAttributes: false,
93+
...baseParserOptions,
8094
alwaysCreateTextNode: true,
81-
numberParseOptions: {
82-
leadingZeros: false,
83-
hex: false,
84-
},
85-
processEntities: false,
8695
};
8796
const builderOptions = {
88-
ignoreAttributes: false,
89-
numberParseOptions: {
90-
leadingZeros: false,
91-
hex: false,
92-
},
93-
processEntities: false,
97+
...baseParserOptions,
98+
};
99+
const parser = new XMLParser(parserOptions);
100+
this.builder = new XMLBuilder(builderOptions);
101+
this.parsedOLX = parser.parse(olxString);
102+
if (_.has(this.parsedOLX, 'problem')) {
103+
this.problem = this.parsedOLX.problem;
104+
}
105+
106+
// Parser with `preservedOrder: true` and `trimValues: false`
107+
this.richTextProblem = [];
108+
const richTextOptions = {
109+
...baseParserOptions,
110+
alwaysCreateTextNode: true,
111+
preserveOrder: true,
112+
trimValues: false,
94113
};
95114
const richTextBuilderOptions = {
96-
ignoreAttributes: false,
97-
numberParseOptions: {
98-
leadingZeros: false,
99-
hex: false,
100-
},
115+
...baseParserOptions,
101116
preserveOrder: true,
102-
processEntities: false,
117+
trimValues: false,
103118
};
104-
// There are two versions of the parsed XLM because the fields using tinymce require the order
105-
// of the parsed data to be preserved. However, all the other widgets need the data grouped by
106-
// the wrapping tag.
107119
const richTextParser = new XMLParser(richTextOptions);
108-
const parser = new XMLParser(parserOptions);
109-
this.builder = new XMLBuilder(builderOptions);
110120
this.richTextBuilder = new XMLBuilder(richTextBuilderOptions);
111-
this.parsedOLX = parser.parse(olxString);
112121
this.richTextOLX = richTextParser.parse(olxString);
113122
if (_.has(this.parsedOLX, 'problem')) {
114-
this.problem = this.parsedOLX.problem;
115-
this.questionData = this.richTextOLX[0].problem;
116123
this.richTextProblem = this.richTextOLX[0].problem;
117124
}
118125
}
@@ -462,7 +469,7 @@ export class OLXParser {
462469
* @return {string} string of OLX
463470
*/
464471
parseQuestions(problemType) {
465-
const problemArray = _.get(this.questionData[0], problemType) || this.questionData;
472+
const problemArray = _.get(this.richTextProblem[0], problemType) || this.richTextProblem;
466473

467474
const questionArray = [];
468475
problemArray.forEach(tag => {
@@ -478,7 +485,7 @@ export class OLXParser {
478485
*/
479486
tag[tagName].forEach(subTag => {
480487
const subTagName = Object.keys(subTag)[0];
481-
if (subTagName === 'label' || subTagName === 'description') {
488+
if (subTagName === 'label' || subTagName === 'description' || richTextFormats.includes(subTagName)) {
482489
questionArray.push(subTag);
483490
}
484491
});
@@ -503,11 +510,13 @@ export class OLXParser {
503510
if (objKeys.includes('demandhint')) {
504511
const currentDemandHint = obj.demandhint;
505512
currentDemandHint.forEach(hint => {
506-
const hintValue = this.richTextBuilder.build(hint.hint);
507-
hintsObject.push({
508-
id: hintsObject.length,
509-
value: hintValue,
510-
});
513+
if (Object.keys(hint).includes('hint')) {
514+
const hintValue = this.richTextBuilder.build(hint.hint);
515+
hintsObject.push({
516+
id: hintsObject.length,
517+
value: hintValue,
518+
});
519+
}
511520
});
512521
}
513522
});
@@ -526,21 +535,17 @@ export class OLXParser {
526535
getSolutionExplanation(problemType) {
527536
if (!_.has(this.problem, `${problemType}.solution`) && !_.has(this.problem, 'solution')) { return null; }
528537
const [problemBody] = this.richTextProblem.filter(section => Object.keys(section).includes(problemType));
529-
let { solution } = problemBody[problemType].pop();
530-
const { div } = solution[0];
531-
if (solution.length === 1 && div) {
532-
div.forEach((block) => {
533-
const [key] = Object.keys(block);
534-
const [value] = block[key];
535-
if ((key === 'p' || key === 'h2')
536-
&& (_.get(value, '#text', null) === 'Explanation')
537-
) {
538-
div.shift();
538+
const [solutionBody] = problemBody[problemType].filter(section => Object.keys(section).includes('solution'));
539+
const [divBody] = solutionBody.solution.filter(section => Object.keys(section).includes('div'));
540+
const solutionArray = [];
541+
if (divBody && divBody.div) {
542+
divBody.div.forEach(tag => {
543+
if (_.get(Object.values(tag)[0][0], '#text', null) !== 'Explanation') {
544+
solutionArray.push(tag);
539545
}
540546
});
541-
solution = div;
542547
}
543-
const solutionString = this.richTextBuilder.build(solution);
548+
const solutionString = this.richTextBuilder.build(solutionArray);
544549
return solutionString;
545550
}
546551

src/editors/containers/ProblemEditor/data/OLXParser.test.js

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { OLXParser } from './OLXParser';
22
import {
33
checkboxesOLXWithFeedbackAndHintsOLX,
4-
getCheckboxesOLXWithFeedbackAndHintsOLX,
54
dropdownOLXWithFeedbackAndHintsOLX,
65
numericInputWithFeedbackAndHintsOLX,
76
textInputWithFeedbackAndHintsOLX,
@@ -21,6 +20,7 @@ import {
2120
labelDescriptionQuestionOLX,
2221
htmlEntityTestOLX,
2322
numberParseTestOLX,
23+
solutionExplanationTest,
2424
} from './mockData/olxTestData';
2525
import { ProblemTypeKeys } from '../../../data/constants/problem';
2626

@@ -261,50 +261,55 @@ describe('OLXParser', () => {
261261
const problemType = olxparser.getProblemType();
262262
const question = olxparser.parseQuestions(problemType);
263263
it('should return an empty string for question', () => {
264-
expect(question).toBe(blankQuestionOLX.question);
264+
expect(question.trim()).toBe(blankQuestionOLX.question);
265265
});
266266
});
267267
describe('given a simple problem olx', () => {
268268
const question = textInputOlxParser.parseQuestions('stringresponse');
269269
it('should return a string of HTML', () => {
270-
expect(question).toEqual(textInputWithFeedbackAndHintsOLX.question);
270+
expect(question.trim()).toEqual(textInputWithFeedbackAndHintsOLX.question);
271271
});
272272
});
273273
describe('given olx with html entities', () => {
274274
const olxparser = new OLXParser(htmlEntityTestOLX.rawOLX);
275275
const problemType = olxparser.getProblemType();
276276
const question = olxparser.parseQuestions(problemType);
277277
it('should not encode html entities', () => {
278-
expect(question).toEqual(htmlEntityTestOLX.question);
278+
expect(question.trim()).toEqual(htmlEntityTestOLX.question);
279279
});
280280
});
281281
describe('given olx with styled content', () => {
282282
const olxparser = new OLXParser(styledQuestionOLX.rawOLX);
283283
const problemType = olxparser.getProblemType();
284284
const question = olxparser.parseQuestions(problemType);
285285
it('should pase/build correct styling', () => {
286-
expect(question).toBe(styledQuestionOLX.question);
286+
expect(question.trim()).toBe(styledQuestionOLX.question);
287287
});
288288
});
289289
describe('given olx with label and description tags inside response tag', () => {
290290
const olxparser = new OLXParser(labelDescriptionQuestionOLX.rawOLX);
291291
const problemType = olxparser.getProblemType();
292292
const question = olxparser.parseQuestions(problemType);
293293
it('should append the label/description to the question', () => {
294-
expect(question).toBe(labelDescriptionQuestionOLX.question);
294+
expect(question.trim()).toBe(labelDescriptionQuestionOLX.question);
295295
});
296296
});
297297
});
298298
describe('getSolutionExplanation()', () => {
299299
describe('for checkbox questions', () => {
300300
test('should parse text in p tags', () => {
301-
const { rawOLX } = getCheckboxesOLXWithFeedbackAndHintsOLX();
302-
const olxparser = new OLXParser(rawOLX);
301+
const olxparser = new OLXParser(checkboxesOLXWithFeedbackAndHintsOLX.rawOLX);
303302
const problemType = olxparser.getProblemType();
304303
const explanation = olxparser.getSolutionExplanation(problemType);
305-
const expected = getCheckboxesOLXWithFeedbackAndHintsOLX().solutionExplanation;
304+
const expected = checkboxesOLXWithFeedbackAndHintsOLX.solutionExplanation;
306305
expect(explanation.replace(/\s/g, '')).toBe(expected.replace(/\s/g, ''));
307306
});
308307
});
308+
it('should parse text with proper spacing', () => {
309+
const olxparser = new OLXParser(solutionExplanationTest.rawOLX);
310+
const problemType = olxparser.getProblemType();
311+
const explanation = olxparser.getSolutionExplanation(problemType);
312+
expect(explanation).toBe(solutionExplanationTest.solutionExplanation);
313+
});
309314
});
310315
});

0 commit comments

Comments
 (0)