From 880d205cbbab952e819950d27f7129ed8b64aaf1 Mon Sep 17 00:00:00 2001 From: connorhaugh <49422820+connorhaugh@users.noreply.github.com> Date: Tue, 10 Jan 2023 09:42:44 -0500 Subject: [PATCH 01/29] Feat: raw editor ingress and egress logic (#179) * feat: conditional rendering of olx editor. * fix: open the raw editor if advanced is chosen * fix: add test fix * feat: add button to switch visual->advanced * fix: add tests + lint for visual->advanced button * feat: revert to advanced if parser fails * fix: improve coverage * feat: add confirm dialog to switch * fix: load settings with advanced * fix: refactor + lint fix --- .../__snapshots__/index.test.jsx.snap | 10 +++ .../EditProblemView/SettingsWidget/index.jsx | 4 ++ .../SettingsWidget/messages.js | 20 ++++++ .../SwitchToAdvancedEditorCard.jsx | 53 ++++++++++++++++ .../SwitchToAdvancedEditorCard.test.jsx | 12 ++++ .../SwitchToAdvancedEditorCard.test.jsx.snap | 48 ++++++++++++++ .../components/EditProblemView/index.jsx | 6 +- .../components/SelectTypeModal/hooks.js | 3 +- .../components/SelectTypeModal/hooks.test.js | 1 + .../ProblemEditor/data/OLXParser.js | 15 ++++- .../ProblemEditor/data/OLXParser.test.js | 12 ++++ .../data/mockData/olxTestData.js | 14 +++++ .../containers/ProblemEditor/index.jsx | 4 +- .../data/redux/thunkActions/problem.js | 62 +++++++++++++------ .../data/redux/thunkActions/problem.test.js | 51 +++++++++++++++ www/package-lock.json | 1 + 16 files changed, 288 insertions(+), 28 deletions(-) create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.test.jsx create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap create mode 100644 src/editors/data/redux/thunkActions/problem.test.js diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap index 346860fd2..8a6aa9fa0 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap @@ -77,6 +77,11 @@ exports[`SettingsWidget snapshot snapshot: renders Settings widget page 1`] = ` > + + + @@ -163,6 +168,11 @@ exports[`SettingsWidget snapshot snapshot: renders Settings widget page advanced > + + + diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx index 90e5f904a..b97c357f6 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx @@ -13,6 +13,7 @@ import ResetCard from './settingsComponents/ResetCard'; import MatlabCard from './settingsComponents/MatlabCard'; import TimerCard from './settingsComponents/TimerCard'; import TypeCard from './settingsComponents/TypeCard'; +import SwitchToAdvancedEditorCard from './settingsComponents/SwitchToAdvancedEditorCard'; import messages from './messages'; import { showAdvancedSettingsCards } from './hooks'; @@ -75,6 +76,9 @@ export const SettingsWidget = ({ + + + diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js index 4539decc3..395acf755 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js @@ -149,6 +149,26 @@ export const messages = { defaultMessage: 'Type', description: 'Type settings card title', }, + SwitchButtonLabel: { + id: 'authoring.problemeditor.settings.switchtoadvancededitor.label', + defaultMessage: 'Switch To Advanced Editor', + description: 'button to switch to the advanced mode of the editor.', + }, + ConfirmSwitchMessage: { + id: 'authoring.problemeditor.settings.switchtoadvancededitor.message', + defaultMessage: 'If you use the advanced editor, this problem will be converted to OLX and you will not be able to return to the simple editor.', + description: 'message to confirm that a user wants to use the advanced editor', + }, + ConfirmSwitchMessageTitle: { + id: 'authoring.problemeditor.settings.switchtoadvancededitor.message', + defaultMessage: 'Convert to OLX?', + description: 'message to confirm that a user wants to use the advanced editor', + }, + ConfirmSwitchButtonLabel: { + id: 'authoring.problemeditor.settings.switchtoadvancededitor.message', + defaultMessage: 'Switch To Advanced Editor', + description: 'message to confirm that a user wants to use the advanced editor', + }, }; export default messages; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx new file mode 100644 index 000000000..297854a5b --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { injectIntl, FormattedMessage } from '@edx/frontend-platform/i18n'; +import { Button, Card } from '@edx/paragon'; +import PropTypes from 'prop-types'; +import messages from '../messages'; +import { thunkActions } from '../../../../../../data/redux'; +import BaseModal from '../../../../../TextEditor/components/BaseModal'; + +export const SwitchToAdvancedEditorCard = ({ + switchToAdvancedEditor, +}) => { + const [isConfirmOpen, setConfirmOpen] = React.useState(false); + return ( + + { setConfirmOpen(false); }} + title={()} + confirmAction={( + + )} + size="md" + > + + + + + ); +}; + +SwitchToAdvancedEditorCard.propTypes = { + switchToAdvancedEditor: PropTypes.func.isRequired, +}; + +export const mapStateToProps = () => ({ +}); +export const mapDispatchToProps = { + switchToAdvancedEditor: thunkActions.problem.switchToAdvancedEditor, +}; + +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SwitchToAdvancedEditorCard)); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.test.jsx new file mode 100644 index 000000000..96d0b4a5c --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.test.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { SwitchToAdvancedEditorCard } from './SwitchToAdvancedEditorCard'; + +describe('SwitchToAdvancedEditorCard snapshot', () => { + const mockSwitchToAdvancedEditor = jest.fn().mockName('switchToAdvancedEditor'); + test('snapshot: SwitchToAdvancedEditorCard', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap new file mode 100644 index 000000000..07768a93c --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap @@ -0,0 +1,48 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SwitchToAdvancedEditorCard snapshot snapshot: SwitchToAdvancedEditorCard 1`] = ` + + + + + } + footerAction={null} + isOpen={false} + size="md" + title={ + + } + > + + + + +`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx index 5600f9e3a..d832f05d4 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx @@ -10,7 +10,7 @@ import { EditorContainer } from '../../../EditorContainer'; import { selectors } from '../../../../data/redux'; import ReactStateSettingsParser from '../../data/ReactStateSettingsParser'; import ReactStateOLXParser from '../../data/ReactStateOLXParser'; -import { AdvanceProblemKeys } from '../../../../data/constants/problem'; +import { ProblemTypeKeys } from '../../../../data/constants/problem'; export const EditProblemView = ({ problemType, @@ -24,8 +24,8 @@ export const EditProblemView = ({ olx: reactOLXParser.buildOLX(), }; }; - if (Object.values(AdvanceProblemKeys).includes(problemType)) { - return `hello raw editor with ${problemType}`; + if (problemType === ProblemTypeKeys.ADVANCED) { + return 'hello raw editor'; } return ( diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js index f1dace285..1b225aee4 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js @@ -19,7 +19,8 @@ export const selectHooks = () => { export const onSelect = (setProblemType, selected, updateField) => () => { if (Object.values(AdvanceProblemKeys).includes(selected)) { - updateField({ rawOLX: AdvanceProblems[selected].template }); + updateField({ problemType: ProblemTypeKeys.ADVANCED, rawOLX: AdvanceProblems[selected].template }); + return; } setProblemType({ selected }); }; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js index 50829221e..9cb24caf5 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js @@ -47,6 +47,7 @@ describe('SelectTypeModal hooks', () => { test('updateField is called with selected templated if selected is an Advanced Problem', () => { module.onSelect(mockSetProblemType, mockAdvancedSelected, mockUpdateField)(); expect(mockUpdateField).toHaveBeenCalledWith({ + problemType: ProblemTypeKeys.ADVANCED, rawOLX: AdvanceProblems[mockAdvancedSelected].template, }); }); diff --git a/src/editors/containers/ProblemEditor/data/OLXParser.js b/src/editors/containers/ProblemEditor/data/OLXParser.js index 18126a039..e01fa50e1 100644 --- a/src/editors/containers/ProblemEditor/data/OLXParser.js +++ b/src/editors/containers/ProblemEditor/data/OLXParser.js @@ -331,10 +331,16 @@ export class OLXParser { getProblemType() { const problemKeys = Object.keys(this.problem); const intersectedProblems = _.intersection(Object.values(ProblemTypeKeys), problemKeys); - if (intersectedProblems.length === 0) { - return null; + // a blank problem is a problem which contains only `` as it's olx. + // blank problems are not given types, so that a type may be selected. + if (problemKeys.length === 1 && problemKeys[0] === '#text' && this.problem[problemKeys[0]] === '') { + return null; + } + // if we have no matching problem type, the problem is advanced. + return ProblemTypeKeys.ADVANCED; } + // make sure compound problems are treated as advanced if (intersectedProblems.length > 1) { return ProblemTypeKeys.ADVANCED; } @@ -369,7 +375,10 @@ export class OLXParser { answersObject = this.parseMultipleChoiceAnswers(ProblemTypeKeys.SINGLESELECT, 'choicegroup', 'choice'); break; case ProblemTypeKeys.ADVANCED: - break; + return { + problemType, + settings: {}, + }; default: // if problem is unset, return null return {}; diff --git a/src/editors/containers/ProblemEditor/data/OLXParser.test.js b/src/editors/containers/ProblemEditor/data/OLXParser.test.js index 3ae946638..5823e4b6e 100644 --- a/src/editors/containers/ProblemEditor/data/OLXParser.test.js +++ b/src/editors/containers/ProblemEditor/data/OLXParser.test.js @@ -7,6 +7,8 @@ import { textInputWithFeedbackAndHintsOLX, mutlipleChoiceWithFeedbackAndHintsOLX, textInputWithFeedbackAndHintsOLXWithMultipleAnswers, + advancedProblemOlX, + blankProblemOLX, } from './mockData/olxTestData'; import { ProblemTypeKeys } from '../../../data/constants/problem'; @@ -36,6 +38,16 @@ describe('Check OLXParser problem type', () => { const problemType = olxparser.getProblemType(); expect(problemType).toBe(ProblemTypeKeys.TEXTINPUT); }); + test('Test Advanced Problem Type', () => { + const olxparser = new OLXParser(advancedProblemOlX.rawOLX); + const problemType = olxparser.getProblemType(); + expect(problemType).toBe(ProblemTypeKeys.ADVANCED); + }); + test('Test Blank Problem Type', () => { + const olxparser = new OLXParser(blankProblemOLX.rawOLX); + const problemType = olxparser.getProblemType(); + expect(problemType).toBe(null); + }); }); describe('Check OLXParser hints', () => { diff --git a/src/editors/containers/ProblemEditor/data/mockData/olxTestData.js b/src/editors/containers/ProblemEditor/data/mockData/olxTestData.js index cc9e2de9d..22ab47cf0 100644 --- a/src/editors/containers/ProblemEditor/data/mockData/olxTestData.js +++ b/src/editors/containers/ProblemEditor/data/mockData/olxTestData.js @@ -553,3 +553,17 @@ export const numericInputWithFeedbackAndHintsOLXException = { `, }; +export const advancedProblemOlX = { + rawOLX: ` + +

You can use this template as a guide to the OLX markup to use for math expression problems. Edit this component to replace the example with your own assessment.

+ + You can add an optional tip or note related to the prompt like this. Example: To test this example, the correct answer is R_1*R_2/R_3 + + +
+
`, +}; +export const blankProblemOLX = { + rawOLX: '', +}; diff --git a/src/editors/containers/ProblemEditor/index.jsx b/src/editors/containers/ProblemEditor/index.jsx index 542fcabaa..b96a400c7 100644 --- a/src/editors/containers/ProblemEditor/index.jsx +++ b/src/editors/containers/ProblemEditor/index.jsx @@ -17,8 +17,6 @@ export const ProblemEditor = ({ blockValue, initializeProblemEditor, }) => { - React.useEffect(() => initializeProblemEditor(blockValue), [blockValue]); - // TODO: INTL MSG, Add LOAD FAILED ERROR using BLOCKFAILED if (!blockFinished || !studioViewFinished) { return (
@@ -31,6 +29,8 @@ export const ProblemEditor = ({ ); } // once data is loaded, init store + React.useEffect(() => initializeProblemEditor(blockValue), [blockValue]); + // TODO: INTL MSG, Add LOAD FAILED ERROR using BLOCKFAILED if (problemType === null) { return (); diff --git a/src/editors/data/redux/thunkActions/problem.js b/src/editors/data/redux/thunkActions/problem.js index d1902d46f..82caf7928 100644 --- a/src/editors/data/redux/thunkActions/problem.js +++ b/src/editors/data/redux/thunkActions/problem.js @@ -1,31 +1,55 @@ import _ from 'lodash-es'; -import * as requests from './requests'; import { actions } from '..'; import { OLXParser } from '../../../containers/ProblemEditor/data/OLXParser'; import { parseSettings } from '../../../containers/ProblemEditor/data/SettingsParser'; +import { ProblemTypeKeys } from '../../constants/problem'; +import ReactStateOLXParser from '../../../containers/ProblemEditor/data/ReactStateOLXParser'; +import { blankProblemOLX } from '../../../containers/ProblemEditor/data/mockData/olxTestData'; + +export const switchToAdvancedEditor = () => (dispatch, getState) => { + const state = getState(); + const reactOLXParser = new ReactStateOLXParser({ problem: state.problem }); + const rawOlx = reactOLXParser.buildOLX(); + dispatch(actions.problem.updateField({ problemType: ProblemTypeKeys.ADVANCED, rawOlx })); +}; + +export const isBlankProblem = ({ rawOLX }) => { + if (rawOLX === blankProblemOLX.rawOLX) { + return true; + } + return false; +}; + +export const getDataFromOlx = ({ rawOLX, rawSettings }) => { + let olxParser; + let parsedProblem; + try { + olxParser = new OLXParser(rawOLX); + parsedProblem = olxParser.getParsedOLXData(); + } catch { + console.error('The Problem Could Not Be Parsed from OLX. redirecting to Advanced editor.'); + return { problemType: ProblemTypeKeys.ADVANCED, rawOLX, settings: parseSettings(rawSettings) }; + } + if (parsedProblem?.problemType === ProblemTypeKeys.ADVANCED) { + return { problemType: ProblemTypeKeys.ADVANCED, rawOLX, settings: parseSettings(rawSettings) }; + } + const { settings, ...data } = parsedProblem; + const parsedSettings = { ...settings, ...parseSettings(rawSettings) }; + if (!_.isEmpty(rawOLX) && !_.isEmpty(data)) { + return { ...data, rawOLX, settings: parsedSettings }; + } + return {}; +}; export const initializeProblem = (blockValue) => (dispatch) => { const rawOLX = _.get(blockValue, 'data.data', {}); - const olxParser = new OLXParser(rawOLX); + const rawSettings = _.get(blockValue, 'data.metadata', {}); - const parsedProblem = olxParser.getParsedOLXData(); - if (_.isEmpty(parsedProblem)) { - // if problem is blank, enable selection. + if (isBlankProblem({ rawOLX })) { dispatch(actions.problem.setEnableTypeSelection()); + } else { + dispatch(actions.problem.load(getDataFromOlx({ rawOLX, rawSettings }))); } - const { settings, ...data } = parsedProblem; - const parsedSettings = { ...settings, ...parseSettings(_.get(blockValue, 'data.metadata', {})) }; - if (!_.isEmpty(rawOLX) && !_.isEmpty(data)) { - dispatch(actions.problem.load({ ...data, rawOLX, settings: parsedSettings })); - } - dispatch(requests.fetchAdvanceSettings({ - onSuccess: (response) => { - console.log(response); - if (response.data.allow_unsupported_xblocks.value) { - console.log(response.allow_unsupported_xblocks.value); - } - }, - })); }; -export default { initializeProblem }; +export default { initializeProblem, switchToAdvancedEditor }; diff --git a/src/editors/data/redux/thunkActions/problem.test.js b/src/editors/data/redux/thunkActions/problem.test.js new file mode 100644 index 000000000..c48c82bd8 --- /dev/null +++ b/src/editors/data/redux/thunkActions/problem.test.js @@ -0,0 +1,51 @@ +import { actions } from '..'; +import { initializeProblem, switchToAdvancedEditor } from './problem'; +import { checkboxesOLXWithFeedbackAndHintsOLX, advancedProblemOlX, blankProblemOLX } from '../../../containers/ProblemEditor/data/mockData/olxTestData'; +import { ProblemTypeKeys } from '../../constants/problem'; + +const mockOlx = 'SOmEVALue'; +const mockBuildOlx = jest.fn(() => mockOlx); +jest.mock('../../../containers/ProblemEditor/data/ReactStateOLXParser', () => jest.fn().mockImplementation(() => ({ buildOLX: mockBuildOlx }))); + +jest.mock('..', () => ({ + actions: { + problem: { + load: () => {}, + setEnableTypeSelection: () => {}, + updateField: (args) => args, + }, + }, +})); + +describe('problem thunkActions', () => { + let dispatch; + let getState; + beforeEach(() => { + dispatch = jest.fn((action) => ({ dispatch: action })); + getState = jest.fn(() => ({ + problem: { + }, + })); + }); + test('initializeProblem visual Problem :', () => { + const blockValue = { data: { data: checkboxesOLXWithFeedbackAndHintsOLX.rawOLX } }; + initializeProblem(blockValue)(dispatch); + expect(dispatch).toHaveBeenCalledWith(actions.problem.load()); + }); + test('initializeProblem advanced Problem', () => { + const blockValue = { data: { data: advancedProblemOlX.rawOLX } }; + initializeProblem(blockValue)(dispatch); + expect(dispatch).toHaveBeenCalledWith(actions.problem.load()); + }); + test('initializeProblem blank Problem', () => { + const blockValue = { data: { data: blankProblemOLX.rawOLX } }; + initializeProblem(blockValue)(dispatch); + expect(dispatch).toHaveBeenCalledWith(actions.problem.setEnableTypeSelection()); + }); + test('switchToAdvancedEditor visual Problem', () => { + switchToAdvancedEditor()(dispatch, getState); + expect(dispatch).toHaveBeenCalledWith( + actions.problem.updateField({ problemType: ProblemTypeKeys.ADVANCED, rawOlx: mockOlx }), + ); + }); +}); diff --git a/www/package-lock.json b/www/package-lock.json index e1efceca8..6dc2344c5 100644 --- a/www/package-lock.json +++ b/www/package-lock.json @@ -26,6 +26,7 @@ } }, "..": { + "name": "@edx/frontend-lib-content-components", "version": "1.0.0-semantically-released", "license": "AGPL-3.0", "dependencies": { From af4cd55390077bd02b64e6880d95ea6eb67059d7 Mon Sep 17 00:00:00 2001 From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> Date: Tue, 10 Jan 2023 13:25:19 -0500 Subject: [PATCH 02/29] feat: unselect multiple choices type multi->single (#181) --- .../AnswerWidget/AnswersContainer.jsx | 6 +- .../AnswerWidget/AnswersContainer.test.jsx | 50 ++++++ .../AnswersContainer.test.jsx.snap | 33 ++++ .../EditProblemView/SettingsWidget/hooks.js | 18 ++- .../SettingsWidget/hooks.test.js | 14 +- .../EditProblemView/SettingsWidget/index.jsx | 23 ++- .../settingsComponents/TypeCard.jsx | 18 ++- .../settingsComponents/TypeCard.test.jsx | 4 + .../settingsComponents/TypeRow.jsx | 20 ++- .../settingsComponents/TypeRow.test.jsx | 11 +- .../__snapshots__/TypeCard.test.jsx.snap | 18 +++ src/editors/containers/ProblemEditor/hooks.js | 9 +- src/editors/data/redux/problem/reducers.js | 18 ++- .../data/redux/problem/reducers.test.js | 147 ++++++++++++++++++ src/editors/data/redux/problem/selectors.js | 1 + .../data/redux/problem/selectors.test.js | 52 +++++++ 16 files changed, 429 insertions(+), 13 deletions(-) create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap create mode 100644 src/editors/data/redux/problem/reducers.test.js create mode 100644 src/editors/data/redux/problem/selectors.test.js diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx index d85b9ea2e..76bff6235 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx @@ -16,9 +16,9 @@ export const AnswersContainer = ({ // Redux answers, addAnswer, + updateField, }) => { - const { hasSingleAnswer } = initializeAnswerContainer(problemType); - + const { hasSingleAnswer } = initializeAnswerContainer({ answers, problemType, updateField }); return (
{answers.map((answer) => ( @@ -44,6 +44,7 @@ AnswersContainer.propTypes = { problemType: PropTypes.string.isRequired, answers: PropTypes.arrayOf(answerOptionProps).isRequired, addAnswer: PropTypes.func.isRequired, + updateField: PropTypes.func.isRequired, }; export const mapStateToProps = (state) => ({ @@ -52,6 +53,7 @@ export const mapStateToProps = (state) => ({ export const mapDispatchToProps = { addAnswer: actions.problem.addAnswer, + updateField: actions.problem.updateField, }; export default connect(mapStateToProps, mapDispatchToProps)(AnswersContainer); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx new file mode 100644 index 000000000..f96a727f5 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { actions, selectors } from '../../../../../data/redux'; + +import * as module from './AnswersContainer'; + +jest.mock('../../../../../data/redux', () => ({ + actions: { + problem: { + updateField: jest.fn().mockName('actions.problem.updateField'), + addAnswer: jest.fn().mockName('actions.problem.addAnswer'), + }, + }, + selectors: { + problem: { + answers: jest.fn(state => ({ answers: state })), + }, + }, +})); +describe('AnswersContainer', () => { + const props = { + answers: [], + updateField: jest.fn(), + addAnswer: jest.fn(), + }; + describe('render', () => { + test('snapshot: renders correct default', () => { + expect(shallow()).toMatchSnapshot(); + }); + test('snapshot: renders correctly with answers', () => { + expect(shallow()).toMatchSnapshot(); + }); + }); + describe('mapStateToProps', () => { + const testState = { A: 'pple', B: 'anana', C: 'ucumber' }; + test('answers from problem.answers', () => { + expect( + module.mapStateToProps(testState).answers, + ).toEqual(selectors.problem.answers(testState)); + }); + }); + describe('mapDispatchToProps', () => { + test('updateField from actions.problem.updateField', () => { + expect(module.mapDispatchToProps.updateField).toEqual(actions.problem.updateField); + }); + test('updateField from actions.problem.addAnswer', () => { + expect(module.mapDispatchToProps.addAnswer).toEqual(actions.problem.addAnswer); + }); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap new file mode 100644 index 000000000..848eaa19e --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AnswersContainer render snapshot: renders correct default 1`] = ` +
+ +
+`; + +exports[`AnswersContainer render snapshot: renders correctly with answers 1`] = ` +
+ +
+`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js index e77ab6196..6ef3e3c76 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js @@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'; import _ from 'lodash-es'; import * as module from './hooks'; import messages from './messages'; -import { ShowAnswerTypesKeys } from '../../../../../data/constants/problem'; +import { ProblemTypeKeys, ShowAnswerTypesKeys } from '../../../../../data/constants/problem'; export const state = { showAdvanced: (val) => useState(val), @@ -182,11 +182,23 @@ export const timerCardHooks = (updateSettings) => ({ }, }); -export const typeRowHooks = (typeKey, updateField) => { +export const typeRowHooks = ({ + answers, + correctAnswerCount, + typeKey, + updateField, + updateAnswer, +}) => { const onClick = () => { + if (typeKey === ProblemTypeKeys.SINGLESELECT || typeKey === ProblemTypeKeys.DROPDOWN) { + if (correctAnswerCount > 1) { + answers.forEach(answer => { + updateAnswer({ ...answer, correct: false }); + }); + } + } updateField({ problemType: typeKey }); }; - return { onClick, }; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js index 9d9a41b9d..b43a5046c 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js @@ -18,6 +18,7 @@ jest.mock('../../../../../data/redux', () => ({ problem: { updateSettings: (args) => ({ updateSettings: args }), updateField: (args) => ({ updateField: args }), + updateAnswer: (args) => ({ updateAnswer: args }), }, }, })); @@ -217,10 +218,19 @@ describe('Problem settings hooks', () => { describe('Type row hooks', () => { test('test onClick', () => { - const typekey = 'TEXTINPUT'; + const typekey = 'multiplechoiceresponse'; const updateField = jest.fn(); - output = hooks.typeRowHooks(typekey, updateField); + const updateAnswer = jest.fn(); + const answers = [{ correct: true, id: 'a' }, { correct: true, id: 'b' }]; + output = hooks.typeRowHooks({ + answers, + correctAnswerCount: 2, + typeKey: typekey, + updateField, + updateAnswer, + }); output.onClick(); + expect(updateAnswer).toHaveBeenCalledWith({ ...answers[1], correct: false }); expect(updateField).toHaveBeenCalledWith({ problemType: typekey }); }); }); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx index b97c357f6..cef24d973 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx @@ -23,9 +23,12 @@ import './index.scss'; export const SettingsWidget = ({ problemType, // redux + answers, + correctAnswerCount, settings, updateSettings, updateField, + updateAnswer, }) => { const { isAdvancedCardsVisible, showAdvancedCards } = showAdvancedSettingsCards(); return ( @@ -38,7 +41,13 @@ export const SettingsWidget = ({ - + @@ -91,7 +100,16 @@ export const SettingsWidget = ({ }; SettingsWidget.propTypes = { + answers: PropTypes.arrayOf(PropTypes.shape({ + correct: PropTypes.bool, + id: PropTypes.string, + selectedFeedback: PropTypes.string, + title: PropTypes.string, + unselectedFeedback: PropTypes.string, + })).isRequired, + correctAnswerCount: PropTypes.number.isRequired, problemType: PropTypes.string.isRequired, + updateAnswer: PropTypes.func.isRequired, updateField: PropTypes.func.isRequired, updateSettings: PropTypes.func.isRequired, // eslint-disable-next-line @@ -100,11 +118,14 @@ SettingsWidget.propTypes = { const mapStateToProps = (state) => ({ settings: selectors.problem.settings(state), + answers: selectors.problem.answers(state), + correctAnswerCount: selectors.problem.correctAnswerCount(state), }); export const mapDispatchToProps = { updateSettings: actions.problem.updateSettings, updateField: actions.problem.updateField, + updateAnswer: actions.problem.updateAnswer, }; export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SettingsWidget)); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx index 06a085db2..b8d509b29 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx @@ -7,8 +7,11 @@ import messages from '../messages'; import TypeRow from './TypeRow'; export const TypeCard = ({ + answers, + correctAnswerCount, problemType, updateField, + updateAnswer, // inject intl, }) => { @@ -21,12 +24,15 @@ export const TypeCard = ({ > {problemTypeKeysArray.map((typeKey, i) => ( ))} @@ -34,9 +40,19 @@ export const TypeCard = ({ }; TypeCard.propTypes = { - intl: intlShape.isRequired, + answers: PropTypes.arrayOf(PropTypes.shape({ + correct: PropTypes.bool, + id: PropTypes.string, + selectedFeedback: PropTypes.string, + title: PropTypes.string, + unselectedFeedback: PropTypes.string, + })).isRequired, + correctAnswerCount: PropTypes.number.isRequired, problemType: PropTypes.string.isRequired, updateField: PropTypes.func.isRequired, + updateAnswer: PropTypes.func.isRequired, + // injected + intl: intlShape.isRequired, }; export default injectIntl(TypeCard); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx index 1886661a5..d2a2f8518 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx @@ -6,8 +6,12 @@ import { ProblemTypeKeys } from '../../../../../../data/constants/problem'; describe('TypeCard', () => { const props = { + answers: [], + correctAnswerCount: 0, problemType: ProblemTypeKeys.TEXTINPUT, updateField: jest.fn().mockName('args.updateField'), + updateAnswer: jest.fn().mockName('args.updateAnswer'), + // injected intl: { formatMessage }, }; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx index 71810543c..811499d43 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx @@ -5,13 +5,22 @@ import { Check } from '@edx/paragon/icons'; import { typeRowHooks } from '../hooks'; export const TypeRow = ({ + answers, + correctAnswerCount, typeKey, label, selected, lastRow, updateField, + updateAnswer, }) => { - const { onClick } = typeRowHooks(typeKey, updateField); + const { onClick } = typeRowHooks({ + answers, + correctAnswerCount, + typeKey, + updateField, + updateAnswer, + }); return ( <> @@ -25,10 +34,19 @@ export const TypeRow = ({ }; TypeRow.propTypes = { + answers: PropTypes.arrayOf(PropTypes.shape({ + correct: PropTypes.bool, + id: PropTypes.string, + selectedFeedback: PropTypes.string, + title: PropTypes.string, + unselectedFeedback: PropTypes.string, + })).isRequired, + correctAnswerCount: PropTypes.number.isRequired, typeKey: PropTypes.string.isRequired, label: PropTypes.string.isRequired, selected: PropTypes.bool.isRequired, lastRow: PropTypes.bool.isRequired, + updateAnswer: PropTypes.func.isRequired, updateField: PropTypes.func.isRequired, }; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx index 72c4579e8..2065e57c0 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx @@ -10,11 +10,14 @@ jest.mock('../hooks', () => ({ describe('TypeRow', () => { const typeKey = 'TEXTINPUT'; const props = { + answers: [], + correctAnswerCount: 0, typeKey, label: 'Text Input Problem', selected: true, lastRow: false, updateField: jest.fn().mockName('args.updateField'), + updateAnswer: jest.fn().mockName('args.updateAnswer'), }; const typeRowHooksProps = { @@ -26,7 +29,13 @@ describe('TypeRow', () => { describe('behavior', () => { it(' calls typeRowHooks when initialized', () => { shallow(); - expect(typeRowHooks).toHaveBeenCalledWith(typeKey, props.updateField); + expect(typeRowHooks).toHaveBeenCalledWith({ + answers: props.answers, + correctAnswerCount: props.correctAnswerCount, + typeKey, + updateField: props.updateField, + updateAnswer: props.updateAnswer, + }); }); }); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap index 251289f10..9b9ef8114 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap @@ -6,51 +6,69 @@ exports[`TypeCard snapshot snapshot: renders type setting card 1`] = ` title="Type" > diff --git a/src/editors/containers/ProblemEditor/hooks.js b/src/editors/containers/ProblemEditor/hooks.js index 78bf14ca6..cb0a539dd 100644 --- a/src/editors/containers/ProblemEditor/hooks.js +++ b/src/editors/containers/ProblemEditor/hooks.js @@ -35,7 +35,14 @@ export const prepareEditorRef = () => { return { editorRef, refReady, setEditorRef }; }; -export const initializeAnswerContainer = (problemType) => { +export const initializeAnswerContainer = ({ answers, problemType, updateField }) => { const hasSingleAnswer = problemType === ProblemTypeKeys.DROPDOWN || problemType === ProblemTypeKeys.SINGLESELECT; + let answerCount = 0; + answers.forEach(answer => { + if (answer.correct) { + answerCount += 1; + } + }); + updateField({ correctAnswerCount: answerCount }); return { hasSingleAnswer }; }; diff --git a/src/editors/data/redux/problem/reducers.js b/src/editors/data/redux/problem/reducers.js index 765c26ede..41c6c3061 100644 --- a/src/editors/data/redux/problem/reducers.js +++ b/src/editors/data/redux/problem/reducers.js @@ -10,6 +10,7 @@ const initialState = { problemType: null, question: '', answers: [], + correctAnswerCount: 0, groupFeedbackList: [], additionalAttributes: {}, settings: { @@ -46,8 +47,17 @@ const problem = createSlice({ }), updateAnswer: (state, { payload }) => { const { id, hasSingleAnswer, ...answer } = payload; + let { correctAnswerCount } = state; const answers = state.answers.map(obj => { if (obj.id === id) { + if (_.has(answer, 'correct') && payload.correct) { + correctAnswerCount += 1; + return { ...obj, ...answer }; + } + if (_.has(answer, 'correct') && payload.correct === false) { + correctAnswerCount -= 1; + return { ...obj, ...answer }; + } return { ...obj, ...answer }; } // set other answers as incorrect if problem only has one answer correct @@ -59,14 +69,19 @@ const problem = createSlice({ }); return { ...state, + correctAnswerCount, answers, }; }, deleteAnswer: (state, { payload }) => { - const { id } = payload; + const { id, correct } = payload; if (state.answers.length <= 1) { return state; } + let { correctAnswerCount } = state; + if (correct) { + correctAnswerCount -= 1; + } const answers = state.answers.filter(obj => obj.id !== id).map((answer, index) => { const newId = indexToLetterMap[index]; if (answer.id === newId) { @@ -76,6 +91,7 @@ const problem = createSlice({ }); return { ...state, + correctAnswerCount, answers, }; }, diff --git a/src/editors/data/redux/problem/reducers.test.js b/src/editors/data/redux/problem/reducers.test.js new file mode 100644 index 000000000..db1b828f2 --- /dev/null +++ b/src/editors/data/redux/problem/reducers.test.js @@ -0,0 +1,147 @@ +import { initialState, actions, reducer } from './reducers'; + +const testingState = { + ...initialState, + arbitraryField: 'arbitrary', +}; + +describe('problem reducer', () => { + it('has initial state', () => { + expect(reducer(undefined, {})).toEqual(initialState); + }); + + const testValue = 'roll for initiative'; + + describe('handling actions', () => { + const setterTest = (action, target) => { + describe(action, () => { + it(`load ${target} from payload`, () => { + expect(reducer(testingState, actions[action](testValue))).toEqual({ + ...testingState, + [target]: testValue, + }); + }); + }); + }; + [ + ['updateQuestion', 'question'], + ].map(args => setterTest(...args)); + describe('load', () => { + it('sets answers', () => { + const answer = { + id: 'A', + correct: false, + feedback: '', + selectedFeedback: undefined, + title: '', + unselectedFeedback: undefined, + }; + expect(reducer(testingState, actions.addAnswer(answer))).toEqual({ + ...testingState, + answers: [answer], + }); + }); + }); + describe('setProblemType', () => { + it('sets problemType', () => { + const payload = { selected: 'soMePRoblEMtYPe' }; + expect(reducer(testingState, actions.setProblemType(payload))).toEqual({ + ...testingState, + problemType: 'soMePRoblEMtYPe', + }); + }); + }); + describe('setEnableTypeSelection', () => { + it('sets problemType to null', () => { + expect(reducer(testingState, actions.setEnableTypeSelection())).toEqual({ + ...testingState, + problemType: null, + }); + }); + }); + describe('updateField', () => { + it('sets given parameter', () => { + const payload = { problemType: 'soMePRoblEMtYPe' }; + expect(reducer(testingState, actions.updateField(payload))).toEqual({ + ...testingState, + ...payload, + }); + }); + }); + describe('updateSettings', () => { + it('sets given settings parameter', () => { + const payload = { hints: ['soMehInt'] }; + expect(reducer(testingState, actions.updateSettings(payload))).toEqual({ + ...testingState, + settings: { + ...testingState.settings, + ...payload, + }, + }); + }); + }); + describe('addAnswer', () => { + it('sets answers', () => { + const answer = { + id: 'A', + correct: false, + feedback: '', + selectedFeedback: undefined, + title: '', + unselectedFeedback: undefined, + }; + expect(reducer(testingState, actions.addAnswer(answer))).toEqual({ + ...testingState, + answers: [answer], + }); + }); + }); + describe('updateAnswer', () => { + it('sets answers, as well as setting the correctAnswerCount ', () => { + const answer = { id: 'A', correct: true }; + expect(reducer( + { + ...testingState, + answers: [{ + id: 'A', + correct: false, + }], + }, + actions.updateAnswer(answer), + )).toEqual({ + ...testingState, + correctAnswerCount: 1, + answers: [{ id: 'A', correct: true }], + }); + }); + }); + describe('deleteAnswer', () => { + it('sets answers, as well as setting the correctAnswerCount ', () => { + const answer = { id: 'A' }; + expect(reducer( + { + ...testingState, + correctAnswerCount: 1, + answers: [{ + id: 'A', + correct: false, + }, + { + id: 'B', + correct: true, + }], + }, + actions.deleteAnswer(answer), + )).toEqual({ + ...testingState, + correctAnswerCount: 1, + answers: [ + { + id: 'A', + correct: true, + }], + }); + }); + }); + }); +}); diff --git a/src/editors/data/redux/problem/selectors.js b/src/editors/data/redux/problem/selectors.js index 532bc5289..a9c267600 100644 --- a/src/editors/data/redux/problem/selectors.js +++ b/src/editors/data/redux/problem/selectors.js @@ -6,6 +6,7 @@ const mkSimpleSelector = (cb) => createSelector([module.problemState], cb); export const simpleSelectors = { problemType: mkSimpleSelector(problemData => problemData.problemType), answers: mkSimpleSelector(problemData => problemData.answers), + correctAnswerCount: mkSimpleSelector(problemData => problemData.correctAnswerCount), settings: mkSimpleSelector(problemData => problemData.settings), question: mkSimpleSelector(problemData => problemData.question), completeState: mkSimpleSelector(problemData => problemData), diff --git a/src/editors/data/redux/problem/selectors.test.js b/src/editors/data/redux/problem/selectors.test.js new file mode 100644 index 000000000..b4d36bb13 --- /dev/null +++ b/src/editors/data/redux/problem/selectors.test.js @@ -0,0 +1,52 @@ +// import * in order to mock in-file references +import { keyStore } from '../../../utils'; +import * as selectors from './selectors'; + +jest.mock('reselect', () => ({ + createSelector: jest.fn((preSelectors, cb) => ({ preSelectors, cb })), +})); + +const testState = { some: 'arbitraryValue' }; +const testValue = 'my VALUE'; + +describe('problem selectors unit tests', () => { + const { + problemState, + simpleSelectors, + } = selectors; + describe('problemState', () => { + it('returns the problem data', () => { + expect(problemState({ ...testState, problem: testValue })).toEqual(testValue); + }); + }); + describe('simpleSelectors', () => { + const testSimpleSelector = (key) => { + test(`${key} simpleSelector returns its value from the problem store`, () => { + const { preSelectors, cb } = simpleSelectors[key]; + expect(preSelectors).toEqual([problemState]); + expect(cb({ ...testState, [key]: testValue })).toEqual(testValue); + }); + }; + const simpleKeys = keyStore(simpleSelectors); + describe('simple selectors link their values from problem store', () => { + [ + simpleKeys.problemType, + simpleKeys.answers, + simpleKeys.correctAnswerCount, + simpleKeys.settings, + simpleKeys.question, + ].map(testSimpleSelector); + }); + test('simple selector completeState equals the entire state', () => { + const { preSelectors, cb } = simpleSelectors[simpleKeys.completeState]; + expect(preSelectors).toEqual([problemState]); + expect(cb({ + ...testState, + [simpleKeys.completeState]: testValue, + })).toEqual({ + ...testState, + [simpleKeys.completeState]: testValue, + }); + }); + }); +}); From 9ba0da04c3e66faf187db4bc70c1913ac5b4db5b Mon Sep 17 00:00:00 2001 From: Ken Clary Date: Tue, 20 Dec 2022 12:05:11 -0500 Subject: [PATCH 03/29] feat: confirmation dialog for closing any editor. TNL-10280. --- .../__snapshots__/index.test.jsx.snap | 102 ++++++++++++------ .../containers/EditorContainer/hooks.js | 22 +++- .../containers/EditorContainer/hooks.test.jsx | 46 +++++++- .../containers/EditorContainer/index.jsx | 40 +++++-- .../containers/EditorContainer/index.test.jsx | 23 ++-- .../containers/EditorContainer/messages.js | 19 ++++ .../SelectTypeWrapper/index.jsx | 6 +- .../SelectTypeWrapper/index.test.jsx | 6 +- 8 files changed, 203 insertions(+), 61 deletions(-) create mode 100644 src/editors/containers/EditorContainer/messages.js diff --git a/src/editors/containers/EditorContainer/__snapshots__/index.test.jsx.snap b/src/editors/containers/EditorContainer/__snapshots__/index.test.jsx.snap index abc5ee84b..368526098 100644 --- a/src/editors/containers/EditorContainer/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/EditorContainer/__snapshots__/index.test.jsx.snap @@ -1,7 +1,40 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`EditorContainer component render snapshot: initialized. enable save and pass to header 1`] = ` -
+
+ + + + } + footerAction={null} + isOpen={false} + size="md" + title="Exit the editor?" + > + + @@ -23,13 +56,7 @@ exports[`EditorContainer component render snapshot: initialized. enable save and >
@@ -40,13 +67,7 @@ exports[`EditorContainer component render snapshot: initialized. enable save and +
+ + + + } + footerAction={null} + isOpen={false} + size="md" + title="Exit the editor?" + > + + @@ -83,13 +137,7 @@ exports[`EditorContainer component render snapshot: not initialized. disable sav >
@@ -97,13 +145,7 @@ exports[`EditorContainer component render snapshot: not initialized. disable sav useState(val), +}); + export const handleSaveClicked = ({ dispatch, getContent, validateEntry }) => { const destination = useSelector(selectors.app.returnUrl); const analytics = useSelector(selectors.app.analytics); @@ -23,7 +30,16 @@ export const handleSaveClicked = ({ dispatch, getContent, validateEntry }) => { validateEntry, }); }; -export const handleCancelClicked = ({ onClose }) => { +export const cancelConfirmModalToggle = () => { + const [isCancelConfirmOpen, setIsOpen] = module.state.isCancelConfirmModalOpen(false); + return { + isCancelConfirmOpen, + openCancelConfirmModal: () => setIsOpen(true), + closeCancelConfirmModal: () => setIsOpen(false), + }; +}; + +export const handleCancel = ({ onClose }) => { if (onClose) { return onClose; } @@ -34,6 +50,6 @@ export const handleCancelClicked = ({ onClose }) => { }); }; export const isInitialized = () => useSelector(selectors.app.isInitialized); -export const saveFailed = () => useSelector((state) => ( - selectors.requests.isFailed(state, { requestKey: RequestKeys.saveBlock }) +export const saveFailed = () => useSelector((rootState) => ( + selectors.requests.isFailed(rootState, { requestKey: RequestKeys.saveBlock }) )); diff --git a/src/editors/containers/EditorContainer/hooks.test.jsx b/src/editors/containers/EditorContainer/hooks.test.jsx index 098302fba..01b9412a7 100644 --- a/src/editors/containers/EditorContainer/hooks.test.jsx +++ b/src/editors/containers/EditorContainer/hooks.test.jsx @@ -1,4 +1,5 @@ import * as reactRedux from 'react-redux'; +import { MockUseState } from '../../../testUtils'; import { RequestKeys } from '../../data/constants/requests'; import { selectors } from '../../data/redux'; @@ -7,6 +8,8 @@ import * as appHooks from '../../hooks'; import * as hooks from './hooks'; import analyticsEvt from '../../data/constants/analyticsEvt'; +const hookState = new MockUseState(hooks); + jest.mock('../../data/redux', () => ({ selectors: { app: { @@ -67,9 +70,46 @@ describe('EditorContainer hooks', () => { }); }); }); - describe('handleCancelClicked', () => { + + describe('cancelConfirmModalToggle', () => { + const hookKey = hookState.keys.isCancelConfirmModalOpen; + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('state hook', () => { + hookState.testGetter(hookKey); + }); + describe('using state', () => { + beforeEach(() => { + hookState.mock(); + }); + afterEach(() => { + hookState.restore(); + }); + + describe('cancelConfirmModalToggle', () => { + let hook; + beforeEach(() => { + hook = hooks.cancelConfirmModalToggle(); + }); + test('isCancelConfirmOpen: state value', () => { + expect(hook.isCancelConfirmOpen).toEqual(hookState.stateVals[hookKey]); + }); + test('openCancelConfirmModal: calls setter with true', () => { + hook.openCancelConfirmModal(); + expect(hookState.setState[hookKey]).toHaveBeenCalledWith(true); + }); + test('closeCancelConfirmModal: calls setter with false', () => { + hook.closeCancelConfirmModal(); + expect(hookState.setState[hookKey]).toHaveBeenCalledWith(false); + }); + }); + }); + }); + + describe('handleCancel', () => { it('calls navigateCallback to returnUrl if onClose is not passed', () => { - expect(hooks.handleCancelClicked({})).toEqual( + expect(hooks.handleCancel({})).toEqual( appHooks.navigateCallback({ destination: reactRedux.useSelector(selectors.app.returnUrl), analyticsEvent: analyticsEvt.editorCancelClick, @@ -79,7 +119,7 @@ describe('EditorContainer hooks', () => { }); it('calls onClose and not navigateCallback if onClose is passed', () => { const onClose = () => 'my close value'; - expect(hooks.handleCancelClicked({ onClose })).toEqual(onClose); + expect(hooks.handleCancel({ onClose })).toEqual(onClose); expect(appHooks.navigateCallback).not.toHaveBeenCalled(); }); }); diff --git a/src/editors/containers/EditorContainer/index.jsx b/src/editors/containers/EditorContainer/index.jsx index b8e5fc9f4..21d7edd2b 100644 --- a/src/editors/containers/EditorContainer/index.jsx +++ b/src/editors/containers/EditorContainer/index.jsx @@ -2,24 +2,50 @@ import React from 'react'; import { useDispatch } from 'react-redux'; import PropTypes from 'prop-types'; -import { Icon, ModalDialog, IconButton } from '@edx/paragon'; +import { + Icon, ModalDialog, IconButton, Button, +} from '@edx/paragon'; import { Close } from '@edx/paragon/icons'; +import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n'; import EditorFooter from './components/EditorFooter'; import TitleHeader from './components/TitleHeader'; import * as hooks from './hooks'; +import BaseModal from '../TextEditor/components/BaseModal'; +import messages from './messages'; export const EditorContainer = ({ children, getContent, onClose, validateEntry, + // injected + intl, }) => { const dispatch = useDispatch(); const isInitialized = hooks.isInitialized(); - const handleCancelClicked = hooks.handleCancelClicked({ onClose }); + const { isCancelConfirmOpen, openCancelConfirmModal, closeCancelConfirmModal } = hooks.cancelConfirmModalToggle(); + const handleCancel = hooks.handleCancel({ onClose }); return ( -
+
+ + + + )} + isOpen={isCancelConfirmOpen} + close={closeCancelConfirmModal} + title={intl.formatMessage(messages.cancelConfirmTitle)} + > + +
{isInitialized && children} ({ isInitialized: jest.fn().mockReturnValue(true), - handleCancelClicked: (args) => ({ handleCancelClicked: args }), + handleCancel: (args) => ({ handleCancel: args }), handleSaveClicked: (args) => ({ handleSaveClicked: args }), saveFailed: jest.fn().mockName('hooks.saveFailed'), + cancelConfirmModalToggle: jest.fn(() => ({ + isCancelConfirmOpen: false, + openCancelConfirmModal: jest.fn().mockName('openCancelConfirmModal'), + closeCancelConfirmModal: jest.fn().mockName('closeCancelConfirmModal'), + })), })); let el; @@ -39,23 +46,13 @@ describe('EditorContainer component', () => { el = shallow({testContent}); }); - test('close behavior is linked to modal onClose', () => { - const expected = hooks.handleCancelClicked({ onClose: props.onClose }); - expect(el.find(IconButton) - .props().onClick).toEqual(expected); - }); - test('close behavior is linked to footer onCancel', () => { - const expected = hooks.handleCancelClicked({ onClose: props.onClose }); - expect(el.children().at(2) - .props().onCancel).toEqual(expected); - }); test('save behavior is linked to footer onSave', () => { const expected = hooks.handleSaveClicked({ dispatch: useDispatch(), getContent: props.getContent, validateEntry: props.validateEntry, }); - expect(el.children().at(2) + expect(el.children().at(3) .props().onSave).toEqual(expected); }); }); diff --git a/src/editors/containers/EditorContainer/messages.js b/src/editors/containers/EditorContainer/messages.js new file mode 100644 index 000000000..c9eb65dce --- /dev/null +++ b/src/editors/containers/EditorContainer/messages.js @@ -0,0 +1,19 @@ +export const messages = { + cancelConfirmTitle: { + id: 'authoring.editorContainer.cancelConfirm.title', + defaultMessage: 'Exit the editor?', + description: 'Label for modal confirming cancellation', + }, + cancelConfirmDescription: { + id: 'authoring.editorContainer.cancelConfirm.description', + defaultMessage: 'Are you sure you want to exit the editor? Any unsaved changes will be lost.', + description: 'Description text for modal confirming cancellation', + }, + okButtonLabel: { + id: 'authoring.editorContainer.okButton.label', + defaultMessage: 'OK', + description: 'Label for OK button', + }, +}; + +export default messages; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx index b8c63786d..edc54d6c4 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx @@ -12,7 +12,7 @@ export const SelectTypeWrapper = ({ onClose, selected, }) => { - const handleCancelClicked = hooks.handleCancelClicked({ onClose }); + const handleCancel = hooks.handleCancel({ onClose }); return (
@@ -23,7 +23,7 @@ export const SelectTypeWrapper = ({
@@ -33,7 +33,7 @@ export const SelectTypeWrapper = ({
); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx index 342d06ced..e5e6c2c49 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx @@ -2,10 +2,10 @@ import React from 'react'; import { shallow } from 'enzyme'; import { IconButton } from '@edx/paragon'; import * as module from '.'; -import { handleCancelClicked } from '../../../../EditorContainer/hooks'; +import { handleCancel } from '../../../../EditorContainer/hooks'; jest.mock('../../../../EditorContainer/hooks', () => ({ - handleCancelClicked: jest.fn().mockName('handleCancelClicked'), + handleCancel: jest.fn().mockName('handleCancel'), })); describe('SelectTypeWrapper', () => { @@ -25,7 +25,7 @@ describe('SelectTypeWrapper', () => { el = shallow(); }); test('close behavior is linked to modal onClose', () => { - const expected = handleCancelClicked({ onClose: props.onClose }); + const expected = handleCancel({ onClose: props.onClose }); expect(el.find(IconButton).props().onClick) .toEqual(expected); }); From f81b0ee925a4355e1f2c54491597769a4587938b Mon Sep 17 00:00:00 2001 From: connorhaugh <49422820+connorhaugh@users.noreply.github.com> Date: Wed, 11 Jan 2023 10:25:25 -0500 Subject: [PATCH 04/29] docs: add period to module.config.js (#183) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f80760868..81796b73c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This guide presumes you have a functioning devstack. 2. In devstack + venv, run `$ make dev.provision.lms+studio+frontend-app-course-authoring` to make up the required services. Minimum services required are lms, studio and frontend-app-course-authoring. 4. In [Studio Django Admin](http://localhost:18000/admin/waffle/flag/) turn on `new_core_editors.use_new_text_editor` flag for HTML editor, `new_core_editors.use_new_video_editor` flag for new video editor, and `new_core_editors.use_new_problem_editor` flag for problems. The list of supported flags is in [toggles.py. ](https://github.com/openedx/edx-platform/blob/master/cms/djangoapps/contentstore/toggles.py). you might have to add a flag for your xblock of choice. 2. Clone this repo into the [`${DEVSTACK_WORKSPACE}/src` directory](https://edx.readthedocs.io/projects/open-edx-devstack/en/latest/readme.html?highlight=DEVSTACK_WORKSPACE#id9) the sibling repo of your edx devstack `/src`. -3. In the course authoring app, follow the guide to use your [local verison of frontend-lib-content-components. ](https://github.com/openedx/frontend-build#local-module-configuration-for-webpack) The moduleconfig.js in the frontend-app-course-authoring repo will be: +3. In the course authoring app, follow the guide to use your [local verison of frontend-lib-content-components. ](https://github.com/openedx/frontend-build#local-module-configuration-for-webpack) The module.config.js in the frontend-app-course-authoring repo will be: ``` module.exports = { From 2c6679fe06ffd4b6e248ef8de09bbfc05fe24d09 Mon Sep 17 00:00:00 2001 From: Jesper Hodge <19345795+jesperhodge@users.noreply.github.com> Date: Wed, 11 Jan 2023 14:23:06 -0500 Subject: [PATCH 05/29] Feat raw olx editing. TNL-10218 (#182) * refactor: move CodeEditor to shared components and remove circular dependency * feat: add code editor to problem editor * fix: typo * feat: add save function to raw olx editor and add highlighting * feat: simplify and add tests to edit problem view * feat: add tests to problem edit view * fix: update raw editor tests * fix: code editor tests * fix: package-lock * fix: lint --- package-lock.json | 1820 +++++------------ package.json | 1 + .../__snapshots__/index.test.jsx.snap | 66 + .../components/EditProblemView/hooks.js | 14 + .../components/EditProblemView/hooks.test.js | 25 + .../components/EditProblemView/index.jsx | 35 +- .../components/EditProblemView/index.test.jsx | 20 + .../__snapshots__/index.test.jsx.snap | 15 +- .../components/CodeEditor/index.jsx | 109 - .../TextEditor/components/RawEditor/index.jsx | 38 - .../components/SourceCodeModal/index.jsx | 2 +- src/editors/containers/TextEditor/index.jsx | 4 +- .../__snapshots__/index.test.jsx.snap | 0 .../CodeEditor/constants.js | 0 .../sharedComponents/CodeEditor/hooks.js | 71 + .../sharedComponents/CodeEditor/index.jsx | 55 + .../CodeEditor/index.scss | 0 .../CodeEditor/index.test.jsx | 25 +- .../CodeEditor/messages.js | 0 .../__snapshots__/index.test.jsx.snap | 5 +- .../sharedComponents/RawEditor/index.jsx | 55 + .../RawEditor/index.test.jsx | 2 +- 22 files changed, 830 insertions(+), 1532 deletions(-) create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/__snapshots__/index.test.jsx.snap create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/hooks.js create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/hooks.test.js create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx delete mode 100644 src/editors/containers/TextEditor/components/CodeEditor/index.jsx delete mode 100644 src/editors/containers/TextEditor/components/RawEditor/index.jsx rename src/editors/{containers/TextEditor/components => sharedComponents}/CodeEditor/__snapshots__/index.test.jsx.snap (100%) rename src/editors/{containers/TextEditor/components => sharedComponents}/CodeEditor/constants.js (100%) create mode 100644 src/editors/sharedComponents/CodeEditor/hooks.js create mode 100644 src/editors/sharedComponents/CodeEditor/index.jsx rename src/editors/{containers/TextEditor/components => sharedComponents}/CodeEditor/index.scss (100%) rename src/editors/{containers/TextEditor/components => sharedComponents}/CodeEditor/index.test.jsx (81%) rename src/editors/{containers/TextEditor/components => sharedComponents}/CodeEditor/messages.js (100%) rename src/editors/{containers/TextEditor/components => sharedComponents}/RawEditor/__snapshots__/index.test.jsx.snap (87%) create mode 100644 src/editors/sharedComponents/RawEditor/index.jsx rename src/editors/{containers/TextEditor/components => sharedComponents}/RawEditor/index.test.jsx (87%) diff --git a/package-lock.json b/package-lock.json index 1c73363eb..62c55aa30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "AGPL-3.0", "dependencies": { "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-xml": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", "@reduxjs/toolkit": "^1.8.1", @@ -192,15 +193,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/code-frame/node_modules/@babel/highlight": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", @@ -437,24 +429,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/core/node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/core/node_modules/@babel/helper-validator-option": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", @@ -478,18 +452,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/core/node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/core/node_modules/@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -525,20 +487,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/core/node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/core/node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -638,6 +586,36 @@ "node": ">=0.10.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", + "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/plugin-proposal-class-properties": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.0.tgz", @@ -796,36 +774,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -861,20 +809,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-proposal-class-properties/node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -1444,24 +1378,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/preset-env/node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/preset-env/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/preset-env/node_modules/@babel/helper-validator-option": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", @@ -1486,18 +1402,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/preset-env/node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/preset-env/node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", @@ -2466,20 +2370,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/preset-env/node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/preset-env/node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -2722,24 +2612,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/preset-react/node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/preset-react/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/preset-react/node_modules/@babel/helper-validator-option": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", @@ -2829,20 +2701,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-react/node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/runtime": { "version": "7.20.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", @@ -2854,6 +2712,20 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/types": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@codemirror/autocomplete": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.4.0.tgz", @@ -2912,14 +2784,6 @@ "@lezer/javascript": "^1.0.0" } }, - "node_modules/@codemirror/lang-javascript/node_modules/@lezer/highlight": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz", - "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, "node_modules/@codemirror/lang-javascript/node_modules/@lezer/javascript": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.0.tgz", @@ -2929,12 +2793,16 @@ "@lezer/lr": "^1.0.0" } }, - "node_modules/@codemirror/lang-javascript/node_modules/@lezer/lr": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.5.tgz", - "integrity": "sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==", + "node_modules/@codemirror/lang-xml": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.0.1.tgz", + "integrity": "sha512-0tvycUTElajCcRKgsszhKjWX+uuOogdu5+enpfqYA+j0gnP8ek7LRxujh2/XMPRdXt/hwOML4slJLE7r2eX3yQ==", "dependencies": { - "@lezer/common": "^1.0.0" + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" } }, "node_modules/@codemirror/language": { @@ -2950,22 +2818,6 @@ "style-mod": "^4.0.0" } }, - "node_modules/@codemirror/language/node_modules/@lezer/highlight": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz", - "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@codemirror/language/node_modules/@lezer/lr": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.5.tgz", - "integrity": "sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, "node_modules/@codemirror/language/node_modules/style-mod": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz", @@ -3288,12 +3140,6 @@ "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", "dev": true }, - "node_modules/@edx/frontend-build/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "node_modules/@edx/frontend-build/node_modules/@types/webpack": { "version": "5.28.0", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz", @@ -4567,23 +4413,6 @@ "integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==", "dev": true }, - "node_modules/@edx/paragon/node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true - }, - "node_modules/@edx/paragon/node_modules/@types/react": { - "version": "18.0.26", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", - "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", - "dev": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, "node_modules/@edx/paragon/node_modules/@types/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -5242,7 +5071,7 @@ "@lezer/lr": "^1.0.0" } }, - "node_modules/@lezer/css/node_modules/@lezer/highlight": { + "node_modules/@lezer/highlight": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz", "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", @@ -5250,14 +5079,6 @@ "@lezer/common": "^1.0.0" } }, - "node_modules/@lezer/css/node_modules/@lezer/lr": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.5.tgz", - "integrity": "sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, "node_modules/@lezer/html": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.0.tgz", @@ -5268,20 +5089,21 @@ "@lezer/lr": "^1.0.0" } }, - "node_modules/@lezer/html/node_modules/@lezer/highlight": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz", - "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", + "node_modules/@lezer/lr": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.0.tgz", + "integrity": "sha512-rpvS+WPS/PlbJCiW+bzXPbIFIRXmzRiTEDzMvrvgpED05w5ZQO59AzH3BJen2AnHuJIlP3DcJRjsKLTrkknUNA==", "dependencies": { "@lezer/common": "^1.0.0" } }, - "node_modules/@lezer/html/node_modules/@lezer/lr": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.5.tgz", - "integrity": "sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==", + "node_modules/@lezer/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-jMDXrV953sDAUEMI25VNrI9dz94Ai96FfeglytFINhhwQ867HKlCE2jt3AwZTCT7M528WxdDWv/Ty8e9wizwmQ==", "dependencies": { - "@lezer/common": "^1.0.0" + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" } }, "node_modules/@mapbox/node-pre-gyp": { @@ -5635,10 +5457,36 @@ } }, "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "dev": true + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.5.0.tgz", + "integrity": "sha512-8zYdkym7qNyfXpWvu4yq46k41pyNM9SOstoWhKlm+IfdCE1DdnRKeMUPsWIEO/DEkaWxJ8T9esNdG3QwQ93jBA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "dev": true + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.5.0.tgz", + "integrity": "sha512-NFdxMq3xA42Kb1UbzCVxplUc0iqSyM9X8kopImvFnB+uSDdzIHOdbs1op8ofAvVRtbg4oZiyRl3fTYeKcOe9Iw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, "node_modules/@svgr/webpack": { "version": "6.2.1", @@ -5931,24 +5779,6 @@ "node": ">=6.9.0" } }, - "node_modules/@svgr/webpack/node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@svgr/webpack/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@svgr/webpack/node_modules/@babel/helper-validator-option": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", @@ -5972,18 +5802,6 @@ "node": ">=6.9.0" } }, - "node_modules/@svgr/webpack/node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@svgr/webpack/node_modules/@babel/plugin-syntax-typescript": { "version": "7.20.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", @@ -6083,20 +5901,6 @@ "node": ">=6.9.0" } }, - "node_modules/@svgr/webpack/node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@svgr/webpack/node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -6585,15 +6389,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/@svgr/webpack/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@svgr/webpack/node_modules/svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", @@ -6702,16 +6497,42 @@ "dev": true }, "node_modules/@types/babel__generator": { - "dev": true + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } }, "node_modules/@types/babel__template": { - "dev": true + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } }, "node_modules/@types/babel__traverse": { - "dev": true + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } }, "node_modules/@types/body-parser": { - "dev": true + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } }, "node_modules/@types/cheerio": { "version": "0.22.31", @@ -6722,41 +6543,151 @@ "@types/node": "*" } }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/eslint": { + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", "dev": true }, "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", "dev": true }, "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "dev": true }, "node_modules/@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, "node_modules/@types/prop-types": { - "dev": true + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", "dev": true }, "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, - "node_modules/@types/react": {}, + "node_modules/@types/react": { + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/scheduler": { - "dev": true + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", "dev": true }, "node_modules/@types/uglify-js": { - "dev": true + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.1.tgz", + "integrity": "sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g==", + "dev": true, + "dependencies": { + "source-map": "^0.6.1" + } }, "node_modules/@types/webpack-sources": { - "dev": true + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-3.2.0.tgz", + "integrity": "sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.7.3" + } + }, + "node_modules/@types/webpack-sources/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } }, "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, "node_modules/@videojs/http-streaming": { @@ -8333,36 +8264,6 @@ "node": ">=6.9.0" } }, - "node_modules/babel-eslint/node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/babel-eslint/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/babel-eslint/node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/babel-eslint/node_modules/@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -8398,20 +8299,6 @@ "node": ">=6.9.0" } }, - "node_modules/babel-eslint/node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/babel-eslint/node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -8512,50 +8399,6 @@ "node": ">=6.9.0" } }, - "node_modules/babel-jest/node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/babel-jest/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/babel-jest/node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/babel-jest/node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/babel-jest/node_modules/@cnakazawa/watch": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", @@ -8652,12 +8495,6 @@ "@types/node": "*" } }, - "node_modules/babel-jest/node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, "node_modules/babel-jest/node_modules/@types/istanbul-reports": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", @@ -9580,15 +9417,6 @@ "node": ">=0.10.0" } }, - "node_modules/babel-jest/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/babel-jest/node_modules/source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -9705,12 +9533,6 @@ "webpack": ">=2" } }, - "node_modules/babel-loader/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "node_modules/babel-loader/node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -9903,50 +9725,6 @@ "node": ">=6.9.0" } }, - "node_modules/babel-plugin-react-intl/node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/babel-plugin-react-intl/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/babel-plugin-react-intl/node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/babel-plugin-react-intl/node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/babel-plugin-react-intl/node_modules/@formatjs/ecma402-abstract": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz", @@ -10027,12 +9805,6 @@ "@types/node": "*" } }, - "node_modules/babel-plugin-react-intl/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "node_modules/babel-plugin-react-intl/node_modules/@types/schema-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/schema-utils/-/schema-utils-2.4.0.tgz", @@ -10111,38 +9883,6 @@ "is-valid-path": "^0.1.1" } }, - "node_modules/babel-plugin-transform-imports/node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/babel-plugin-transform-imports/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/babel-plugin-transform-imports/node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/babel-polyfill": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", @@ -10204,36 +9944,6 @@ "node": ">=6.9.0" } }, - "node_modules/babel-preset-jest/node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/babel-preset-jest/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/babel-preset-jest/node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/babel-preset-jest/node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -10395,20 +10105,6 @@ "node": ">=6.9.0" } }, - "node_modules/babel-preset-jest/node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/babel-preset-jest/node_modules/@types/babel__core": { "version": "7.1.20", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", @@ -10422,15 +10118,6 @@ "@types/babel__traverse": "*" } }, - "node_modules/babel-preset-jest/node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.3.0" - } - }, "node_modules/babel-preset-jest/node_modules/babel-plugin-jest-hoist": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", @@ -11247,15 +10934,6 @@ "source-map": "^0.6.0" } }, - "node_modules/clean-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -11594,12 +11272,6 @@ "webpack": "^4.27.0 || ^5.0.0" } }, - "node_modules/css-loader/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "node_modules/css-loader/node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -12496,15 +12168,6 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, - "node_modules/cssnano/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/cssnano/node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -14755,12 +14418,6 @@ "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/file-loader/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "node_modules/file-loader/node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -16695,15 +16352,6 @@ "strip-ansi": "^6.0.1" } }, - "node_modules/html-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/html-webpack-plugin/node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -16946,12 +16594,6 @@ "@types/node": "*" } }, - "node_modules/image-webpack-loader/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "node_modules/image-webpack-loader/node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -20688,16 +20330,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/imagemin-svgo/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/imagemin-svgo/node_modules/svgo": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", @@ -22789,11 +22421,6 @@ } } }, - "node_modules/jest-resolve": { - "dev": true, - "optional": true, - "peer": true - }, "node_modules/jest/node_modules/@babel/generator": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", @@ -22863,36 +22490,6 @@ "node": ">=6.9.0" } }, - "node_modules/jest/node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/jest/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/jest/node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/jest/node_modules/@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -22928,20 +22525,6 @@ "node": ">=6.9.0" } }, - "node_modules/jest/node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/jest/node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", @@ -23291,15 +22874,6 @@ "node": ">= 6" } }, - "node_modules/jest/node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.3.0" - } - }, "node_modules/jest/node_modules/@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -23309,12 +22883,6 @@ "@types/node": "*" } }, - "node_modules/jest/node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, "node_modules/jest/node_modules/@types/istanbul-reports": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", @@ -25560,15 +25128,6 @@ "node": ">=0.10.0" } }, - "node_modules/jest/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/jest/node_modules/source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -26501,12 +26060,6 @@ "webpack": "^4.4.0 || ^5.0.0" } }, - "node_modules/mini-css-extract-plugin/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -26566,15 +26119,6 @@ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", "dev": true }, - "node_modules/mini-css-extract-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/mini-css-extract-plugin/node_modules/webpack-sources": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", @@ -28742,12 +28286,6 @@ "node": ">= 8" } }, - "node_modules/react-dev-utils/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "node_modules/react-dev-utils/node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -29440,17 +28978,6 @@ "hoist-non-react-statics": "^3.3.0" } }, - "node_modules/react-intl/node_modules/@types/react": { - "version": "18.0.26", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", - "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", - "dev": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, "node_modules/react-intl/node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -30009,15 +29536,6 @@ "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", "dev": true }, - "node_modules/resolve-url-loader/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve/node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -30641,6 +30159,15 @@ "node": ">=0.10.0" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-loader": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", @@ -31292,12 +30819,6 @@ "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/style-loader/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "node_modules/style-loader/node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -32038,12 +31559,6 @@ } } }, - "node_modules/url-loader/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "node_modules/url-loader/node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -32814,12 +32329,6 @@ "@types/node": "*" } }, - "node_modules/webpack-dev-server/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "node_modules/webpack-dev-server/node_modules/@types/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", @@ -34387,13 +33896,6 @@ "dev": true, "peer": true }, - "node_modules/webpack/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true, - "peer": true - }, "node_modules/webpack/node_modules/@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -34694,16 +34196,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/webpack/node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -35144,12 +34636,6 @@ "@babel/highlight": "^7.18.6" }, "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, "@babel/highlight": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", @@ -35333,18 +34819,6 @@ "@babel/types": "^7.18.6" } }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, "@babel/helper-validator-option": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", @@ -35362,12 +34836,6 @@ "@babel/types": "^7.20.5" } }, - "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true - }, "@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -35397,17 +34865,6 @@ "globals": "^11.1.0" } }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, "@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -35479,6 +34936,24 @@ } } }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/parser": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", + "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", + "dev": true + }, "@babel/plugin-proposal-class-properties": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.0.tgz", @@ -35595,24 +35070,6 @@ "@babel/types": "^7.18.6" } }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true - }, "@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -35642,17 +35099,6 @@ "globals": "^11.1.0" } }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, "@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -36090,18 +35536,6 @@ "@babel/types": "^7.18.6" } }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, "@babel/helper-validator-option": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", @@ -36120,12 +35554,6 @@ "@babel/types": "^7.20.5" } }, - "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true - }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", @@ -36755,17 +36183,6 @@ "globals": "^11.1.0" } }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, "@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -36951,18 +36368,6 @@ "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "dev": true }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, "@babel/helper-validator-option": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", @@ -37018,17 +36423,6 @@ "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" } - }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } } } }, @@ -37040,6 +36434,17 @@ "regenerator-runtime": "^0.13.11" } }, + "@babel/types": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, "@codemirror/autocomplete": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.4.0.tgz", @@ -37092,14 +36497,6 @@ "@lezer/javascript": "^1.0.0" }, "dependencies": { - "@lezer/highlight": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz", - "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, "@lezer/javascript": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.0.tgz", @@ -37108,17 +36505,21 @@ "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0" } - }, - "@lezer/lr": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.5.tgz", - "integrity": "sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==", - "requires": { - "@lezer/common": "^1.0.0" - } } } }, + "@codemirror/lang-xml": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.0.1.tgz", + "integrity": "sha512-0tvycUTElajCcRKgsszhKjWX+uuOogdu5+enpfqYA+j0gnP8ek7LRxujh2/XMPRdXt/hwOML4slJLE7r2eX3yQ==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" + } + }, "@codemirror/language": { "version": "6.3.2", "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.3.2.tgz", @@ -37132,22 +36533,6 @@ "style-mod": "^4.0.0" }, "dependencies": { - "@lezer/highlight": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz", - "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@lezer/lr": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.5.tgz", - "integrity": "sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, "style-mod": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz", @@ -37393,12 +36778,6 @@ "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", "dev": true }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "@types/webpack": { "version": "5.28.0", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz", @@ -38384,23 +37763,6 @@ "integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==", "dev": true }, - "@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true - }, - "@types/react": { - "version": "18.0.26", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", - "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", - "dev": true, - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, "@types/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -38875,24 +38237,14 @@ "requires": { "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0" - }, - "dependencies": { - "@lezer/highlight": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz", - "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@lezer/lr": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.5.tgz", - "integrity": "sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==", - "requires": { - "@lezer/common": "^1.0.0" - } - } + } + }, + "@lezer/highlight": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz", + "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", + "requires": { + "@lezer/common": "^1.0.0" } }, "@lezer/html": { @@ -38903,24 +38255,23 @@ "@lezer/common": "^1.0.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0" - }, - "dependencies": { - "@lezer/highlight": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz", - "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@lezer/lr": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.5.tgz", - "integrity": "sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==", - "requires": { - "@lezer/common": "^1.0.0" - } - } + } + }, + "@lezer/lr": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.0.tgz", + "integrity": "sha512-rpvS+WPS/PlbJCiW+bzXPbIFIRXmzRiTEDzMvrvgpED05w5ZQO59AzH3BJen2AnHuJIlP3DcJRjsKLTrkknUNA==", + "requires": { + "@lezer/common": "^1.0.0" + } + }, + "@lezer/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-jMDXrV953sDAUEMI25VNrI9dz94Ai96FfeglytFINhhwQ867HKlCE2jt3AwZTCT7M528WxdDWv/Ty8e9wizwmQ==", + "requires": { + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" } }, "@mapbox/node-pre-gyp": { @@ -39195,10 +38546,18 @@ } }, "@svgr/babel-plugin-remove-jsx-attribute": { - "dev": true + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.5.0.tgz", + "integrity": "sha512-8zYdkym7qNyfXpWvu4yq46k41pyNM9SOstoWhKlm+IfdCE1DdnRKeMUPsWIEO/DEkaWxJ8T9esNdG3QwQ93jBA==", + "dev": true, + "requires": {} }, "@svgr/babel-plugin-remove-jsx-empty-expression": { - "dev": true + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.5.0.tgz", + "integrity": "sha512-NFdxMq3xA42Kb1UbzCVxplUc0iqSyM9X8kopImvFnB+uSDdzIHOdbs1op8ofAvVRtbg4oZiyRl3fTYeKcOe9Iw==", + "dev": true, + "requires": {} }, "@svgr/webpack": { "version": "6.2.1", @@ -39419,18 +38778,6 @@ "@babel/types": "^7.18.6" } }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, "@babel/helper-validator-option": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", @@ -39448,12 +38795,6 @@ "@babel/types": "^7.20.5" } }, - "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true - }, "@babel/plugin-syntax-typescript": { "version": "7.20.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", @@ -39523,17 +38864,6 @@ "globals": "^11.1.0" } }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, "@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -39842,12 +39172,6 @@ "boolbase": "^1.0.0" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", @@ -39928,16 +39252,42 @@ "dev": true }, "@types/babel__generator": { - "dev": true + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } }, "@types/babel__template": { - "dev": true + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } }, "@types/babel__traverse": { - "dev": true + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } }, "@types/body-parser": { - "dev": true + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } }, "@types/cheerio": { "version": "0.22.31", @@ -39948,41 +39298,150 @@ "@types/node": "*" } }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/eslint": { + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", "dev": true }, "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", "dev": true }, "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "dev": true }, "@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, "@types/prop-types": { - "dev": true + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", "dev": true }, "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, - "@types/react": {}, + "@types/react": { + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, "@types/scheduler": { - "dev": true + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", "dev": true }, "@types/uglify-js": { - "dev": true + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.1.tgz", + "integrity": "sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + } }, "@types/webpack-sources": { - "dev": true + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-3.2.0.tgz", + "integrity": "sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + } + } }, "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, "@videojs/http-streaming": { @@ -41126,24 +40585,6 @@ "@babel/types": "^7.18.6" } }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true - }, "@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -41173,17 +40614,6 @@ "globals": "^11.1.0" } }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, "@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -41259,35 +40689,6 @@ "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "dev": true }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true - }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, "@cnakazawa/watch": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", @@ -41369,12 +40770,6 @@ "@types/node": "*" } }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, "@types/istanbul-reports": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", @@ -42125,12 +41520,6 @@ } } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -42227,12 +41616,6 @@ "schema-utils": "^2.6.5" }, "dependencies": { - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -42375,35 +41758,6 @@ "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "dev": true }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true - }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, "@formatjs/ecma402-abstract": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz", @@ -42477,12 +41831,6 @@ "@types/node": "*" } }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "@types/schema-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/schema-utils/-/schema-utils-2.4.0.tgz", @@ -42547,31 +41895,6 @@ "requires": { "@babel/types": "^7.4", "is-valid-path": "^0.1.1" - }, - "dependencies": { - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - } } }, "babel-polyfill": { @@ -42628,24 +41951,6 @@ "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "dev": true }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true - }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -42765,17 +42070,6 @@ "@babel/types": "^7.18.10" } }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, "@types/babel__core": { "version": "7.1.20", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", @@ -42789,15 +42083,6 @@ "@types/babel__traverse": "*" } }, - "@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, "babel-plugin-jest-hoist": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", @@ -43418,12 +42703,6 @@ "anymatch": "^3.0.0", "source-map": "^0.6.0" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -43706,12 +42985,6 @@ "semver": "^7.3.5" }, "dependencies": { - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -44302,12 +43575,6 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -46041,12 +45308,6 @@ "schema-utils": "^3.0.0" }, "dependencies": { - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -47528,12 +46789,6 @@ "strip-ansi": "^6.0.1" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -47726,12 +46981,6 @@ "@types/node": "*" } }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -50736,13 +49985,6 @@ "boolbase": "^1.0.0" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - }, "svgo": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", @@ -52423,24 +51665,6 @@ "@babel/types": "^7.18.6" } }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "dev": true - }, "@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -52470,17 +51694,6 @@ "globals": "^11.1.0" } }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, "@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", @@ -52773,15 +51986,6 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, - "@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -52791,12 +51995,6 @@ "@types/node": "*" } }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, "@types/istanbul-reports": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", @@ -54588,12 +53786,6 @@ } } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -54931,11 +54123,6 @@ "dev": true, "requires": {} }, - "jest-resolve": { - "dev": true, - "optional": true, - "peer": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -55358,12 +54545,6 @@ "webpack-sources": "^1.1.0" }, "dependencies": { - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -55405,12 +54586,6 @@ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", "dev": true }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "webpack-sources": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", @@ -57027,12 +56202,6 @@ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -57555,17 +56724,6 @@ "hoist-non-react-statics": "^3.3.0" } }, - "@types/react": { - "version": "18.0.26", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", - "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", - "dev": true, - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, "hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -58037,12 +57195,6 @@ "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -58488,6 +57640,12 @@ "sort-keys": "^1.0.0" } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "source-map-loader": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", @@ -58974,12 +58132,6 @@ "schema-utils": "^3.0.0" }, "dependencies": { - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -59562,12 +58714,6 @@ "schema-utils": "^3.0.0" }, "dependencies": { - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -59862,13 +59008,6 @@ "dev": true, "peer": true }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true, - "peer": true - }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -60137,13 +59276,6 @@ "ajv-keywords": "^3.5.2" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "peer": true - }, "source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -60566,12 +59698,6 @@ "@types/node": "*" } }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, "@types/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", diff --git a/package.json b/package.json index 7b99ac4ab..555f11e50 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ }, "dependencies": { "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-xml": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", "@reduxjs/toolkit": "^1.8.1", diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/__snapshots__/index.test.jsx.snap new file mode 100644 index 000000000..2266b9fe0 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/__snapshots__/index.test.jsx.snap @@ -0,0 +1,66 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EditorProblemView component renders raw editor 1`] = ` + + + + + + + + + + + + +`; + +exports[`EditorProblemView component renders simple view 1`] = ` + + + + + + + + + + + + + +`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.js new file mode 100644 index 000000000..80298bc56 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.js @@ -0,0 +1,14 @@ +import ReactStateSettingsParser from '../../data/ReactStateSettingsParser'; +import ReactStateOLXParser from '../../data/ReactStateOLXParser'; + +// eslint-disable-next-line import/prefer-default-export +export const parseState = (problem, isAdvanced, ref) => () => { + const reactSettingsParser = new ReactStateSettingsParser(problem); + const reactOLXParser = new ReactStateOLXParser({ problem }); + const rawOLX = ref?.current?.state.doc.toString(); + + return { + settings: reactSettingsParser.getSettings(), + olx: isAdvanced ? rawOLX : reactOLXParser.buildOLX(), + }; +}; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.test.js new file mode 100644 index 000000000..9cfc8a42f --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.test.js @@ -0,0 +1,25 @@ +import * as hooks from './hooks'; + +const mockRawOLX = 'rawOLX'; +const mockBuiltOLX = 'builtOLX'; + +jest.mock('../../data/ReactStateOLXParser', () => ( + jest.fn().mockImplementation(() => ({ + buildOLX: () => mockBuiltOLX, + })) +)); +jest.mock('../../data/ReactStateSettingsParser'); + +describe('EditProblemView hooks parseState', () => { + const toStringMock = () => mockRawOLX; + const refMock = { current: { state: { doc: { toString: toStringMock } } } }; + + test('default problem', () => { + const res = hooks.parseState('problem', false, refMock)(); + expect(res.olx).toBe(mockBuiltOLX); + }); + test('advanced problem', () => { + const res = hooks.parseState('problem', true, refMock)(); + expect(res.olx).toBe(mockRawOLX); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx index d832f05d4..e115c8724 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useRef } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; @@ -8,32 +8,33 @@ import SettingsWidget from './SettingsWidget'; import QuestionWidget from './QuestionWidget'; import { EditorContainer } from '../../../EditorContainer'; import { selectors } from '../../../../data/redux'; -import ReactStateSettingsParser from '../../data/ReactStateSettingsParser'; -import ReactStateOLXParser from '../../data/ReactStateOLXParser'; +import RawEditor from '../../../../sharedComponents/RawEditor'; import { ProblemTypeKeys } from '../../../../data/constants/problem'; +import { parseState } from './hooks'; + export const EditProblemView = ({ problemType, problemState, }) => { - const parseState = (problem) => () => { - const reactSettingsParser = new ReactStateSettingsParser(problem); - const reactOLXParser = new ReactStateOLXParser({ problem }); - return { - settings: reactSettingsParser.getSettings(), - olx: reactOLXParser.buildOLX(), - }; - }; - if (problemType === ProblemTypeKeys.ADVANCED) { - return 'hello raw editor'; - } + const editorRef = useRef(null); + const isAdvancedProblemType = problemType === ProblemTypeKeys.ADVANCED; + + const getContent = parseState(problemState, isAdvancedProblemType, editorRef); + return ( - + - - + {isAdvancedProblemType ? ( + + ) : ( + <> + + + + )} diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx new file mode 100644 index 000000000..05e934a07 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx @@ -0,0 +1,20 @@ +import { shallow } from 'enzyme'; +import { EditProblemView } from '.'; +import AnswerWidget from './AnswerWidget'; +import { ProblemTypeKeys } from '../../../../data/constants/problem'; +import RawEditor from '../../../../sharedComponents/RawEditor'; + +describe('EditorProblemView component', () => { + test('renders simple view', () => { + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); + expect(wrapper.find(AnswerWidget).length).toBe(1); + }); + + test('renders raw editor', () => { + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); + expect(wrapper.find(AnswerWidget).length).toBe(0); + expect(wrapper.find(RawEditor).length).toBe(1); + }); +}); diff --git a/src/editors/containers/TextEditor/__snapshots__/index.test.jsx.snap b/src/editors/containers/TextEditor/__snapshots__/index.test.jsx.snap index 085a3400e..b7b79cd3c 100644 --- a/src/editors/containers/TextEditor/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/TextEditor/__snapshots__/index.test.jsx.snap @@ -236,6 +236,13 @@ exports[`TextEditor snapshots loaded, raw editor 1`] = ` />
diff --git a/src/editors/containers/TextEditor/components/CodeEditor/index.jsx b/src/editors/containers/TextEditor/components/CodeEditor/index.jsx deleted file mode 100644 index f4a465bd2..000000000 --- a/src/editors/containers/TextEditor/components/CodeEditor/index.jsx +++ /dev/null @@ -1,109 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import PropTypes from 'prop-types'; - -import { - Button, -} from '@edx/paragon'; - -import { basicSetup } from 'codemirror'; -import { EditorState } from '@codemirror/state'; -import { EditorView } from '@codemirror/view'; -import { html } from '@codemirror/lang-html'; - -import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import alphanumericMap from './constants'; -import * as module from './index'; -import messages from './messages'; -import './index.scss'; - -export const hooks = { - - state: { - showBtnEscapeHTML: (val) => React.useState(val), - }, - - prepareShowBtnEscapeHTML: () => { - const [visibility, setVisibility] = hooks.state.showBtnEscapeHTML(true); - const hide = () => setVisibility(false); - return { showBtnEscapeHTML: visibility, hideBtn: hide }; - }, - - createCodeMirrorDomNode: ({ ref, initialText, upstreamRef }) => { - useEffect(() => { - const cleanText = hooks.cleanHTML({ initialText }); - const state = EditorState.create({ - doc: cleanText, - extensions: [basicSetup, html(), EditorView.lineWrapping], - }); - const view = new EditorView({ state, parent: ref.current }); - // eslint-disable-next-line no-param-reassign - upstreamRef.current = view; - view.focus(); - return () => { - // called on cleanup - view.destroy(); - }; - }, []); - }, - cleanHTML: ({ initialText }) => { - const translateRegex = new RegExp(`&(${Object.keys(alphanumericMap).join('|')});`, 'g'); - const translator = ($0, $1) => alphanumericMap[$1]; - return initialText.replace(translateRegex, translator); - }, - escapeHTMLSpecialChars: ({ ref, hideBtn }) => { - const text = ref.current.state.doc.toString(); let - pos = 0; - const changes = []; - Object.keys(alphanumericMap).forEach( - (escapedKeyword) => { - // eslint-disable-next-line no-cond-assign - for (let next; (next = text.indexOf(alphanumericMap[escapedKeyword], pos)) > -1;) { - changes.push({ from: next, to: next + 1, insert: `&${escapedKeyword};` }); - pos = next + 1; - } - }, - ); - ref.current.dispatch({ changes }); - hideBtn(); - }, - -}; - -export const CodeEditor = ({ - innerRef, - value, - // injected - intl, -}) => { - const DOMref = useRef(); - const btnRef = useRef(); - module.hooks.createCodeMirrorDomNode({ ref: DOMref, initialText: value, upstreamRef: innerRef }); - const { showBtnEscapeHTML, hideBtn } = module.hooks.prepareShowBtnEscapeHTML(); - - return ( -
-
- {showBtnEscapeHTML && ( - - )} -
- ); -}; - -CodeEditor.propTypes = { - innerRef: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.shape({ current: PropTypes.any }), - ]).isRequired, - value: PropTypes.string.isRequired, - intl: intlShape.isRequired, -}; - -export default injectIntl(CodeEditor); diff --git a/src/editors/containers/TextEditor/components/RawEditor/index.jsx b/src/editors/containers/TextEditor/components/RawEditor/index.jsx deleted file mode 100644 index 57a3f7515..000000000 --- a/src/editors/containers/TextEditor/components/RawEditor/index.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Alert } from '@edx/paragon'; - -import CodeEditor from '../CodeEditor'; - -export const RawEditor = ({ - editorRef, - text, -}) => ( -
- - You are using the raw HTML editor. - - { text && text.data.data ? ( - - ) : null} - -
-); -RawEditor.defaultProps = { - editorRef: null, - text: null, -}; -RawEditor.propTypes = { - editorRef: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.shape({ current: PropTypes.any }), - ]), - text: PropTypes.shape({ - data: PropTypes.shape({ data: PropTypes.string }), - }), -}; - -export default RawEditor; diff --git a/src/editors/containers/TextEditor/components/SourceCodeModal/index.jsx b/src/editors/containers/TextEditor/components/SourceCodeModal/index.jsx index 590e10c1e..3241fd9bb 100644 --- a/src/editors/containers/TextEditor/components/SourceCodeModal/index.jsx +++ b/src/editors/containers/TextEditor/components/SourceCodeModal/index.jsx @@ -12,7 +12,7 @@ import messages from './messages'; import hooks from './hooks'; import BaseModal from '../BaseModal'; -import CodeEditor from '../CodeEditor'; +import CodeEditor from '../../../../sharedComponents/CodeEditor'; export const SourceCodeModal = ({ isOpen, diff --git a/src/editors/containers/TextEditor/index.jsx b/src/editors/containers/TextEditor/index.jsx index 450071b7d..b3f3d16c4 100644 --- a/src/editors/containers/TextEditor/index.jsx +++ b/src/editors/containers/TextEditor/index.jsx @@ -32,7 +32,7 @@ import { RequestKeys } from '../../data/constants/requests'; import EditorContainer from '../EditorContainer'; import ImageUploadModal from './components/ImageUploadModal'; import SourceCodeModal from './components/SourceCodeModal'; -import RawEditor from './components/RawEditor'; +import RawEditor from '../../sharedComponents/RawEditor'; import * as hooks from './hooks'; import messages from './messages'; @@ -64,7 +64,7 @@ export const TextEditor = ({ return ( ); } diff --git a/src/editors/containers/TextEditor/components/CodeEditor/__snapshots__/index.test.jsx.snap b/src/editors/sharedComponents/CodeEditor/__snapshots__/index.test.jsx.snap similarity index 100% rename from src/editors/containers/TextEditor/components/CodeEditor/__snapshots__/index.test.jsx.snap rename to src/editors/sharedComponents/CodeEditor/__snapshots__/index.test.jsx.snap diff --git a/src/editors/containers/TextEditor/components/CodeEditor/constants.js b/src/editors/sharedComponents/CodeEditor/constants.js similarity index 100% rename from src/editors/containers/TextEditor/components/CodeEditor/constants.js rename to src/editors/sharedComponents/CodeEditor/constants.js diff --git a/src/editors/sharedComponents/CodeEditor/hooks.js b/src/editors/sharedComponents/CodeEditor/hooks.js new file mode 100644 index 000000000..3c99b0f1e --- /dev/null +++ b/src/editors/sharedComponents/CodeEditor/hooks.js @@ -0,0 +1,71 @@ +import React, { useEffect } from 'react'; + +import { basicSetup } from 'codemirror'; +import { EditorState } from '@codemirror/state'; +import { EditorView } from '@codemirror/view'; +import { html } from '@codemirror/lang-html'; +import { xml } from '@codemirror/lang-xml'; + +import alphanumericMap from './constants'; +import './index.scss'; + +const CODEMIRROR_LANGUAGES = { HTML: 'html', XML: 'xml' }; + +export const state = { + showBtnEscapeHTML: (val) => React.useState(val), +}; + +export const prepareShowBtnEscapeHTML = () => { + const [visibility, setVisibility] = state.showBtnEscapeHTML(true); + const hide = () => setVisibility(false); + return { showBtnEscapeHTML: visibility, hideBtn: hide }; +}; + +export const cleanHTML = ({ initialText }) => { + const translateRegex = new RegExp(`&(${Object.keys(alphanumericMap).join('|')});`, 'g'); + const translator = ($0, $1) => alphanumericMap[$1]; + return initialText.replace(translateRegex, translator); +}; + +export const createCodeMirrorDomNode = ({ + ref, + initialText, + upstreamRef, + lang, +}) => { + useEffect(() => { + const languageExtension = lang === CODEMIRROR_LANGUAGES.HTML ? html() : xml(); + const cleanText = cleanHTML({ initialText }); + const newState = EditorState.create({ + doc: cleanText, + extensions: [basicSetup, languageExtension, EditorView.lineWrapping], + }); + const view = new EditorView({ state: newState, parent: ref.current }); + // eslint-disable-next-line no-param-reassign + upstreamRef.current = view; + view.focus(); + + return () => { + // called on cleanup + view.destroy(); + }; + }, []); +}; + +export const escapeHTMLSpecialChars = ({ ref, hideBtn }) => { + const text = ref.current.state.doc.toString(); + let pos = 0; + const changes = []; + Object.keys(alphanumericMap).forEach( + (escapedKeyword) => { + // eslint-disable-next-line no-cond-assign + for (let next; (next = text.indexOf(alphanumericMap[escapedKeyword], pos)) > -1;) { + changes.push({ from: next, to: next + 1, insert: `&${escapedKeyword};` }); + pos = next + 1; + } + }, + ); + + ref.current.dispatch({ changes }); + hideBtn(); +}; diff --git a/src/editors/sharedComponents/CodeEditor/index.jsx b/src/editors/sharedComponents/CodeEditor/index.jsx new file mode 100644 index 000000000..49155ae92 --- /dev/null +++ b/src/editors/sharedComponents/CodeEditor/index.jsx @@ -0,0 +1,55 @@ +import React, { useRef } from 'react'; +import PropTypes from 'prop-types'; + +import { + Button, +} from '@edx/paragon'; + +import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import messages from './messages'; +import './index.scss'; + +import * as hooks from './hooks'; + +export const CodeEditor = ({ + innerRef, + value, + lang, + // injected + intl, +}) => { + const DOMref = useRef(); + const btnRef = useRef(); + hooks.createCodeMirrorDomNode({ + ref: DOMref, initialText: value, upstreamRef: innerRef, lang, + }); + const { showBtnEscapeHTML, hideBtn } = hooks.prepareShowBtnEscapeHTML(); + + return ( +
+
+ {showBtnEscapeHTML && ( + + )} +
+ ); +}; + +CodeEditor.propTypes = { + innerRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.any }), + ]).isRequired, + value: PropTypes.string.isRequired, + intl: intlShape.isRequired, + lang: PropTypes.string.isRequired, +}; + +export default injectIntl(CodeEditor); diff --git a/src/editors/containers/TextEditor/components/CodeEditor/index.scss b/src/editors/sharedComponents/CodeEditor/index.scss similarity index 100% rename from src/editors/containers/TextEditor/components/CodeEditor/index.scss rename to src/editors/sharedComponents/CodeEditor/index.scss diff --git a/src/editors/containers/TextEditor/components/CodeEditor/index.test.jsx b/src/editors/sharedComponents/CodeEditor/index.test.jsx similarity index 81% rename from src/editors/containers/TextEditor/components/CodeEditor/index.test.jsx rename to src/editors/sharedComponents/CodeEditor/index.test.jsx index 918818b99..7004a25ab 100644 --- a/src/editors/containers/TextEditor/components/CodeEditor/index.test.jsx +++ b/src/editors/sharedComponents/CodeEditor/index.test.jsx @@ -4,9 +4,10 @@ import { shallow } from 'enzyme'; import { EditorState } from '@codemirror/state'; import { EditorView } from '@codemirror/view'; import { html } from '@codemirror/lang-html'; -import { formatMessage, MockUseState } from '../../../../../testUtils'; +import { formatMessage, MockUseState } from '../../../testUtils'; import alphanumericMap from './constants'; import * as module from './index'; +import * as hooks from './hooks'; jest.mock('@codemirror/view'); @@ -28,11 +29,15 @@ jest.mock('@codemirror/lang-html', () => ({ html: jest.fn(), })); +jest.mock('@codemirror/lang-xml', () => ({ + xml: jest.fn(), +})); + jest.mock('codemirror', () => ({ basicSetup: 'bAsiCSetUp', })); -const state = new MockUseState(module.hooks); +const state = new MockUseState(hooks); describe('CodeEditor', () => { describe('Hooks', () => { @@ -45,7 +50,7 @@ describe('CodeEditor', () => { state.mock(); }); it('prepareShowBtnEscapeHTML', () => { - const hook = module.hooks.prepareShowBtnEscapeHTML(); + const hook = hooks.prepareShowBtnEscapeHTML(); expect(state.stateVals.showBtnEscapeHTML).toEqual(hook.showBtnEscapeHTML); hook.hideBtn(); expect(state.setState.showBtnEscapeHTML).toHaveBeenCalledWith(false); @@ -58,7 +63,7 @@ describe('CodeEditor', () => { const cleanText = `${Object.values(alphanumericMap).join(' , ')}`; it('escapes alphanumerics and sets them to be literals', () => { - expect(module.hooks.cleanHTML({ initialText: dirtyText })).toEqual(cleanText); + expect(hooks.cleanHTML({ initialText: dirtyText })).toEqual(cleanText); }); }); @@ -79,7 +84,7 @@ describe('CodeEditor', () => { }; const mockHideBtn = jest.fn(); it('unescapes literals and sets them to be alphanumerics', () => { - module.hooks.escapeHTMLSpecialChars({ ref, hideBtn: mockHideBtn }); + hooks.escapeHTMLSpecialChars({ ref, hideBtn: mockHideBtn }); expect(mockDispatch).toHaveBeenCalled(); expect(mockHideBtn).toHaveBeenCalled(); }); @@ -90,13 +95,14 @@ describe('CodeEditor', () => { ref: { current: 'sOmEvAlUe', }, + lang: 'html', initialText: 'sOmEhTmL', upstreamRef: { current: 'sOmEotHERvAlUe', }, }; beforeEach(() => { - module.hooks.createCodeMirrorDomNode(props); + hooks.createCodeMirrorDomNode(props); }); it('calls useEffect and sets up codemirror objects', () => { const [cb, prereqs] = React.useEffect.mock.calls[0]; @@ -118,18 +124,19 @@ describe('CodeEditor', () => { innerRef: { current: 'sOmEvALUE', }, + lang: 'html', value: 'mOcKhTmL', }; - jest.spyOn(module.hooks, 'createCodeMirrorDomNode').mockImplementation(() => ({})); + jest.spyOn(hooks, 'createCodeMirrorDomNode').mockImplementation(() => ({})); }); afterAll(() => { jest.clearAllMocks(); }); test('Renders and calls Hooks ', () => { - jest.spyOn(module.hooks, 'prepareShowBtnEscapeHTML').mockImplementation(() => ({ showBtnEscapeHTML: true, hideBtn: mockHideBtn })); + jest.spyOn(hooks, 'prepareShowBtnEscapeHTML').mockImplementation(() => ({ showBtnEscapeHTML: true, hideBtn: mockHideBtn })); // Note: ref won't show up as it is not acutaly a DOM attribute. expect(shallow()).toMatchSnapshot(); - expect(module.hooks.createCodeMirrorDomNode).toHaveBeenCalled(); + expect(hooks.createCodeMirrorDomNode).toHaveBeenCalled(); }); }); }); diff --git a/src/editors/containers/TextEditor/components/CodeEditor/messages.js b/src/editors/sharedComponents/CodeEditor/messages.js similarity index 100% rename from src/editors/containers/TextEditor/components/CodeEditor/messages.js rename to src/editors/sharedComponents/CodeEditor/messages.js diff --git a/src/editors/containers/TextEditor/components/RawEditor/__snapshots__/index.test.jsx.snap b/src/editors/sharedComponents/RawEditor/__snapshots__/index.test.jsx.snap similarity index 87% rename from src/editors/containers/TextEditor/components/RawEditor/__snapshots__/index.test.jsx.snap rename to src/editors/sharedComponents/RawEditor/__snapshots__/index.test.jsx.snap index 32899800f..310a73298 100644 --- a/src/editors/containers/TextEditor/components/RawEditor/__snapshots__/index.test.jsx.snap +++ b/src/editors/sharedComponents/RawEditor/__snapshots__/index.test.jsx.snap @@ -12,7 +12,9 @@ exports[`RawEditor renders as expected with default behavior 1`] = ` - You are using the raw HTML editor. + You are using the raw + html + editor.
diff --git a/src/editors/sharedComponents/RawEditor/index.jsx b/src/editors/sharedComponents/RawEditor/index.jsx new file mode 100644 index 000000000..6c3adccd6 --- /dev/null +++ b/src/editors/sharedComponents/RawEditor/index.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Alert } from '@edx/paragon'; + +import CodeEditor from '../CodeEditor'; + +function getValue(content) { + if (!content) { return null; } + if (typeof content === 'string') { return content; } + return content.data?.data; +} + +export const RawEditor = ({ + editorRef, + content, + lang, +}) => { + const value = getValue(content); + + return ( +
+ + You are using the raw {lang} editor. + + { value ? ( + + ) : null} + +
+ ); +}; +RawEditor.defaultProps = { + editorRef: null, + content: null, + lang: 'html', +}; +RawEditor.propTypes = { + editorRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.any }), + ]), + content: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.shape({ + data: PropTypes.shape({ data: PropTypes.string }), + }), + ]), + lang: PropTypes.string, +}; + +export default RawEditor; diff --git a/src/editors/containers/TextEditor/components/RawEditor/index.test.jsx b/src/editors/sharedComponents/RawEditor/index.test.jsx similarity index 87% rename from src/editors/containers/TextEditor/components/RawEditor/index.test.jsx rename to src/editors/sharedComponents/RawEditor/index.test.jsx index 7c403ed07..868f7b47d 100644 --- a/src/editors/containers/TextEditor/components/RawEditor/index.test.jsx +++ b/src/editors/sharedComponents/RawEditor/index.test.jsx @@ -10,7 +10,7 @@ describe('RawEditor', () => { value: 'Ref Value', }, }, - text: { data: { data: 'eDiTablE Text' } }, + content: { data: { data: 'eDiTablE Text' } }, }; test('renders as expected with default behavior', () => { expect(shallow()).toMatchSnapshot(); From cf1daa3ba5c0cddbd08a175398ac3ea776fc6989 Mon Sep 17 00:00:00 2001 From: connorhaugh <49422820+connorhaugh@users.noreply.github.com> Date: Wed, 11 Jan 2023 14:26:46 -0500 Subject: [PATCH 06/29] Feat add templates for default problems after problem select (#185) https://2u-internal.atlassian.net/browse/TNL-10316 is the relevant bug. Problems have default values when created using the select type page. --- .../SelectTypeWrapper/SelectTypeFooter.jsx | 5 +--- .../SelectTypeFooter.test.jsx | 8 ++--- .../components/SelectTypeModal/hooks.js | 11 ++++--- .../components/SelectTypeModal/hooks.test.js | 23 +++++++++------ .../circuitschematic.js | 0 .../customgrader.js | 0 .../drag_and_drop.js | 0 .../formularesponse.js | 0 .../imageresponse.js | 0 .../constants/advancedOlxTemplates/index.js | 12 ++++++++ .../jsinput_response.js | 0 .../problem_with_hint.js | 0 .../constants/basicOlxTemplates/dropdown.js | 15 ++++++++++ .../data/constants/basicOlxTemplates/index.js | 10 +++++++ .../basicOlxTemplates/multiSelect.js | 15 ++++++++++ .../constants/basicOlxTemplates/numeric.js | 12 ++++++++ .../basicOlxTemplates/singleSelect.js | 14 +++++++++ .../constants/basicOlxTemplates/textInput.js | 11 +++++++ src/editors/data/constants/problem.js | 29 ++++++++++--------- src/editors/data/redux/problem/reducers.js | 4 --- src/editors/data/services/cms/mockApi.js | 3 +- 21 files changed, 132 insertions(+), 40 deletions(-) rename src/editors/data/constants/{olxTemplates => advancedOlxTemplates}/circuitschematic.js (100%) rename src/editors/data/constants/{olxTemplates => advancedOlxTemplates}/customgrader.js (100%) rename src/editors/data/constants/{olxTemplates => advancedOlxTemplates}/drag_and_drop.js (100%) rename src/editors/data/constants/{olxTemplates => advancedOlxTemplates}/formularesponse.js (100%) rename src/editors/data/constants/{olxTemplates => advancedOlxTemplates}/imageresponse.js (100%) create mode 100644 src/editors/data/constants/advancedOlxTemplates/index.js rename src/editors/data/constants/{olxTemplates => advancedOlxTemplates}/jsinput_response.js (100%) rename src/editors/data/constants/{olxTemplates => advancedOlxTemplates}/problem_with_hint.js (100%) create mode 100644 src/editors/data/constants/basicOlxTemplates/dropdown.js create mode 100644 src/editors/data/constants/basicOlxTemplates/index.js create mode 100644 src/editors/data/constants/basicOlxTemplates/multiSelect.js create mode 100644 src/editors/data/constants/basicOlxTemplates/numeric.js create mode 100644 src/editors/data/constants/basicOlxTemplates/singleSelect.js create mode 100644 src/editors/data/constants/basicOlxTemplates/textInput.js diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx index e7f279fe7..d633ba172 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx @@ -17,7 +17,6 @@ export const SelectTypeFooter = ({ onCancel, selected, // redux - setProblemType, updateField, // injected, intl, @@ -35,7 +34,7 @@ export const SelectTypeFooter = ({
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js index b43a5046c..0f7997e68 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js @@ -221,7 +221,14 @@ describe('Problem settings hooks', () => { const typekey = 'multiplechoiceresponse'; const updateField = jest.fn(); const updateAnswer = jest.fn(); - const answers = [{ correct: true, id: 'a' }, { correct: true, id: 'b' }]; + const answers = [{ + correct: true, + id: 'a', + }, + { + correct: true, + id: 'b', + }]; output = hooks.typeRowHooks({ answers, correctAnswerCount: 2, @@ -230,7 +237,8 @@ describe('Problem settings hooks', () => { updateAnswer, }); output.onClick(); - expect(updateAnswer).toHaveBeenCalledWith({ ...answers[1], correct: false }); + expect(updateAnswer).toHaveBeenNthCalledWith(1, { ...answers[0], correct: false }); + expect(updateAnswer).toHaveBeenNthCalledWith(2, { ...answers[1], correct: false }); expect(updateField).toHaveBeenCalledWith({ problemType: typekey }); }); }); diff --git a/src/editors/data/redux/problem/reducers.js b/src/editors/data/redux/problem/reducers.js index ee7acf74e..bbbc26b22 100644 --- a/src/editors/data/redux/problem/reducers.js +++ b/src/editors/data/redux/problem/reducers.js @@ -105,15 +105,12 @@ const problem = createSlice({ title: '', selectedFeedback: undefined, unselectedFeedback: undefined, - feedback: undefined, correct: false, }; if (state.problemType === ProblemTypeKeys.MULTISELECT) { - newOption.selectedFeedback = ''; newOption.unselectedFeedback = ''; - } else { - newOption.feedback = ''; } + newOption.selectedFeedback = ''; const answers = [ ...currAnswers, newOption, diff --git a/src/editors/data/redux/problem/reducers.test.js b/src/editors/data/redux/problem/reducers.test.js index db1b828f2..3e4e3b881 100644 --- a/src/editors/data/redux/problem/reducers.test.js +++ b/src/editors/data/redux/problem/reducers.test.js @@ -31,8 +31,7 @@ describe('problem reducer', () => { const answer = { id: 'A', correct: false, - feedback: '', - selectedFeedback: undefined, + selectedFeedback: '', title: '', unselectedFeedback: undefined, }; @@ -42,15 +41,6 @@ describe('problem reducer', () => { }); }); }); - describe('setProblemType', () => { - it('sets problemType', () => { - const payload = { selected: 'soMePRoblEMtYPe' }; - expect(reducer(testingState, actions.setProblemType(payload))).toEqual({ - ...testingState, - problemType: 'soMePRoblEMtYPe', - }); - }); - }); describe('setEnableTypeSelection', () => { it('sets problemType to null', () => { expect(reducer(testingState, actions.setEnableTypeSelection())).toEqual({ @@ -85,13 +75,13 @@ describe('problem reducer', () => { const answer = { id: 'A', correct: false, - feedback: '', - selectedFeedback: undefined, + selectedFeedback: '', title: '', - unselectedFeedback: undefined, + unselectedFeedback: '', }; - expect(reducer(testingState, actions.addAnswer(answer))).toEqual({ + expect(reducer({ ...testingState, problemType: 'choiceresponse' }, actions.addAnswer())).toEqual({ ...testingState, + problemType: 'choiceresponse', answers: [answer], }); }); From 9b2e284ee3888dd64d68b77e31a72de7e2b516e0 Mon Sep 17 00:00:00 2001 From: connorhaugh <49422820+connorhaugh@users.noreply.github.com> Date: Wed, 11 Jan 2023 16:53:16 -0500 Subject: [PATCH 08/29] fix: remove test (#188) --- src/editors/data/redux/problem/reducers.test.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/editors/data/redux/problem/reducers.test.js b/src/editors/data/redux/problem/reducers.test.js index 3e4e3b881..737404097 100644 --- a/src/editors/data/redux/problem/reducers.test.js +++ b/src/editors/data/redux/problem/reducers.test.js @@ -41,14 +41,6 @@ describe('problem reducer', () => { }); }); }); - describe('setEnableTypeSelection', () => { - it('sets problemType to null', () => { - expect(reducer(testingState, actions.setEnableTypeSelection())).toEqual({ - ...testingState, - problemType: null, - }); - }); - }); describe('updateField', () => { it('sets given parameter', () => { const payload = { problemType: 'soMePRoblEMtYPe' }; From 5f5dc911da95ddd0f7d8c2d4beb1f626b088fbc4 Mon Sep 17 00:00:00 2001 From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> Date: Thu, 12 Jan 2023 09:22:37 -0500 Subject: [PATCH 09/29] fix: change # destination to advance settings url (#189) --- .../settingsComponents/ShowAnswerCard.jsx | 20 +++++++++-- .../ShowAnswerCard.test.jsx | 34 ++++++++++++++++++- .../ShowAnswerCard.test.jsx.snap | 2 +- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx index da15c2fbc..cdd8186ac 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx @@ -1,9 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; import { injectIntl, FormattedMessage, intlShape } from '@edx/frontend-platform/i18n'; import { Form, Hyperlink } from '@edx/paragon'; import SettingsOption from '../SettingsOption'; import { ShowAnswerTypes, ShowAnswerTypesKeys } from '../../../../../../data/constants/problem'; +import { selectors } from '../../../../../../data/redux'; import messages from '../messages'; import { showAnswerCardHooks } from '../hooks'; @@ -12,6 +14,9 @@ export const ShowAnswerCard = ({ updateSettings, // inject intl, + // redux + studioEndpointUrl, + learningContextId, }) => { const { handleShowAnswerChange, @@ -29,7 +34,7 @@ export const ShowAnswerCard = ({
- +
@@ -49,7 +54,7 @@ export const ShowAnswerCard = ({ ))} - { showAttempts + {showAttempts && ( ({ + studioEndpointUrl: selectors.app.studioEndpointUrl(state), + learningContextId: selectors.app.learningContextId(state), +}); + +export const mapDispatchToProps = {}; + +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ShowAnswerCard)); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx index e727746e2..63d2e2bfe 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx @@ -1,13 +1,23 @@ import React from 'react'; import { shallow } from 'enzyme'; import { formatMessage } from '../../../../../../../testUtils'; -import { ShowAnswerCard } from './ShowAnswerCard'; +import { selectors } from '../../../../../../data/redux'; +import { ShowAnswerCard, mapStateToProps, mapDispatchToProps } from './ShowAnswerCard'; import { showAnswerCardHooks } from '../hooks'; jest.mock('../hooks', () => ({ showAnswerCardHooks: jest.fn(), })); +jest.mock('../../../../../../data/redux', () => ({ + selectors: { + app: { + studioEndpointUrl: jest.fn(state => ({ studioEndpointUrl: state })), + learningContextId: jest.fn(state => ({ learningContextId: state })), + }, + }, +})); + describe('ShowAnswerCard', () => { const showAnswer = { on: 'after_attempts', @@ -17,7 +27,11 @@ describe('ShowAnswerCard', () => { }; const props = { showAnswer, + // injected intl: { formatMessage }, + // redux + studioEndpointUrl: 'SoMEeNDpOinT', + learningContextId: 'sOMEcouRseId', }; const showAnswerCardHooksProps = { @@ -39,4 +53,22 @@ describe('ShowAnswerCard', () => { expect(shallow()).toMatchSnapshot(); }); }); + describe('mapStateToProps', () => { + const testState = { A: 'pple', B: 'anana', C: 'ucumber' }; + test('studioEndpointUrl from app.studioEndpointUrl', () => { + expect( + mapStateToProps(testState).studioEndpointUrl, + ).toEqual(selectors.app.studioEndpointUrl(testState)); + }); + test('learningContextId from app.learningContextId', () => { + expect( + mapStateToProps(testState).learningContextId, + ).toEqual(selectors.app.learningContextId(testState)); + }); + }); + describe('mapDispatchToProps', () => { + test('equal an empty object', () => { + expect(mapDispatchToProps).toEqual({}); + }); + }); }); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ShowAnswerCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ShowAnswerCard.test.jsx.snap index 8a750c7b3..7e4b65d7e 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ShowAnswerCard.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ShowAnswerCard.test.jsx.snap @@ -20,7 +20,7 @@ exports[`ShowAnswerCard snapshot snapshot: show answer setting card 1`] = ` className="spacedMessage" > Date: Thu, 12 Jan 2023 09:31:31 -0500 Subject: [PATCH 10/29] build: pin semantic release to unblock pipeline (#191) I saw that the release CI https://github.com/openedx/frontend-lib-content-components/actions/runs/3897027405/jobs/6654318273 was failing the release on [semantic-release]: node version >=18 is required. Found v16.19.0. from https://github.com/semantic-release/semantic-release/releases/tag/v20.0.0 we learn that node v18 is now the minimum required version of node. The version of semantic-release that runs in a repo is usually based on the relevant Github Acton workflow file for the release, defined in the repo itself. I am pinning that version to 19.0.5 until the next node upgrade, as it seems we recently upgrade to node 16. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6ab04a8c0..3375feae9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,4 +36,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.SEMANTIC_RELEASE_NPM_TOKEN }} - run: npx semantic-release + run: npx semantic-release@19.05 From 6b2b5ac45525b08929c370bd70cfbaaaa0012f4c Mon Sep 17 00:00:00 2001 From: connorhaugh <49422820+connorhaugh@users.noreply.github.com> Date: Thu, 12 Jan 2023 09:56:59 -0500 Subject: [PATCH 11/29] Update workflow --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3375feae9..a1007f8a7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,4 +36,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.SEMANTIC_RELEASE_NPM_TOKEN }} - run: npx semantic-release@19.05 + run: npx semantic-release@19.0.5 From 390d620664631713d177b0bd0ba6b93021e17f1d Mon Sep 17 00:00:00 2001 From: Ken Clary Date: Wed, 11 Jan 2023 16:11:39 -0500 Subject: [PATCH 12/29] fix: match new frontend behavior with legacy behavior (zero problem attempts is zero attempts, null attempts is infinite); negative values disallowed and forced to zero. TNL-10324. --- .../EditProblemView/SettingsWidget/hooks.js | 8 +++--- .../SettingsWidget/hooks.test.js | 26 ++++++++++++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js index 6ef3e3c76..47c88e6e7 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js @@ -113,14 +113,14 @@ export const resetCardHooks = (updateSettings) => { export const scoringCardHooks = (scoring, updateSettings) => { const handleMaxAttemptChange = (event) => { - let unlimitedAttempts = true; + let unlimitedAttempts = false; let attemptNumber = parseInt(event.target.value); if (_.isNaN(attemptNumber)) { + attemptNumber = null; + unlimitedAttempts = true; + } else if (attemptNumber < 0) { attemptNumber = 0; } - if (attemptNumber > 0) { - unlimitedAttempts = false; - } updateSettings({ scoring: { ...scoring, attempts: { number: attemptNumber, unlimited: unlimitedAttempts } } }); }; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js index 0f7997e68..556004402 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js @@ -178,7 +178,31 @@ describe('Problem settings hooks', () => { const value = 0; output.handleMaxAttemptChange({ target: { value } }); expect(updateSettings) - .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: value, unlimited: true } } }); + .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: value, unlimited: false } } }); + }); + test('test handleMaxAttemptChange set attempts to null value', () => { + const value = null; + output.handleMaxAttemptChange({ target: { value } }); + expect(updateSettings) + .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: null, unlimited: true } } }); + }); + test('test handleMaxAttemptChange set attempts to empty string', () => { + const value = ''; + output.handleMaxAttemptChange({ target: { value } }); + expect(updateSettings) + .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: null, unlimited: true } } }); + }); + test('test handleMaxAttemptChange set attempts to non-numeric value', () => { + const value = 'abc'; + output.handleMaxAttemptChange({ target: { value } }); + expect(updateSettings) + .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: null, unlimited: true } } }); + }); + test('test handleMaxAttemptChange set attempts to negative value', () => { + const value = -1; + output.handleMaxAttemptChange({ target: { value } }); + expect(updateSettings) + .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: 0, unlimited: false } } }); }); test('test handleWeightChange', () => { const value = 2; From 574c2cc76a4c3cf54de72f28aab061b2e51f1807 Mon Sep 17 00:00:00 2001 From: Jesper Hodge <19345795+jesperhodge@users.noreply.github.com> Date: Thu, 12 Jan 2023 12:25:18 -0500 Subject: [PATCH 13/29] fix: cannot open problem editor because of typeerror undefined (#193) * fix: cannot open problem editor because of typeerror * fix: snapshots --- .../__snapshots__/index.test.jsx.snap | 12 ++++-------- .../components/EditProblemView/index.jsx | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/__snapshots__/index.test.jsx.snap index 2266b9fe0..005ba0de5 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/__snapshots__/index.test.jsx.snap @@ -1,10 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`EditorProblemView component renders raw editor 1`] = ` -
- + `; exports[`EditorProblemView component renders simple view 1`] = ` -
- + `; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx index e115c8724..710c3e635 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx @@ -6,7 +6,7 @@ import { Col, Container, Row } from '@edx/paragon'; import AnswerWidget from './AnswerWidget'; import SettingsWidget from './SettingsWidget'; import QuestionWidget from './QuestionWidget'; -import { EditorContainer } from '../../../EditorContainer'; +import EditorContainer from '../../../EditorContainer'; import { selectors } from '../../../../data/redux'; import RawEditor from '../../../../sharedComponents/RawEditor'; import { ProblemTypeKeys } from '../../../../data/constants/problem'; From aad7a6b706cb8437c378ab46143523dccef9c7b3 Mon Sep 17 00:00:00 2001 From: Raymond Zhou <56318341+rayzhou-bit@users.noreply.github.com> Date: Thu, 12 Jan 2023 09:51:56 -0800 Subject: [PATCH 14/29] feat: video editor fix to disappearing transcript (#178) * feat: video editor fix to disappearing transcript --- .../components/VideoSourceWidget/hooks.jsx | 83 +++---------- .../VideoSourceWidget/hooks.test.jsx | 110 ++++++++---------- .../components/VideoSourceWidget/index.jsx | 29 ++--- .../VideoSourceWidget/index.test.jsx | 68 +++++++---- src/editors/data/redux/thunkActions/video.js | 36 +++--- .../data/redux/thunkActions/video.test.js | 38 +++--- src/editors/data/services/cms/api.js | 21 ++-- src/editors/data/services/cms/api.test.js | 37 +++--- src/editors/data/services/cms/mockApi.js | 4 + 9 files changed, 178 insertions(+), 248 deletions(-) diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.jsx index d68d1cde2..90a8fce8c 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.jsx @@ -1,70 +1,19 @@ import { actions } from '../../../../../../data/redux'; -import { isEdxVideo } from '../../../../../../data/services/cms/api'; -/** - * updateVideoId({ dispatch })({e, source}) - * updateVideoId takes the current onBlur event, the current object of the video - * source, and dispatch method, and updates the redux value for all the fields to - * their default values except videoId, fallbackVideos, and handouts. - * @param {event} e - object for onBlur event - * @param {func} dispatch - redux dispatch method - * @param {object} source - object for the Video Source field functions and values - */ -export const updateVideoId = ({ dispatch }) => ({ e, source }) => { - if (source.local !== '') { - if (source.formValue !== e.target.value) { - source.onBlur(e); - let videoId; - let videoSource; - if (isEdxVideo(source.local)) { - videoId = source.local; - videoSource = ''; - } else if (source.local.includes('youtu.be') || source.local.includes('youtube')) { - videoId = ''; - videoSource = source.local; - } else { - videoId = ''; - videoSource = source.local; - } - dispatch(actions.video.updateField({ - videoId, - videoSource, - allowVideoDownloads: false, - thumbnail: null, - transcripts: [], - allowTranscriptDownloads: false, - showTranscriptByDefault: false, - duration: { - startTime: '00:00:00', - stopTime: '00:00:00', - total: '00:00:00', - }, - licenseType: null, - })); - } - } +export const sourceHooks = ({ dispatch }) => ({ + updateVideoURL: (e) => dispatch(actions.video.updateField({ videoSource: e.target.value })), + updateVideoId: (e) => dispatch(actions.video.updateField({ videoId: e.target.value })), +}); + +export const fallbackHooks = ({ fallbackVideos, dispatch }) => ({ + addFallbackVideo: () => dispatch(actions.video.updateField({ fallbackVideos: [...fallbackVideos, ''] })), + deleteFallbackVideo: (videoUrl) => { + const updatedFallbackVideos = fallbackVideos.splice(fallbackVideos.indexOf(videoUrl), 1); + dispatch(actions.video.updateField({ fallbackVideos: updatedFallbackVideos })); + }, +}); + +export default { + sourceHooks, + fallbackHooks, }; - -/** - * deleteFallbackVideo({ fallbackVideos, dispatch })(videoUrl) - * deleteFallbackVideo takes the current array of fallback videos, string of - * deleted video URL and dispatch method, and updates the redux value for - * fallbackVideos. - * @param {array} fallbackVideos - array of current fallback videos - * @param {func} dispatch - redux dispatch method - * @param {string} videoUrl - string of the video URL for the fallabck video that needs to be deleted - */ -export const deleteFallbackVideo = ({ fallbackVideos, dispatch }) => (videoUrl) => { - const updatedFallbackVideos = []; - let firstOccurence = true; - fallbackVideos.forEach(item => { - if (item === videoUrl && firstOccurence) { - firstOccurence = false; - } else { - updatedFallbackVideos.push(item); - } - }); - dispatch(actions.video.updateField({ fallbackVideos: updatedFallbackVideos })); -}; - -export default { deleteFallbackVideo }; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx index 4f21f183e..34181e523 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx @@ -6,6 +6,7 @@ jest.mock('react-redux', () => { const dispatchFn = jest.fn(); return { ...jest.requireActual('react-redux'), + useSelector: jest.fn(), dispatch: dispatchFn, useDispatch: jest.fn(() => dispatchFn), }; @@ -20,78 +21,61 @@ jest.mock('../../../../../../data/redux', () => ({ })); describe('VideoEditorHandout hooks', () => { - describe('updateVideoId', () => { - const sourceEdxVideo = { - onBlur: jest.fn(), - local: '06b15030-7df0-4e70-b979-326e02dbcbe0', - }; - const sourceYouTube = { - onBlur: jest.fn(), - local: 'youtu.be', - }; - const sourceHtml5Source = { - onBlur: jest.fn(), - local: 'sOMEranDomfILe.mp4', - }; - const mockState = { - videoId: '', - videoSource: '', - allowVideoDownloads: false, - thumbnail: null, - transcripts: [], - allowTranscriptDownloads: false, - showTranscriptByDefault: false, - duration: { - startTime: '00:00:00', - stopTime: '00:00:00', - total: '00:00:00', - }, - licenseType: null, - }; - it('returns dispatches updateField action with default state and edxVideo Id', () => { - hooks.updateVideoId({ dispatch })({ e: { target: { value: sourceEdxVideo.local } }, source: sourceEdxVideo }); - expect(dispatch).toHaveBeenCalledWith( - actions.video.updateField({ - ...mockState, - videoId: sourceEdxVideo.local, - }), - ); + let hook; + + describe('sourceHooks', () => { + const e = { target: { value: 'soMEvALuE' } }; + beforeEach(() => { + hook = hooks.sourceHooks({ dispatch }); }); - it('returns dispatches updateField action with default state and YouTube video', () => { - hooks.updateVideoId({ dispatch })({ - e: { target: { value: sourceYouTube.local } }, - source: sourceYouTube, + describe('updateVideoURL', () => { + it('dispatches updateField action with new videoSource', () => { + hook.updateVideoURL(e); + expect(dispatch).toHaveBeenCalledWith( + actions.video.updateField({ + videoSource: e.target.value, + }), + ); }); - expect(dispatch).toHaveBeenCalledWith( - actions.video.updateField({ - ...mockState, - }), - ); }); - it('returns dispatches updateField action with default state and html5source video', () => { - hooks.updateVideoId({ dispatch })({ - e: { target: { value: sourceHtml5Source.local } }, - source: sourceHtml5Source, + describe('updateVideoId', () => { + it('dispatches updateField action with new videoId', () => { + hook.updateVideoId(e); + expect(dispatch).toHaveBeenCalledWith( + actions.video.updateField({ + videoId: e.target.value, + }), + ); }); - expect(dispatch).toHaveBeenCalledWith( - actions.video.updateField({ - ...mockState, - }), - ); }); }); - describe('deleteFallbackVideo', () => { + describe('fallbackHooks', () => { const videoUrl = 'sOmERAndoMuRl1'; const fallbackVideos = ['sOmERAndoMuRl1', 'sOmERAndoMuRl2', 'sOmERAndoMuRl1', '']; - const updatedFallbackVideos = ['sOmERAndoMuRl2', 'sOmERAndoMuRl1', '']; - it('returns dispatches updateField action with updatedFallbackVideos', () => { - hooks.deleteFallbackVideo({ fallbackVideos, dispatch })(videoUrl); - expect(dispatch).toHaveBeenCalledWith( - actions.video.updateField({ - fallbackVideos: updatedFallbackVideos, - }), - ); + beforeEach(() => { + hook = hooks.fallbackHooks({ fallbackVideos, dispatch }); + }); + describe('addFallbackVideo', () => { + it('dispatches updateField action with updated array appended by a new empty element', () => { + hook.addFallbackVideo(); + expect(dispatch).toHaveBeenCalledWith( + actions.video.updateField({ + fallbackVideos: [...fallbackVideos, ''], + }), + ); + }); + }); + describe('deleteFallbackVideo', () => { + it('dispatches updateField action with updated array with videoUrl removed', () => { + const updatedFallbackVideos = ['sOmERAndoMuRl2', 'sOmERAndoMuRl1', '']; + hook.deleteFallbackVideo(videoUrl); + expect(dispatch).toHaveBeenCalledWith( + actions.video.updateField({ + fallbackVideos: updatedFallbackVideos, + }), + ); + }); }); }); }); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx index 7dc2762d5..915f4281e 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx @@ -1,6 +1,5 @@ import React from 'react'; -import { connect, useDispatch } from 'react-redux'; -import PropTypes from 'prop-types'; +import { useDispatch } from 'react-redux'; import { Form, @@ -20,9 +19,8 @@ import { } from '@edx/frontend-platform/i18n'; import * as widgetHooks from '../hooks'; -import * as module from './hooks'; +import * as hooks from './hooks'; import messages from './messages'; -import { actions } from '../../../../../../data/redux'; import CollapsibleFormWidget from '../CollapsibleFormWidget'; @@ -32,8 +30,6 @@ import CollapsibleFormWidget from '../CollapsibleFormWidget'; export const VideoSourceWidget = ({ // injected intl, - // redux - updateField, }) => { const dispatch = useDispatch(); const { @@ -50,8 +46,11 @@ export const VideoSourceWidget = ({ [widgetHooks.selectorKeys.allowVideoDownloads]: widgetHooks.genericWidget, }, }); - const deleteFallbackVideo = module.deleteFallbackVideo({ fallbackVideos: fallbackVideos.formValue, dispatch }); - const updateVideoId = module.updateVideoId({ dispatch }); + const { updateVideoId, updateVideoURL } = hooks.sourceHooks({ dispatch }); + const { + addFallbackVideo, + deleteFallbackVideo, + } = hooks.fallbackHooks({ fallbackVideos: fallbackVideos.formValue, dispatch }); return ( updateVideoId({ e, source: videoId })} + onBlur={updateVideoId} value={videoId.local} /> @@ -72,7 +71,7 @@ export const VideoSourceWidget = ({ updateVideoId({ e, source })} + onBlur={updateVideoURL} value={source.local} /> @@ -134,7 +133,7 @@ export const VideoSourceWidget = ({ size="sm" iconBefore={Add} variant="link" - onClick={() => updateField({ fallbackVideos: [...fallbackVideos.formValue, ''] })} + onClick={() => addFallbackVideo()} > @@ -144,12 +143,6 @@ export const VideoSourceWidget = ({ VideoSourceWidget.propTypes = { // injected intl: intlShape.isRequired, - // redux - updateField: PropTypes.func.isRequired, }; -export const mapStateToProps = () => ({}); -export const mapDispatchToProps = (dispatch) => ({ - updateField: (stateUpdate) => dispatch(actions.video.updateField(stateUpdate)), -}); -export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(VideoSourceWidget)); +export default injectIntl(VideoSourceWidget); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx index fcc48d57a..88d5825db 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx @@ -1,25 +1,19 @@ import React from 'react'; +import { dispatch } from 'react-redux'; import { shallow } from 'enzyme'; import { formatMessage } from '../../../../../../../testUtils'; -import { actions } from '../../../../../../data/redux'; -import { VideoSourceWidget, mapDispatchToProps } from '.'; +import { VideoSourceWidget } from '.'; +import * as hooks from './hooks'; -jest.mock('../../../../../../data/redux', () => ({ - actions: { - video: { - updateField: jest.fn().mockName('actions.video.updateField'), - }, - }, - selectors: { - video: { - videoSource: jest.fn(state => ({ videoSource: state })), - videoId: jest.fn(state => ({ videoId: state })), - fallbackVideos: jest.fn(state => ({ fallbackVideos: state })), - allowVideoDownloads: jest.fn(state => ({ allowVideoDownloads: state })), - }, - }, -})); +jest.mock('react-redux', () => { + const dispatchFn = jest.fn(); + return { + ...jest.requireActual('react-redux'), + dispatch: dispatchFn, + useDispatch: jest.fn(() => dispatchFn), + }; +}); jest.mock('../hooks', () => ({ selectorKeys: ['soMEkEy'], @@ -36,14 +30,21 @@ jest.mock('../hooks', () => ({ }), })); +jest.mock('./hooks', () => ({ + sourceHooks: jest.fn().mockReturnValue({ + updateVideoId: (args) => ({ updateVideoId: args }), + updateVideoURL: (args) => ({ updateVideoURL: args }), + }), + fallbackHooks: jest.fn().mockReturnValue({ + addFallbackVideo: jest.fn().mockName('addFallbackVideo'), + deleteFallbackVideo: jest.fn().mockName('deleteFallbackVideo'), + }), +})); + describe('VideoSourceWidget', () => { const props = { - error: {}, - title: 'tiTLE', // inject intl: { formatMessage }, - // redux - updateField: jest.fn().mockName('args.updateField'), }; describe('snapshots', () => { @@ -53,10 +54,27 @@ describe('VideoSourceWidget', () => { ).toMatchSnapshot(); }); }); - describe('mapDispatchToProps', () => { - const dispatch = jest.fn(); - test('updateField from actions.video.updateField', () => { - expect(mapDispatchToProps.updateField).toEqual(dispatch(actions.video.updateField)); + + describe('behavior inspection', () => { + let el; + let hook; + beforeEach(() => { + el = shallow(); + hook = hooks.sourceHooks({ dispatch }); + }); + test('updateVideoId is tied to id field onBlur', () => { + const expected = hook.updateVideoId; + expect(el + // eslint-disable-next-line + .children().at(0).children().at(0).children().at(0) + .props().onBlur).toEqual(expected); + }); + test('updateVideoURL is tied to url field onBlur', () => { + const expected = hook.updateVideoURL; + expect(el + // eslint-disable-next-line + .children().at(0).children().at(0).children().at(2) + .props().onBlur).toEqual(expected); }); }); }); diff --git a/src/editors/data/redux/thunkActions/video.js b/src/editors/data/redux/thunkActions/video.js index f36ecd86d..4b3d628a3 100644 --- a/src/editors/data/redux/thunkActions/video.js +++ b/src/editors/data/redux/thunkActions/video.js @@ -11,10 +11,10 @@ export const loadVideoData = () => (dispatch, getState) => { const courseLicenseData = state.app.courseDetails.data ? state.app.courseDetails.data : {}; const studioView = state.app.studioView?.data?.html; const { - videoSource, videoId, + videoUrl, fallbackVideos, - } = module.determineVideoSource({ + } = module.determineVideoSources({ edxVideoId: rawVideoData.edx_video_id, youtubeId: rawVideoData.youtube_id_1_0, html5Sources: rawVideoData.html5_sources, @@ -27,7 +27,7 @@ export const loadVideoData = () => (dispatch, getState) => { }); dispatch(actions.video.load({ - videoSource, + videoSource: videoUrl, videoId, fallbackVideos, allowVideoDownloads: rawVideoData.download_video, @@ -61,7 +61,7 @@ export const loadVideoData = () => (dispatch, getState) => { allowThumbnailUpload: response.data.allowThumbnailUpload, })), })); - const youTubeId = parseYoutubeId(videoSource); + const youTubeId = parseYoutubeId(videoUrl); if (youTubeId) { dispatch(requests.checkTranscriptsForImport({ videoId, @@ -77,33 +77,23 @@ export const loadVideoData = () => (dispatch, getState) => { } }; -export const determineVideoSource = ({ +export const determineVideoSources = ({ edxVideoId, youtubeId, html5Sources, }) => { - // videoSource should be the edx_video_id, the youtube url or the first fallback url in that order. - // If we are falling back to the first fallback url, remove it from the list of fallback urls for display. const youtubeUrl = `https://youtu.be/${youtubeId}`; - const videoId = edxVideoId || ''; - let videoSource = ''; - let fallbackVideos = []; + let videoUrl; + let fallbackVideos; if (youtubeId) { - // videoSource = youtubeUrl; - // fallbackVideos = html5Sources; - [videoSource, fallbackVideos] = [youtubeUrl, html5Sources]; - } else if (edxVideoId) { - // fallbackVideos = html5Sources; - fallbackVideos = html5Sources; + [videoUrl, fallbackVideos] = [youtubeUrl, html5Sources]; } else if (Array.isArray(html5Sources) && html5Sources[0]) { - // videoSource = html5Sources[0]; - // fallbackVideos = html5Sources.slice(1); - [videoSource, fallbackVideos] = [html5Sources[0], html5Sources.slice(1)]; + [videoUrl, fallbackVideos] = [html5Sources[0], html5Sources.slice(1)]; } return { - videoSource, - videoId, - fallbackVideos, + videoId: edxVideoId, + videoUrl: videoUrl || '', + fallbackVideos: fallbackVideos || [], }; }; @@ -343,7 +333,7 @@ export const replaceTranscript = ({ newFile, newFilename, language }) => (dispat export default { loadVideoData, - determineVideoSource, + determineVideoSources, parseLicense, saveVideoData, uploadThumbnail, diff --git a/src/editors/data/redux/thunkActions/video.test.js b/src/editors/data/redux/thunkActions/video.test.js index 895d4f425..cb90ecde4 100644 --- a/src/editors/data/redux/thunkActions/video.test.js +++ b/src/editors/data/redux/thunkActions/video.test.js @@ -99,8 +99,8 @@ describe('video thunkActions', () => { let dispatchedAction1; let dispatchedAction2; beforeEach(() => { - jest.spyOn(thunkActions, thunkActionsKeys.determineVideoSource).mockReturnValue({ - videoSource: 'videOsOurce', + jest.spyOn(thunkActions, thunkActionsKeys.determineVideoSources).mockReturnValue({ + videoUrl: 'videOsOurce', videoId: 'videOiD', fallbackVideos: 'fALLbACKvIDeos', }); @@ -176,69 +176,69 @@ describe('video thunkActions', () => { })); }); }); - describe('determineVideoSource', () => { + describe('determineVideoSources', () => { const edxVideoId = 'EDxviDEoiD'; const youtubeId = 'yOuTuBEiD'; const youtubeUrl = `https://youtu.be/${youtubeId}`; const html5Sources = ['htmLOne', 'hTMlTwo', 'htMLthrEE']; describe('when there is an edx video id, youtube id and html5 sources', () => { - it('returns the youtube id for video source and html5 sources for fallback videos', () => { - expect(thunkActions.determineVideoSource({ + it('returns all three with the youtube id wrapped in url', () => { + expect(thunkActions.determineVideoSources({ edxVideoId, youtubeId, html5Sources, })).toEqual({ - videoSource: youtubeUrl, + videoUrl: youtubeUrl, videoId: edxVideoId, fallbackVideos: html5Sources, }); }); }); - describe('when there is an edx video id', () => { + describe('when there is only an edx video id', () => { it('returns the edx video id for video source', () => { - expect(thunkActions.determineVideoSource({ + expect(thunkActions.determineVideoSources({ edxVideoId, youtubeId: '', html5Sources: '', })).toEqual({ - videoSource: '', + videoUrl: '', videoId: edxVideoId, - fallbackVideos: '', + fallbackVideos: [], }); }); }); describe('when there is no edx video id', () => { it('returns the youtube url for video source and html5 sources for fallback videos', () => { - expect(thunkActions.determineVideoSource({ + expect(thunkActions.determineVideoSources({ edxVideoId: '', youtubeId, html5Sources, })).toEqual({ - videoSource: youtubeUrl, + videoUrl: youtubeUrl, videoId: '', fallbackVideos: html5Sources, }); }); }); describe('when there is no edx video id and no youtube id', () => { - it('returns the first html5 source for video source and the rest for fallback videos', () => { - expect(thunkActions.determineVideoSource({ + it('returns the first html5 source for video url and the rest for fallback videos', () => { + expect(thunkActions.determineVideoSources({ edxVideoId: '', youtubeId: '', html5Sources, })).toEqual({ - videoSource: 'htmLOne', + videoUrl: 'htmLOne', videoId: '', fallbackVideos: ['hTMlTwo', 'htMLthrEE'], }); }); it('returns the html5 source for video source and an array with 2 empty values for fallback videos', () => { - expect(thunkActions.determineVideoSource({ + expect(thunkActions.determineVideoSources({ edxVideoId: '', youtubeId: '', html5Sources: ['htmlOne'], })).toEqual({ - videoSource: 'htmlOne', + videoUrl: 'htmlOne', videoId: '', fallbackVideos: [], }); @@ -246,12 +246,12 @@ describe('video thunkActions', () => { }); describe('when there is no edx video id, no youtube id and no html5 sources', () => { it('returns an empty string for video source and an array with 2 empty values for fallback videos', () => { - expect(thunkActions.determineVideoSource({ + expect(thunkActions.determineVideoSources({ edxVideoId: '', youtubeId: '', html5Sources: [], })).toEqual({ - videoSource: '', + videoUrl: '', videoId: '', fallbackVideos: [], }); diff --git a/src/editors/data/services/cms/api.js b/src/editors/data/services/cms/api.js index 434df60f5..66183e99a 100644 --- a/src/editors/data/services/cms/api.js +++ b/src/editors/data/services/cms/api.js @@ -157,7 +157,7 @@ export const apiMethods = { youtubeId, } = module.processVideoIds({ videoId: content.videoId, - videoSource: content.videoSource, + videoUrl: content.videoSource, fallbackVideos: content.fallbackVideos, }); response = { @@ -217,21 +217,18 @@ export const loadImages = (rawImages) => camelizeKeys(rawImages).reduce( export const processVideoIds = ({ videoId, - videoSource, + videoUrl, fallbackVideos, - edxVideoId, }) => { - let newEdxVideoId = edxVideoId; let youtubeId = ''; const html5Sources = []; - // overwrite videoId if source is changed. - if (module.isEdxVideo(videoId)) { - newEdxVideoId = videoId; - } else if (module.parseYoutubeId(videoSource)) { - youtubeId = module.parseYoutubeId(videoSource); - } else if (videoSource) { - html5Sources.push(videoSource); + if (videoUrl) { + if (module.parseYoutubeId(videoUrl)) { + youtubeId = module.parseYoutubeId(videoUrl); + } else { + html5Sources.push(videoUrl); + } } if (fallbackVideos) { @@ -239,7 +236,7 @@ export const processVideoIds = ({ } return { - edxVideoId: newEdxVideoId, + edxVideoId: videoId, html5Sources, youtubeId, }; diff --git a/src/editors/data/services/cms/api.test.js b/src/editors/data/services/cms/api.test.js index 6426f93d5..004f660dc 100644 --- a/src/editors/data/services/cms/api.test.js +++ b/src/editors/data/services/cms/api.test.js @@ -333,7 +333,8 @@ describe('cms api', () => { }); describe('processVideoIds', () => { const edxVideoId = 'eDXviDEoid'; - const youtubeId = 'yOuTuBeID'; + const youtubeId = 'yOuTuBeUrL'; + const youtubeUrl = `https://youtu.be/${youtubeId}`; const html5Sources = [ 'sOuRce1', 'sourCE2', @@ -341,15 +342,14 @@ describe('cms api', () => { afterEach(() => { jest.restoreAllMocks(); }); - describe('if the videoSource is an edx video id', () => { + describe('if there is a video id', () => { beforeEach(() => { jest.spyOn(api, 'isEdxVideo').mockReturnValue(true); - jest.spyOn(api, 'parseYoutubeId').mockReturnValue(null); + jest.spyOn(api, 'parseYoutubeId').mockReturnValue(youtubeId); }); it('returns edxVideoId when there are no fallbackVideos', () => { expect(api.processVideoIds({ - edxVideoId, - videoSource: '', + videoUrl: '', fallbackVideos: [], videoId: edxVideoId, })).toEqual({ @@ -360,42 +360,39 @@ describe('cms api', () => { }); it('returns edxVideoId and html5Sources when there are fallbackVideos', () => { expect(api.processVideoIds({ - edxVideoId, - videoSource: 'edxVideoId', + videoUrl: youtubeUrl, fallbackVideos: html5Sources, videoId: edxVideoId, })).toEqual({ edxVideoId, html5Sources, - youtubeId: '', + youtubeId, }); }); }); - describe('if the videoSource is a youtube url', () => { + describe('if there is a youtube url', () => { beforeEach(() => { jest.spyOn(api, 'isEdxVideo').mockReturnValue(false); jest.spyOn(api, 'parseYoutubeId').mockReturnValue(youtubeId); }); it('returns youtubeId when there are no fallbackVideos', () => { expect(api.processVideoIds({ - edxVideoId, - videoSource: edxVideoId, + videoUrl: youtubeUrl, fallbackVideos: [], videoId: '', })).toEqual({ - edxVideoId, + edxVideoId: '', html5Sources: [], youtubeId, }); }); it('returns youtubeId and html5Sources when there are fallbackVideos', () => { expect(api.processVideoIds({ - edxVideoId, - videoSource: edxVideoId, + videoUrl: youtubeUrl, fallbackVideos: html5Sources, videoId: '', })).toEqual({ - edxVideoId, + edxVideoId: '', html5Sources, youtubeId, }); @@ -408,24 +405,22 @@ describe('cms api', () => { }); it('returns html5Sources when there are no fallbackVideos', () => { expect(api.processVideoIds({ - edxVideoId, - videoSource: html5Sources[0], + videoUrl: html5Sources[0], fallbackVideos: [], videoId: '', })).toEqual({ - edxVideoId, + edxVideoId: '', html5Sources: [html5Sources[0]], youtubeId: '', }); }); it('returns html5Sources when there are fallbackVideos', () => { expect(api.processVideoIds({ - edxVideoId, - videoSource: html5Sources[0], + videoUrl: html5Sources[0], fallbackVideos: [html5Sources[1]], videoId: '', })).toEqual({ - edxVideoId, + edxVideoId: '', html5Sources, youtubeId: '', }); diff --git a/src/editors/data/services/cms/mockApi.js b/src/editors/data/services/cms/mockApi.js index ac17d7ff5..68dea813d 100644 --- a/src/editors/data/services/cms/mockApi.js +++ b/src/editors/data/services/cms/mockApi.js @@ -287,3 +287,7 @@ export const fetchStudioView = ({ blockId, studioEndpointUrl }) => { }, }); }; + +export const checkTranscriptsForImport = () => mockPromise({}); + +export const uploadTranscript = () => mockPromise({}); From 2a9851544e3d0fe8d8b5f5dc7e30d01ff41abd36 Mon Sep 17 00:00:00 2001 From: connorhaugh <49422820+connorhaugh@users.noreply.github.com> Date: Thu, 12 Jan 2023 13:23:02 -0500 Subject: [PATCH 15/29] Fix: advanced problem template reference (#194) * fix: correctly reference advanced problem templates * fix: remove console log --- .../ProblemEditor/components/SelectTypeModal/hooks.js | 2 +- .../ProblemEditor/components/SelectTypeModal/hooks.test.js | 2 +- .../data/constants/advancedOlxTemplates/circuitschematic.js | 2 +- src/editors/data/constants/advancedOlxTemplates/customgrader.js | 2 +- .../data/constants/advancedOlxTemplates/drag_and_drop.js | 2 +- .../data/constants/advancedOlxTemplates/formularesponse.js | 2 +- .../data/constants/advancedOlxTemplates/imageresponse.js | 2 +- .../data/constants/advancedOlxTemplates/jsinput_response.js | 2 +- .../data/constants/advancedOlxTemplates/problem_with_hint.js | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js index 4adec09b5..0f5a13e75 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js @@ -20,7 +20,7 @@ export const selectHooks = () => { export const onSelect = (selected, updateField) => () => { if (Object.values(AdvanceProblemKeys).includes(selected)) { - updateField({ problemType: ProblemTypeKeys.ADVANCED, rawOLX: AdvanceProblems[selected] }); + updateField({ problemType: ProblemTypeKeys.ADVANCED, rawOLX: AdvanceProblems[selected].template }); } else { const newOLX = ProblemTypes[selected].template; const { settings, ...newState } = getDataFromOlx({ rawOLX: newOLX, rawSettings: {} }); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js index 206205487..771b58b91 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js @@ -48,7 +48,7 @@ describe('SelectTypeModal hooks', () => { module.onSelect(mockAdvancedSelected, mockUpdateField)(); expect(mockUpdateField).toHaveBeenCalledWith({ problemType: ProblemTypeKeys.ADVANCED, - rawOLX: AdvanceProblems[mockAdvancedSelected], + rawOLX: AdvanceProblems[mockAdvancedSelected].template, }); }); test('updateField is called with selected on visual propblems', () => { diff --git a/src/editors/data/constants/advancedOlxTemplates/circuitschematic.js b/src/editors/data/constants/advancedOlxTemplates/circuitschematic.js index 571b7c484..6dc5beb85 100644 --- a/src/editors/data/constants/advancedOlxTemplates/circuitschematic.js +++ b/src/editors/data/constants/advancedOlxTemplates/circuitschematic.js @@ -89,4 +89,4 @@ export const circuitSchematic = ` `; -export default { circuitSchematic }; +export default circuitSchematic; diff --git a/src/editors/data/constants/advancedOlxTemplates/customgrader.js b/src/editors/data/constants/advancedOlxTemplates/customgrader.js index 914fe3bb3..cd0f50555 100644 --- a/src/editors/data/constants/advancedOlxTemplates/customgrader.js +++ b/src/editors/data/constants/advancedOlxTemplates/customgrader.js @@ -76,4 +76,4 @@ export const customGrader = ` `; -export default { customGrader }; +export default customGrader; diff --git a/src/editors/data/constants/advancedOlxTemplates/drag_and_drop.js b/src/editors/data/constants/advancedOlxTemplates/drag_and_drop.js index 40d313652..05f6c77f8 100644 --- a/src/editors/data/constants/advancedOlxTemplates/drag_and_drop.js +++ b/src/editors/data/constants/advancedOlxTemplates/drag_and_drop.js @@ -85,4 +85,4 @@ export const dragAndDrop = ` `; -export default { dragAndDrop }; +export default dragAndDrop; diff --git a/src/editors/data/constants/advancedOlxTemplates/formularesponse.js b/src/editors/data/constants/advancedOlxTemplates/formularesponse.js index ea5d43987..b77a166ae 100644 --- a/src/editors/data/constants/advancedOlxTemplates/formularesponse.js +++ b/src/editors/data/constants/advancedOlxTemplates/formularesponse.js @@ -13,4 +13,4 @@ export const formulaResponse = ` `; -export default { formulaResponse }; +export default formulaResponse; diff --git a/src/editors/data/constants/advancedOlxTemplates/imageresponse.js b/src/editors/data/constants/advancedOlxTemplates/imageresponse.js index b3b7acb6a..36ac9e2c6 100644 --- a/src/editors/data/constants/advancedOlxTemplates/imageresponse.js +++ b/src/editors/data/constants/advancedOlxTemplates/imageresponse.js @@ -30,4 +30,4 @@ export const imageResponse = ` `; -export default { imageResponse }; +export default imageResponse; diff --git a/src/editors/data/constants/advancedOlxTemplates/jsinput_response.js b/src/editors/data/constants/advancedOlxTemplates/jsinput_response.js index 3af5a3761..356a96d66 100644 --- a/src/editors/data/constants/advancedOlxTemplates/jsinput_response.js +++ b/src/editors/data/constants/advancedOlxTemplates/jsinput_response.js @@ -78,4 +78,4 @@ export const jsInputResponse = ` `; -export default { jsInputResponse }; +export default jsInputResponse; diff --git a/src/editors/data/constants/advancedOlxTemplates/problem_with_hint.js b/src/editors/data/constants/advancedOlxTemplates/problem_with_hint.js index 10be3531c..fb79a28d2 100644 --- a/src/editors/data/constants/advancedOlxTemplates/problem_with_hint.js +++ b/src/editors/data/constants/advancedOlxTemplates/problem_with_hint.js @@ -43,4 +43,4 @@ export const problemWithHint = ` `; -export default { problemWithHint }; +export default problemWithHint; From 990e35bdc2e31b69e88e9fe899f58bdf117f0a40 Mon Sep 17 00:00:00 2001 From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> Date: Thu, 12 Jan 2023 15:30:06 -0500 Subject: [PATCH 16/29] feat: remove question box branding + menu (#192) This PR address the console.log errors from the question box tinyMCE, removing the tinyMCE branding and menu. --- src/editors/containers/ProblemEditor/hooks.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/editors/containers/ProblemEditor/hooks.js b/src/editors/containers/ProblemEditor/hooks.js index cb0a539dd..fea936b0c 100644 --- a/src/editors/containers/ProblemEditor/hooks.js +++ b/src/editors/containers/ProblemEditor/hooks.js @@ -2,6 +2,7 @@ import { useRef, useCallback, useState, useEffect, } from 'react'; import { ProblemTypeKeys } from '../../data/constants/problem'; +import tinyMCEStyles from '../../data/constants/tinyMCEStyles'; import { StrictDict } from '../../utils'; import * as module from './hooks'; @@ -19,6 +20,13 @@ export const problemEditorConfig = ({ setEditorRef(editor); }, initialValue: question || '', + init: { + skin: false, + content_css: false, + content_style: tinyMCEStyles, + menubar: false, + branding: false, + }, onFocusOut: () => { const content = editorRef.current.getContent(); updateQuestion(content); From 1a370c12d9d2c3e62ac996ba2cfed262f722ad57 Mon Sep 17 00:00:00 2001 From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> Date: Fri, 13 Jan 2023 10:04:01 -0500 Subject: [PATCH 17/29] feat: update problem type titles + small ui fixes (#195) --- .../__snapshots__/AnswerOption.test.jsx.snap | 24 +- .../__snapshots__/index.test.jsx.snap | 8 +- .../__snapshots__/HintRow.test.jsx.snap | 8 +- .../__snapshots__/TypeCard.test.jsx.snap | 12 +- .../__snapshots__/index.test.jsx.snap | 16 +- .../__snapshots__/index.test.jsx.snap | 8 +- .../SelectTypeWrapper/index.jsx | 7 +- .../SelectTypeWrapper/messages.js | 5 + .../__snapshots__/index.test.jsx.snap | 14 +- .../content/AdvanceTypeSelect.jsx | 16 +- .../content/ProblemTypeSelect.jsx | 2 +- .../AdvanceTypeSelect.test.jsx.snap | 320 ++++++++++++++---- .../__snapshots__/Preview.test.jsx.snap | 30 +- .../ProblemTypeSelect.test.jsx.snap | 85 +++-- .../SelectTypeModal/content/messages.js | 13 +- .../components/SelectTypeModal/index.jsx | 2 +- .../DurationWidget.test.jsx.snap | 8 +- src/editors/data/constants/problem.js | 10 +- src/setupTest.js | 1 + 19 files changed, 408 insertions(+), 181 deletions(-) diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap index 62286c074..4396f8e4b 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap @@ -9,7 +9,7 @@ exports[`AnswerOption render snapshot: renders correct option with feedback 1`] - - - +
- - + @@ -91,7 +91,7 @@ exports[`AnswerOption render snapshot: renders correct option with feedback 1`] onClick={[Function]} variant="primary" /> - + `; @@ -105,7 +105,7 @@ exports[`AnswerOption render snapshot: renders correct option with selected unse - - - +
- - + @@ -220,7 +220,7 @@ exports[`AnswerOption render snapshot: renders correct option with selected unse onClick={[Function]} variant="primary" /> - + `; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap index 8a6aa9fa0..92eda9137 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap @@ -12,7 +12,7 @@ exports[`SettingsWidget snapshot snapshot: renders Settings widget page 1`] = ` - + @@ -84,7 +84,7 @@ exports[`SettingsWidget snapshot snapshot: renders Settings widget page 1`] = ` - + @@ -103,7 +103,7 @@ exports[`SettingsWidget snapshot snapshot: renders Settings widget page advanced - + @@ -175,7 +175,7 @@ exports[`SettingsWidget snapshot snapshot: renders Settings widget page advanced - + diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/HintRow.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/HintRow.test.jsx.snap index ea0adb253..a106c71a8 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/HintRow.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/HintRow.test.jsx.snap @@ -5,7 +5,7 @@ exports[`HintRow snapshot snapshot: renders hints row 1`] = ` fluid={true} > - @@ -15,8 +15,8 @@ exports[`HintRow snapshot snapshot: renders hints row 1`] = ` value="hint_1" /> - - + - + `; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap index 9b9ef8114..e18d0dc4b 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap @@ -2,14 +2,14 @@ exports[`TypeCard snapshot snapshot: renders type setting card 1`] = ` - - - + - +
@@ -41,21 +41,21 @@ exports[`EditorProblemView component renders simple view 1`] = ` fluid={true} > - - - + - +
diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.jsx.snap index d88ab2baf..244b85247 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.jsx.snap @@ -4,9 +4,11 @@ exports[`SelectTypeWrapper snapshot 1`] = `
-

- Select Problem type -

+
diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx index edc54d6c4..f40e50260 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx @@ -1,11 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; - +import { injectIntl, FormattedMessage } from '@edx/frontend-platform/i18n'; import { Icon, ModalDialog, IconButton } from '@edx/paragon'; import { Close } from '@edx/paragon/icons'; import SelectTypeFooter from './SelectTypeFooter'; import * as hooks from '../../../../EditorContainer/hooks'; +import messages from './messages'; export const SelectTypeWrapper = ({ children, @@ -18,7 +19,7 @@ export const SelectTypeWrapper = ({
-

Select Problem type

+
- + - - + + - + - + `; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx index 2ecfa2568..e61eef086 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx @@ -8,6 +8,8 @@ import { Icon, OverlayTrigger, Tooltip, + Hyperlink, + Col, } from '@edx/paragon'; import { ArrowBack } from '@edx/paragon/icons'; import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; @@ -22,8 +24,8 @@ export const AdvanceTypeSelect = ({ }) => { const handleChange = e => { setSelected(e.target.value); }; return ( -
- + + setSelected(ProblemTypeKeys.SINGLESELECT)} /> @@ -56,7 +58,7 @@ export const AdvanceTypeSelect = ({ )} > -
+
{intl.formatMessage(messages.problemSupportStatus, { supportStatus: data.status })}
@@ -74,7 +76,13 @@ export const AdvanceTypeSelect = ({ })} -
+ + + + ); }; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx index e2c8fea06..a721a6d8c 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx @@ -24,7 +24,7 @@ export const ProblemTypeSelect = ({ {Object.values(ProblemTypeKeys).map((key) => ( key !== 'advanced' ? ( - + {ProblemTypes[key].title} ) diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap index 040a7ca10..5dcf85a1c 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap @@ -1,11 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default props 1`] = ` -
-
+
Not supported
@@ -133,7 +137,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default } placement="right" > -
+
Provisional
@@ -175,7 +181,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default } placement="right" > -
+
Not supported
@@ -217,7 +225,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default } placement="right" > -
+
Not supported
@@ -270,22 +280,36 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default } placement="right" > -
+
Not supported
-
+ + + + `; exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is circuitschematic 1`] = ` -
-
+
Not supported
@@ -413,7 +439,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Provisional
@@ -455,7 +483,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -497,7 +527,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -550,22 +582,36 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
-
+ + + + `; exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is customgrader 1`] = ` -
-
+
Not supported
@@ -693,7 +741,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Provisional
@@ -735,7 +785,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -777,7 +829,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -830,22 +884,36 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
-
+ + + + `; exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is drag_and_drop 1`] = ` -
-
+
Not supported
@@ -973,7 +1043,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Provisional
@@ -1015,7 +1087,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -1057,7 +1131,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -1110,22 +1186,36 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
-
+ + + + `; exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is formularesponse 1`] = ` -
-
+
Not supported
@@ -1253,7 +1345,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Provisional
@@ -1295,7 +1389,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -1337,7 +1433,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -1390,22 +1488,36 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
-
+ + + + `; exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is imageresponse 1`] = ` -
-
+
Not supported
@@ -1533,7 +1647,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Provisional
@@ -1575,7 +1691,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -1617,7 +1735,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -1670,22 +1790,36 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
-
+ + + + `; exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is jsinput_response 1`] = ` -
-
+
Not supported
@@ -1813,7 +1949,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Provisional
@@ -1855,7 +1993,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -1897,7 +2037,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -1950,22 +2092,36 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
-
+ + + + `; exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is problem_with_hint 1`] = ` -
-
+
Not supported
@@ -2093,7 +2251,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Provisional
@@ -2135,7 +2295,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -2177,7 +2339,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
@@ -2230,12 +2394,24 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem } placement="right" > -
+
Not supported
-
+ + + + `; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap index 8dd2e23bf..828676b0d 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap @@ -9,7 +9,7 @@ exports[`Preview snapshots snapshots: renders as expected with problemType is ch
- Multi Select Problem + Multi-select problem
A preview illustration of a {problemType, select,
@@ -34,8 +34,8 @@ exports[`Preview snapshots snapshots: renders as expected with problemType is ch
     target= @@ -49,7 +49,7 @@ exports[`Preview snapshots snapshots: renders as expected with problemType is mu
- Single Select Problem + Single select problem
A preview illustration of a {problemType, select,
@@ -74,8 +74,8 @@ exports[`Preview snapshots snapshots: renders as expected with problemType is mu
     target= @@ -89,7 +89,7 @@ exports[`Preview snapshots snapshots: renders as expected with problemType is nu
- Numeric Response Problem + Numerical input problem
A preview illustration of a {problemType, select,
@@ -114,8 +114,8 @@ exports[`Preview snapshots snapshots: renders as expected with problemType is nu
     target= @@ -129,7 +129,7 @@ exports[`Preview snapshots snapshots: renders as expected with problemType is op
- Dropdown Problem + Dropdown problem
A preview illustration of a {problemType, select,
@@ -154,8 +154,8 @@ exports[`Preview snapshots snapshots: renders as expected with problemType is op
     target= @@ -169,7 +169,7 @@ exports[`Preview snapshots snapshots: renders as expected with problemType is st
- Text Input Problem + Text input problem
A preview illustration of a {problemType, select,
@@ -194,8 +194,8 @@ exports[`Preview snapshots snapshots: renders as expected with problemType is st
     target= diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap index bf2f8389e..618529bb7 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap @@ -10,43 +10,48 @@ exports[`ProblemTypeSelect snapshot DROPDOWN 1`] = ` > - Single Select Problem + Single select - Multi Select Problem + Multi-select - Dropdown Problem + Dropdown - Numeric Response Problem + Numerical input - Text Input Problem + Text input
+
+ { setAnswer({ title: e.target.value }); }} + placeholder={intl.formatMessage(messages.answerTextboxPlaceholder)} + /> + + - - - - { setAnswer({ title: e.target.value }); }} - placeholder={intl.formatMessage(messages.answerTextboxPlaceholder)} - /> - - -
- {displayFeedbackControl(answer)} -
-
- - - - - - +
+
+
+ - - - + + +
); }; @@ -164,22 +90,6 @@ AnswerOption.propTypes = { problemType: PropTypes.string.isRequired, }; -FeedbackControl.propTypes = { - feedback: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, - labelMessage: PropTypes.string.isRequired, - labelMessageBoldUnderline: PropTypes.string.isRequired, - key: PropTypes.string.isRequired, - answer: answerOptionProps.isRequired, - intl: intlShape.isRequired, -}; - -Checker.propTypes = { - hasSingleAnswer: PropTypes.bool.isRequired, - answer: answerOptionProps.isRequired, - setAnswer: PropTypes.func.isRequired, -}; - export const mapStateToProps = (state) => ({ problemType: selectors.problem.problemType(state), }); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap index 4396f8e4b..c6bf42cc8 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap @@ -2,17 +2,40 @@ exports[`AnswerOption render snapshot: renders correct option with feedback 1`] = ` - - - +
+
+ + + - - - - -
- - - - - - - , - } - } - /> - - - -
- - - - - - + +
+
+ - - + + +
`; exports[`AnswerOption render snapshot: renders correct option with selected unselected feedback 1`] = ` - - - +
+
+ + + - - - - -
- - - - - - - , - } - } - /> - - - - - - - - - - , - } - } - /> - - - -
- - - - - - + +
+
+ - - + + +
`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/__snapshots__/index.test.jsx.snap new file mode 100644 index 000000000..d8108e18d --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/__snapshots__/index.test.jsx.snap @@ -0,0 +1,23 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Checker component with multiple answers 1`] = ` + + A + +`; + +exports[`Checker component with single answer 1`] = ` + + A + +`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.jsx new file mode 100644 index 000000000..de39a3d87 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.jsx @@ -0,0 +1,32 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Form } from '@edx/paragon'; + +const Checker = ({ + hasSingleAnswer, answer, setAnswer, +}) => { + let CheckerType = Form.Checkbox; + if (hasSingleAnswer) { + CheckerType = Form.Radio; + } + return ( + setAnswer({ correct: e.target.checked })} + checked={answer.correct} + > + {answer.id} + + ); +}; +Checker.propTypes = { + hasSingleAnswer: PropTypes.bool.isRequired, + answer: PropTypes.shape({ + correct: PropTypes.bool, + id: PropTypes.number, + }).isRequired, + setAnswer: PropTypes.func.isRequired, +}; + +export default Checker; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.test.jsx new file mode 100644 index 000000000..19998a536 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.test.jsx @@ -0,0 +1,22 @@ +import { shallow } from 'enzyme'; +import Checker from '.'; + +const props = { + hasSingleAnswer: true, + answer: { + id: 'A', + title: 'Answer 1', + correct: true, + selectedFeedback: 'some feedback', + }, + setAnswer: jest.fn(), +}; +describe('Checker component', () => { + test('with single answer', () => { + expect(shallow()).toMatchSnapshot(); + }); + + test('with multiple answers', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.jsx new file mode 100644 index 000000000..4101f944a --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.jsx @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; + +import { answerOptionProps } from '../../../../../../../data/services/cms/types'; +import FeedbackControl from './FeedbackControl'; +import { messages } from './messages'; + +export const FeedbackBox = ({ + answer, setAnswer, intl, +}) => { + const props = { + onChange: (e) => setAnswer({ selectedFeedback: e.target.value }), + answer, + intl, + }; + + return ( +
+ + +
+ ); +}; +FeedbackBox.propTypes = { + answer: answerOptionProps.isRequired, + setAnswer: PropTypes.func.isRequired, + intl: intlShape.isRequired, +}; + +export default injectIntl(FeedbackBox); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.test.jsx new file mode 100644 index 000000000..117c48255 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.test.jsx @@ -0,0 +1,22 @@ +import { shallow } from 'enzyme'; +import { FeedbackBox } from './FeedbackBox'; + +const answerWithFeedback = { + id: 'A', + title: 'Answer 1', + correct: true, + selectedFeedback: 'some feedback', + unselectedFeedback: 'unselectedFeedback', +}; + +const props = { + answer: answerWithFeedback, + intl: {}, + setAnswer: jest.fn(), +}; + +describe('FeedbackBox component', () => { + test('renders', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackControl.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackControl.jsx new file mode 100644 index 000000000..12ff8091d --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackControl.jsx @@ -0,0 +1,38 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage, intlShape } from '@edx/frontend-platform/i18n'; +import { Form } from '@edx/paragon'; + +import { answerOptionProps } from '../../../../../../../data/services/cms/types'; +import messages from './messages'; + +const FeedbackControl = ({ + feedback, onChange, labelMessage, labelMessageBoldUnderline, answer, intl, +}) => ( + + + , + }} + /> + + + +); +FeedbackControl.propTypes = { + feedback: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, + labelMessage: PropTypes.string.isRequired, + labelMessageBoldUnderline: PropTypes.string.isRequired, + answer: answerOptionProps.isRequired, + intl: intlShape.isRequired, +}; + +export default FeedbackControl; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackControl.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackControl.test.jsx new file mode 100644 index 000000000..93d6499d4 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackControl.test.jsx @@ -0,0 +1,26 @@ +import { shallow } from 'enzyme'; +import FeedbackControl from './FeedbackControl'; + +const answerWithFeedback = { + id: 'A', + title: 'Answer 1', + correct: true, + selectedFeedback: 'some feedback', + unselectedFeedback: 'unselectedFeedback', +}; + +const props = { + answer: answerWithFeedback, + intl: { formatMessage: jest.fn() }, + setAnswer: jest.fn(), + feedback: 'feedback', + onChange: jest.fn(), + labelMessage: 'msg', + labelMessageBoldUnderline: 'msg', +}; + +describe('FeedbackControl component', () => { + test('renders', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/__snapshots__/FeedbackBox.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/__snapshots__/FeedbackBox.test.jsx.snap new file mode 100644 index 000000000..ae9ffb472 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/__snapshots__/FeedbackBox.test.jsx.snap @@ -0,0 +1,66 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FeedbackBox component renders 1`] = ` +
+ + +
+`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/__snapshots__/FeedbackControl.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/__snapshots__/FeedbackControl.test.jsx.snap new file mode 100644 index 000000000..40a7fde43 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/__snapshots__/FeedbackControl.test.jsx.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FeedbackControl component renders 1`] = ` + + + + + + + , + } + } + /> + + + +`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/index.jsx new file mode 100644 index 000000000..bce02835a --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/index.jsx @@ -0,0 +1,2 @@ +export { default as FeedbackBox } from './FeedbackBox'; +export { default as FeedbackControl } from './FeedbackControl'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/messages.js b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/messages.js new file mode 100644 index 000000000..a580c9c7e --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/messages.js @@ -0,0 +1,34 @@ +export const messages = { + feedbackPlaceholder: { + id: 'authoring.answerwidget.feedback.placeholder', + defaultMessage: 'Feedback message', + description: 'Placeholder text for feedback text', + }, + feedbackToggleIconAltText: { + id: 'authoring.answerwidget.feedback.icon.alt', + defaultMessage: 'Toggle feedback', + description: 'Alt text for feedback toggle icon', + }, + selectedFeedbackLabel: { + id: 'authoring.answerwidget.feedback.selected.label', + defaultMessage: 'Show following feedback when {answerId} {boldunderline}:', + description: 'Label text for feedback if option is selected', + }, + selectedFeedbackLabelBoldUnderlineText: { + id: 'authoring.answerwidget.feedback.selected.label.boldunderline', + defaultMessage: 'is selected', + description: 'Bold & underlined text for feedback if option is selected', + }, + unSelectedFeedbackLabel: { + id: 'authoring.answerwidget.feedback.unselected.label', + defaultMessage: 'Show following feedback when {answerId} {boldunderline}:', + description: 'Label text for feedback if option is not selected', + }, + unSelectedFeedbackLabelBoldUnderlineText: { + id: 'authoring.answerwidget.feedback.unselected.label.boldunderline', + defaultMessage: 'is not selected', + description: 'Bold & underlined text for feedback if option is not selected', + }, +}; + +export default messages; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/index.scss b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/index.scss index 622a591a9..bd272ed1e 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/index.scss +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/index.scss @@ -1,11 +1,59 @@ -.problem-answer { - padding: 12px; - color: #00262B; - .problem-answer-title { - font-weight: bold; - } +@import '../../../../../../colors.scss'; - .problem-answer-description { - font-size: 0.9rem; - } +.problem-answer { + padding: 12px; + + .problem-answer-title { + font-weight: bold; + } + + .problem-answer-description { + font-size: 0.9rem; + } } + +.answer-option { + // The paragon icon path is missing "fill: currentColor" + .feedback-icon-button { + &:hover { + &:not(:focus) { + svg { + path { + fill: $white; + } + } + } + } + } + + .answer-option-flex-item-1 { + flex-basis: 8.33%; + } + + .answer-option-flex-item-2 { + flex-basis: 83.34%; + } + + .answer-option-flex-item-3 { + flex-basis: 8.33%; + } + + .answer-option-textarea { + textarea { + border: none; + resize: none; + + &:active, &:hover, &:focus, &:focus-visible { + border: none; + border-right: none; + border-left: none; + border-radius: 0; + box-shadow: none; + } + + &:focus, &:hover { + border-bottom: 1px solid $black; + } + } + } +} \ No newline at end of file diff --git a/src/index.scss b/src/index.scss index c4301db28..491817e5c 100644 --- a/src/index.scss +++ b/src/index.scss @@ -1 +1 @@ -/* styles go here */ +@import './colors.scss'; \ No newline at end of file From 7d21a3d4c9b7bc978d809874e45263b1f73f6b37 Mon Sep 17 00:00:00 2001 From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> Date: Tue, 17 Jan 2023 15:41:11 -0500 Subject: [PATCH 20/29] feat: change feedback and delete icons to outline variants (#198) --- package-lock.json | 14 +++---- package.json | 2 +- src/colors.scss | 1 - .../AnswerWidget/AnswerOption.jsx | 14 ++++--- .../AnswerWidget/AnswersContainer.jsx | 4 +- .../__snapshots__/AnswerOption.test.jsx.snap | 6 +-- .../AnswersContainer.test.jsx.snap | 12 ++++-- .../Checker/__snapshots__/index.test.jsx.snap | 4 +- .../AnswerWidget/components/Checker/index.jsx | 2 +- .../components/Feedback/FeedbackBox.jsx | 2 +- .../__snapshots__/FeedbackBox.test.jsx.snap | 2 +- .../EditProblemView/AnswerWidget/index.jsx | 6 +-- .../EditProblemView/AnswerWidget/index.scss | 25 ------------ www/package-lock.json | 39 +++++++++++++------ www/package.json | 2 +- 15 files changed, 65 insertions(+), 70 deletions(-) diff --git a/package-lock.json b/package-lock.json index 62c55aa30..930a72c40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "devDependencies": { "@edx/frontend-build": "^11.0.2", "@edx/frontend-platform": "2.4.0", - "@edx/paragon": "^20.21.3", + "@edx/paragon": "^20.27.0", "@testing-library/dom": "^8.13.0", "@testing-library/react": "12.1.1", "@testing-library/user-event": "^13.5.0", @@ -4327,9 +4327,9 @@ } }, "node_modules/@edx/paragon": { - "version": "20.21.5", - "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.21.5.tgz", - "integrity": "sha512-7+pRDS3MejiF3tsOFs9R6PC66zZJb8eN2nYObNWnyUmKZ7DfzzpHVsDuUYUg+J5cm3dYMY2imyIrMrqov6ettA==", + "version": "20.27.0", + "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.27.0.tgz", + "integrity": "sha512-jy62ZEBdAVlsP6tAm1/YDyMtc9fiD47H00whoW+y2Z+lLZqPsv6D5boIPQIcdBeg0W4f2gCU4TEy2+b2q8mYGA==", "dev": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.1.1", @@ -37695,9 +37695,9 @@ } }, "@edx/paragon": { - "version": "20.21.5", - "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.21.5.tgz", - "integrity": "sha512-7+pRDS3MejiF3tsOFs9R6PC66zZJb8eN2nYObNWnyUmKZ7DfzzpHVsDuUYUg+J5cm3dYMY2imyIrMrqov6ettA==", + "version": "20.27.0", + "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.27.0.tgz", + "integrity": "sha512-jy62ZEBdAVlsP6tAm1/YDyMtc9fiD47H00whoW+y2Z+lLZqPsv6D5boIPQIcdBeg0W4f2gCU4TEy2+b2q8mYGA==", "dev": true, "requires": { "@fortawesome/fontawesome-svg-core": "^6.1.1", diff --git a/package.json b/package.json index 555f11e50..bc07d0ddb 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "devDependencies": { "@edx/frontend-build": "^11.0.2", "@edx/frontend-platform": "2.4.0", - "@edx/paragon": "^20.21.3", + "@edx/paragon": "^20.27.0", "@testing-library/dom": "^8.13.0", "@testing-library/react": "12.1.1", "@testing-library/user-event": "^13.5.0", diff --git a/src/colors.scss b/src/colors.scss index a6630db18..f3c642859 100644 --- a/src/colors.scss +++ b/src/colors.scss @@ -1,2 +1 @@ -$white: #fff; $black: #000; \ No newline at end of file diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx index 725dbeddb..73f07b3ea 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx @@ -2,9 +2,12 @@ import React, { memo } from 'react'; import { connect, useDispatch } from 'react-redux'; import PropTypes from 'prop-types'; import { - Collapsible, Icon, IconButton, Form, + Collapsible, + Icon, + IconButton, + Form, } from '@edx/paragon'; -import { Feedback, Delete } from '@edx/paragon/icons'; +import { FeedbackOutline, DeleteOutline } from '@edx/paragon/icons'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import messages from './messages'; @@ -33,7 +36,7 @@ export const AnswerOption = ({ onToggle={toggleFeedback} className="answer-option d-flex flex-row justify-content-between flex-nowrap pb-2 pt-2" > -
+
{ const { hasSingleAnswer } = initializeAnswerContainer({ answers, problemType, updateField }); return ( -
+
{answers.map((answer) => ( ))} - + ); }; ProblemTypeSelect.propTypes = { diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap index 659b887c5..971ee0749 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap @@ -41,7 +41,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default id="blankadvanced" value="blankadvanced" > - Blank advance problem + Blank advanced problem @@ -299,7 +299,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem id="blankadvanced" value="blankadvanced" > - Blank advance problem + Blank advanced problem @@ -557,7 +557,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem id="blankadvanced" value="blankadvanced" > - Blank advance problem + Blank advanced problem @@ -815,7 +815,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem id="blankadvanced" value="blankadvanced" > - Blank advance problem + Blank advanced problem @@ -1073,7 +1073,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem id="blankadvanced" value="blankadvanced" > - Blank advance problem + Blank advanced problem @@ -1331,7 +1331,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem id="blankadvanced" value="blankadvanced" > - Blank advance problem + Blank advanced problem @@ -1589,7 +1589,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem id="blankadvanced" value="blankadvanced" > - Blank advance problem + Blank advanced problem @@ -1847,7 +1847,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem id="blankadvanced" value="blankadvanced" > - Blank advance problem + Blank advanced problem diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap index 828676b0d..9cc84af14 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap @@ -3,8 +3,14 @@ exports[`Preview snapshots snapshots: renders as expected with default props 1`] = `""`; exports[`Preview snapshots snapshots: renders as expected with problemType is choiceresponse 1`] = ` -
-
+ `; exports[`Preview snapshots snapshots: renders as expected with problemType is multiplechoiceresponse 1`] = ` -
-
+ `; exports[`Preview snapshots snapshots: renders as expected with problemType is numericalresponse 1`] = ` -
-
+ `; exports[`Preview snapshots snapshots: renders as expected with problemType is optionresponse 1`] = ` -
-
+ `; exports[`Preview snapshots snapshots: renders as expected with problemType is stringresponse 1`] = ` -
-
+ `; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap index 618529bb7..775965cf3 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap @@ -1,7 +1,14 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`ProblemTypeSelect snapshot DROPDOWN 1`] = ` - + - + `; exports[`ProblemTypeSelect snapshot MULTISELECT 1`] = ` - + - + `; exports[`ProblemTypeSelect snapshot NUMERIC 1`] = ` - + - + `; exports[`ProblemTypeSelect snapshot SINGLESELECT 1`] = ` - + - + `; exports[`ProblemTypeSelect snapshot TEXTINPUT 1`] = ` - + - + `; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx index 62fe35081..0c02e0647 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Col, Row } from '@edx/paragon'; +import { Row, Stack } from '@edx/paragon'; import ProblemTypeSelect from './content/ProblemTypeSelect'; import Preview from './content/Preview'; import AdvanceTypeSelect from './content/AdvanceTypeSelect'; @@ -17,16 +17,12 @@ export const SelectTypeModal = ({ return ( - + {(!Object.values(AdvanceProblemKeys).includes(selected)) ? ( - <> - - - - - - - + + + + ) : } diff --git a/src/editors/data/constants/problem.js b/src/editors/data/constants/problem.js index ecf2623f2..20bca15ef 100644 --- a/src/editors/data/constants/problem.js +++ b/src/editors/data/constants/problem.js @@ -83,7 +83,7 @@ export const AdvanceProblemKeys = StrictDict({ export const AdvanceProblems = StrictDict({ [AdvanceProblemKeys.BLANK]: { - title: 'Blank advance problem', + title: 'Blank advanced problem', status: '', template: '', }, From 5aca835a4b021a9796e9fe4cb7ba62e65911567c Mon Sep 17 00:00:00 2001 From: Jesper Hodge <19345795+jesperhodge@users.noreply.github.com> Date: Fri, 20 Jan 2023 15:59:30 -0500 Subject: [PATCH 28/29] Fix problem editor margins and borders (#203) * fix: fix border and allow customizing of tinymce style * fix: make tinymce widget look like on figma * fix: update settingsoptions card border * fix: header typography * fix: spacings * chore: update snapshots * Update src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> * Update src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> * Update src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> * Update src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> * Update src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> * Update src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> * fix: html and react problems in problem editor * chore: update snapshots * chore: apply pr suggestions * chore: fix test coverage * chore: fix lint * chore: fix tests * chore: fix lint Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> --- package-lock.json | 2 +- package.json | 2 +- src/colors.scss | 1 - .../AnswerWidget/AnswersContainer.jsx | 9 +- .../AnswerWidget/AnswersContainer.test.jsx | 44 +- .../AnswersContainer.test.jsx.snap | 26 +- .../EditProblemView/AnswerWidget/hook.test.js | 12 + .../EditProblemView/AnswerWidget/hooks.js | 21 +- .../EditProblemView/AnswerWidget/index.jsx | 2 +- .../EditProblemView/AnswerWidget/index.scss | 6 +- .../EditProblemView/QuestionWidget/index.jsx | 15 +- .../EditProblemView/QuestionWidget/index.scss | 28 ++ .../SettingsWidget/SettingsOption.jsx | 2 +- .../SettingsOption.test.jsx.snap | 4 +- .../__snapshots__/index.test.jsx.snap | 334 +++++++------- .../EditProblemView/SettingsWidget/index.jsx | 111 +++-- .../SwitchToAdvancedEditorCard.jsx | 2 +- .../SwitchToAdvancedEditorCard.test.jsx.snap | 4 +- .../__snapshots__/index.test.jsx.snap | 2 + .../components/EditProblemView/index.jsx | 2 +- src/editors/containers/ProblemEditor/hooks.js | 13 - src/editors/data/constants/tinyMCEStyles.js | 428 +++++++++--------- src/index.scss | 1 - 23 files changed, 592 insertions(+), 479 deletions(-) delete mode 100644 src/colors.scss create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.scss diff --git a/package-lock.json b/package-lock.json index 930a72c40..27075c386 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "@edx/frontend-platform": "2.4.0", "@edx/paragon": "^20.27.0", "@testing-library/dom": "^8.13.0", - "@testing-library/react": "12.1.1", + "@testing-library/react": "^12.1.1", "@testing-library/user-event": "^13.5.0", "codecov": "3.8.3", "enzyme": "3.11.0", diff --git a/package.json b/package.json index bc07d0ddb..fc5f9b095 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@edx/frontend-platform": "2.4.0", "@edx/paragon": "^20.27.0", "@testing-library/dom": "^8.13.0", - "@testing-library/react": "12.1.1", + "@testing-library/react": "^12.1.1", "@testing-library/user-event": "^13.5.0", "codecov": "3.8.3", "enzyme": "3.11.0", diff --git a/src/colors.scss b/src/colors.scss deleted file mode 100644 index f3c642859..000000000 --- a/src/colors.scss +++ /dev/null @@ -1 +0,0 @@ -$black: #000; \ No newline at end of file diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx index eea060eb5..42c079a4f 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx @@ -6,7 +6,7 @@ import { Add } from '@edx/paragon/icons'; import { FormattedMessage } from '@edx/frontend-platform/i18n'; import messages from './messages'; -import { initializeAnswerContainer } from '../../../hooks'; +import { useAnswerContainer, isSingleAnswerProblem } from './hooks'; import { actions, selectors } from '../../../../../data/redux'; import { answerOptionProps } from '../../../../../data/services/cms/types'; import AnswerOption from './AnswerOption'; @@ -18,9 +18,12 @@ export const AnswersContainer = ({ addAnswer, updateField, }) => { - const { hasSingleAnswer } = initializeAnswerContainer({ answers, problemType, updateField }); + const hasSingleAnswer = isSingleAnswerProblem(problemType); + + useAnswerContainer({ answers, problemType, updateField }); + return ( -
+
{answers.map((answer) => ( ({ + FormattedMessage: ({ defaultMessage }) => (

{defaultMessage}

), + injectIntl: (args) => args, + intlShape: {}, +})); + +jest.mock('./AnswerOption', () => () =>
MockAnswerOption
); + jest.mock('../../../../../data/redux', () => ({ actions: { problem: { @@ -25,10 +38,37 @@ describe('AnswersContainer', () => { }; describe('render', () => { test('snapshot: renders correct default', () => { - expect(shallow()).toMatchSnapshot(); + act(() => { + expect(shallow()).toMatchSnapshot(); + }); }); test('snapshot: renders correctly with answers', () => { - expect(shallow()).toMatchSnapshot(); + act(() => { + expect(shallow( + , + )).toMatchSnapshot(); + }); + }); + + test('with react-testing-library', async () => { + let container = null; + await act(async () => { + const wrapper = render( + , + ); + container = wrapper.container; + }); + + await waitFor(() => expect(container.querySelector('button')).toBeTruthy()); + await new Promise(resolve => setTimeout(resolve, 500)); + + expect(props.updateField).toHaveBeenCalledWith(expect.objectContaining({ correctAnswerCount: 2 })); }); }); describe('mapStateToProps', () => { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap index fef7eb828..07078350c 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap @@ -2,7 +2,7 @@ exports[`AnswersContainer render snapshot: renders correct default 1`] = `
- - - - - - - - - - - - - - - - - - - - - - - - - +
+
-
-`; - -exports[`SettingsWidget snapshot snapshot: renders Settings widget page advanced settings visible 1`] = ` -
-
-

- -

- - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + - - - -
+ + + + + + + + + + + + + + + + + + + + + + + +
+`; + +exports[`SettingsWidget snapshot snapshot: renders Settings widget page advanced settings visible 1`] = ` +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx index cef24d973..b8f0aa374 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx @@ -33,68 +33,65 @@ export const SettingsWidget = ({ const { isAdvancedCardsVisible, showAdvancedCards } = showAdvancedSettingsCards(); return (
-
-

- -

- - - - - - - - - - - - +
+ +
+ + + + + + + + + + + + - - - - - - - - - + + - - - - - - - - - - - - - - - + - - - -
+ + + + + + + + + + + + + + + + + + + + + + +
); }; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx index 297854a5b..e68e10351 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx @@ -12,7 +12,7 @@ export const SwitchToAdvancedEditorCard = ({ }) => { const [isConfirmOpen, setConfirmOpen] = React.useState(false); return ( - + { setConfirmOpen(false); }} diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap index 07768a93c..cbe4b6508 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap @@ -1,7 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`SwitchToAdvancedEditorCard snapshot snapshot: SwitchToAdvancedEditorCard 1`] = ` - + @@ -38,6 +39,7 @@ exports[`EditorProblemView component renders simple view 1`] = ` getContent={[Function]} > diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx index 710c3e635..f43ddd1d2 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx @@ -24,7 +24,7 @@ export const EditProblemView = ({ return ( - + {isAdvancedProblemType ? ( diff --git a/src/editors/containers/ProblemEditor/hooks.js b/src/editors/containers/ProblemEditor/hooks.js index fea936b0c..63ab97f6b 100644 --- a/src/editors/containers/ProblemEditor/hooks.js +++ b/src/editors/containers/ProblemEditor/hooks.js @@ -1,7 +1,6 @@ import { useRef, useCallback, useState, useEffect, } from 'react'; -import { ProblemTypeKeys } from '../../data/constants/problem'; import tinyMCEStyles from '../../data/constants/tinyMCEStyles'; import { StrictDict } from '../../utils'; import * as module from './hooks'; @@ -42,15 +41,3 @@ export const prepareEditorRef = () => { useEffect(() => setRefReady(true), [setRefReady]); return { editorRef, refReady, setEditorRef }; }; - -export const initializeAnswerContainer = ({ answers, problemType, updateField }) => { - const hasSingleAnswer = problemType === ProblemTypeKeys.DROPDOWN || problemType === ProblemTypeKeys.SINGLESELECT; - let answerCount = 0; - answers.forEach(answer => { - if (answer.correct) { - answerCount += 1; - } - }); - updateField({ correctAnswerCount: answerCount }); - return { hasSingleAnswer }; -}; diff --git a/src/editors/data/constants/tinyMCEStyles.js b/src/editors/data/constants/tinyMCEStyles.js index a5c9f4f55..d201a86fd 100644 --- a/src/editors/data/constants/tinyMCEStyles.js +++ b/src/editors/data/constants/tinyMCEStyles.js @@ -1,214 +1,220 @@ -export default `@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400;1,600;1,700&display=swap"); +const getStyles = () => ( + `@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400;1,600;1,700&display=swap"); -.mce-content-body *[contentEditable=false] { - cursor: default; -} -.mce-content-body *[contentEditable=true] { - cursor: text; -} -.mce-content-body div.mce-resizehandle { - background-color: #4099ff; - border-color: #4099ff; - border-style: solid; - border-width: 1px; - box-sizing: border-box; - height: 10px; - position: absolute; - width: 10px; - z-index: 1298; -} -.mce-content-body div.mce-resizehandle:hover { - background-color: #4099ff; -} -.mce-content-body div.mce-resizehandle:nth-of-type(1) { - cursor: nwse-resize; -} -.mce-content-body div.mce-resizehandle:nth-of-type(2) { - cursor: nesw-resize; -} -.mce-content-body div.mce-resizehandle:nth-of-type(3) { - cursor: nwse-resize; -} -.mce-content-body div.mce-resizehandle:nth-of-type(4) { - cursor: nesw-resize; -} -.mce-content-body .mce-resize-backdrop { - z-index: 10000; -} -.mce-content-body .mce-clonedresizable { - cursor: default; - opacity: 0.5; - outline: 1px dashed black; - position: absolute; - z-index: 10001; -} -.mce-content-body .mce-clonedresizable.mce-resizetable-columns th, -.mce-content-body .mce-clonedresizable.mce-resizetable-columns td { - border: 0; -} -.mce-content-body .mce-resize-helper { - background: #555; - background: rgba(0, 0, 0, 0.75); - border: 1px; - border-radius: 3px; - color: white; - display: none; - font-family: sans-serif; - font-size: 12px; - line-height: 14px; - margin: 5px 10px; - padding: 5px; - position: absolute; - white-space: nowrap; - z-index: 10002; -} -.mce-content-body img[data-mce-selected], -.mce-content-body video[data-mce-selected], -.mce-content-body audio[data-mce-selected], -.mce-content-body object[data-mce-selected], -.mce-content-body embed[data-mce-selected], -.mce-content-body table[data-mce-selected] { - outline: 3px solid #b4d7ff; -} -.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus { - outline: 3px solid #b4d7ff; -} -.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover { - outline: 3px solid #b4d7ff; -} -.mce-content-body *[contentEditable=false][data-mce-selected] { - cursor: not-allowed; - outline: 3px solid #b4d7ff; -} -.mce-content-body.mce-content-readonly *[contentEditable=true]:focus, -.mce-content-body.mce-content-readonly *[contentEditable=true]:hover { - outline: none; -} -.mce-content-body *[data-mce-selected="inline-boundary"] { - background-color: #b4d7ff; -} -.mce-content-body .mce-edit-focus { - outline: 3px solid #b4d7ff; -} -.mce-content-body img::-moz-selection { - background: none; -} -.mce-content-body img::selection { - background: none; -} -.mce-content-body { - padding: 10px; - background-color: #fff; - font-family: 'Open Sans', Verdana, Arial, Helvetica, sans-serif; - font-size: 16px; - line-height: 1.6; - color: #3c3c3c; - scrollbar-3dlight-color: #F0F0EE; - scrollbar-arrow-color: #676662; - scrollbar-base-color: #F0F0EE; - scrollbar-darkshadow-color: #DDDDDD; - scrollbar-face-color: #E0E0DD; - scrollbar-highlight-color: #F0F0EE; - scrollbar-shadow-color: #F0F0EE; - scrollbar-track-color: #F5F5F5; -} -.mce-content-body h1, -.mce-content-body .hd-1 { - color: #3c3c3c; - font-weight: normal; - font-size: 2em; - line-height: 1.4em; - margin: 0 0 1.41575em 0; -} -.mce-content-body h2, -.mce-content-body .hd-2 { - letter-spacing: 1px; - margin-bottom: 15px; - color: #646464; - font-weight: 300; - font-size: 1.2em; - line-height: 1.2em; - text-transform: uppercase; -} -.mce-content-body h3, -.mce-content-body .hd-3 { - margin: 0 0 10px 0; - font-size: 1.1125em; - font-weight: 400; - text-transform: initial; -} -.mce-content-body .hd-3, -.mce-content-body h4, -.mce-content-body .hd-4, -.mce-content-body h5, -.mce-content-body .hd-5, -.mce-content-body h6, -.mce-content-body .hd-6 { - margin: 0 0 10px 0; - font-weight: 600; -} -.mce-content-body h4, -.mce-content-body .hd-4 { - font-size: 1em; -} -.mce-content-body h5, -.mce-content-body .hd-5 { - font-size: 0.83em; -} -.mce-content-body h6, -.mce-content-body .hd-6 { - font-size: 0.75em; -} -.mce-content-body p { - margin-bottom: 1.416em; - font-size: 1em; - line-height: 1.6em !important; - color: #3c3c3c; -} -.mce-content-body em, .mce-content-body i { - font-style: italic; -} -.mce-content-body strong, .mce-content-body b { - font-weight: bold; -} -.mce-content-body p + p, .mce-content-body ul + p, .mce-content-body ol + p { - margin-top: 20px; -} -.mce-content-body ol, .mce-content-body ul { - margin: 1em 0; - padding: 0 0 0 1em; - color: #3c3c3c; -} -.mce-content-body ol li, .mce-content-body ul li { - margin-bottom: 0.708em; -} -.mce-content-body ol { - list-style: decimal outside none; - margin: 0; -} -.mce-content-body ul { - list-style: disc outside none; - margin: 0; -} -.mce-content-body a, .mce-content-body a:link, .mce-content-body a:visited, .mce-content-body a:hover, .mce-content-body a:active { - color: #0075b4; - text-decoration: none; -} -.mce-content-body img { - max-width: 100%; - height: auto; -} -.mce-content-body pre { - margin: 1em 0; - color: #3c3c3c; - font-family: monospace, serif; - font-size: 1em; - white-space: pre-wrap; - word-wrap: break-word; -} -.mce-content-body code { - font-family: monospace, serif; + .mce-content-body *[contentEditable=false] { + cursor: default; + } + .mce-content-body *[contentEditable=true] { + cursor: text; + } + .mce-content-body div.mce-resizehandle { + background-color: #4099ff; + border-color: #4099ff; + border-style: solid; + border-width: 1px; + box-sizing: border-box; + height: 10px; + position: absolute; + width: 10px; + z-index: 1298; + } + .mce-content-body div.mce-resizehandle:hover { + background-color: #4099ff; + } + .mce-content-body div.mce-resizehandle:nth-of-type(1) { + cursor: nwse-resize; + } + .mce-content-body div.mce-resizehandle:nth-of-type(2) { + cursor: nesw-resize; + } + .mce-content-body div.mce-resizehandle:nth-of-type(3) { + cursor: nwse-resize; + } + .mce-content-body div.mce-resizehandle:nth-of-type(4) { + cursor: nesw-resize; + } + .mce-content-body .mce-resize-backdrop { + z-index: 10000; + } + .mce-content-body .mce-clonedresizable { + cursor: default; + opacity: 0.5; + outline: 1px dashed black; + position: absolute; + z-index: 10001; + } + .mce-content-body .mce-clonedresizable.mce-resizetable-columns th, + .mce-content-body .mce-clonedresizable.mce-resizetable-columns td { + border: 0; + } + .mce-content-body .mce-resize-helper { + background: #555; + background: rgba(0, 0, 0, 0.75); + border: 1px; + border-radius: 3px; + color: white; + display: none; + font-family: sans-serif; + font-size: 12px; + line-height: 14px; + margin: 5px 10px; + padding: 5px; + position: absolute; + white-space: nowrap; + z-index: 10002; + } + .mce-content-body img[data-mce-selected], + .mce-content-body video[data-mce-selected], + .mce-content-body audio[data-mce-selected], + .mce-content-body object[data-mce-selected], + .mce-content-body embed[data-mce-selected], + .mce-content-body table[data-mce-selected] { + outline: 3px solid #b4d7ff; + } + .mce-content-body *[contentEditable=false] *[contentEditable=true]:focus { + outline: 3px solid #b4d7ff; + } + .mce-content-body *[contentEditable=false] *[contentEditable=true]:hover { + outline: 3px solid #b4d7ff; + } + .mce-content-body *[contentEditable=false][data-mce-selected] { + cursor: not-allowed; + outline: 3px solid #b4d7ff; + } + .mce-content-body.mce-content-readonly *[contentEditable=true]:focus, + .mce-content-body.mce-content-readonly *[contentEditable=true]:hover { + outline: none; + } + .mce-content-body *[data-mce-selected="inline-boundary"] { + background-color: #b4d7ff; + } + .mce-content-body .mce-edit-focus { + outline: 3px solid #b4d7ff; + } + .mce-content-body img::-moz-selection { background: none; + } + .mce-content-body img::selection { + background: none; + } + .mce-content-body { + padding: 10px; + background-color: #fff; + font-family: 'Open Sans', Verdana, Arial, Helvetica, sans-serif; + font-size: 16px; + line-height: 1.6; + color: #3c3c3c; + scrollbar-3dlight-color: #F0F0EE; + scrollbar-arrow-color: #676662; + scrollbar-base-color: #F0F0EE; + scrollbar-darkshadow-color: #DDDDDD; + scrollbar-face-color: #E0E0DD; + scrollbar-highlight-color: #F0F0EE; + scrollbar-shadow-color: #F0F0EE; + scrollbar-track-color: #F5F5F5; + } + .mce-content-body h1, + .mce-content-body .hd-1 { + color: #3c3c3c; + font-weight: normal; + font-size: 2em; + line-height: 1.4em; + margin: 0 0 1.41575em 0; + } + .mce-content-body h2, + .mce-content-body .hd-2 { + letter-spacing: 1px; + margin-bottom: 15px; + color: #646464; + font-weight: 300; + font-size: 1.2em; + line-height: 1.2em; + text-transform: uppercase; + } + .mce-content-body h3, + .mce-content-body .hd-3 { + margin: 0 0 10px 0; + font-size: 1.1125em; + font-weight: 400; + text-transform: initial; + } + .mce-content-body .hd-3, + .mce-content-body h4, + .mce-content-body .hd-4, + .mce-content-body h5, + .mce-content-body .hd-5, + .mce-content-body h6, + .mce-content-body .hd-6 { + margin: 0 0 10px 0; + font-weight: 600; + } + .mce-content-body h4, + .mce-content-body .hd-4 { + font-size: 1em; + } + .mce-content-body h5, + .mce-content-body .hd-5 { + font-size: 0.83em; + } + .mce-content-body h6, + .mce-content-body .hd-6 { + font-size: 0.75em; + } + .mce-content-body p { + margin-bottom: 1.416em; + font-size: 1em; + line-height: 1.6em !important; + color: #3c3c3c; + } + .mce-content-body em, .mce-content-body i { + font-style: italic; + } + .mce-content-body strong, .mce-content-body b { + font-weight: bold; + } + .mce-content-body p + p, .mce-content-body ul + p, .mce-content-body ol + p { + margin-top: 20px; + } + .mce-content-body ol, .mce-content-body ul { + margin: 1em 0; + padding: 0 0 0 1em; + color: #3c3c3c; + } + .mce-content-body ol li, .mce-content-body ul li { + margin-bottom: 0.708em; + } + .mce-content-body ol { + list-style: decimal outside none; + margin: 0; + } + .mce-content-body ul { + list-style: disc outside none; + margin: 0; + } + .mce-content-body a, .mce-content-body a:link, .mce-content-body a:visited, .mce-content-body a:hover, .mce-content-body a:active { + color: #0075b4; + text-decoration: none; + } + .mce-content-body img { + max-width: 100%; + height: auto; + } + .mce-content-body pre { + margin: 1em 0; color: #3c3c3c; - padding: 0; -}`; + font-family: monospace, serif; + font-size: 1em; + white-space: pre-wrap; + word-wrap: break-word; + } + .mce-content-body code { + font-family: monospace, serif; + background: none; + color: #3c3c3c; + padding: 0; + }` +); + +export { getStyles }; + +export default getStyles({}); diff --git a/src/index.scss b/src/index.scss index 491817e5c..e69de29bb 100644 --- a/src/index.scss +++ b/src/index.scss @@ -1 +0,0 @@ -@import './colors.scss'; \ No newline at end of file From d3be3d424057b9083aaef321f04ad113d19d1c45 Mon Sep 17 00:00:00 2001 From: Ken Clary Date: Wed, 18 Jan 2023 11:29:22 -0500 Subject: [PATCH 29/29] fix: Correct summary of number of attempts; new separator between attempts summary and points summary; hint text for both attempts and points settings; fixed some widget paddings; advanced problems do not show type setting, and type setting menu does not show advanced problems. TNL-10332. --- .../SettingsWidget/messages.js | 35 +++++++-- .../settingsComponents/HintsCard.jsx | 2 +- .../settingsComponents/ScoringCard.jsx | 23 +++++- .../settingsComponents/TypeCard.jsx | 4 +- .../__snapshots__/HintsCard.test.jsx.snap | 8 +- .../__snapshots__/ScoringCard.test.jsx.snap | 75 ++++++++++++++++++- .../__snapshots__/TypeCard.test.jsx.snap | 13 +--- .../ProblemEditor/data/SettingsParser.js | 5 +- .../ProblemEditor/data/SettingsParser.test.js | 4 +- .../data/mockData/problemTestData.js | 8 +- src/editors/data/redux/problem/reducers.js | 2 +- 11 files changed, 139 insertions(+), 40 deletions(-) diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js index 395acf755..ce597208a 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js @@ -36,7 +36,7 @@ export const messages = { }, noHintSummary: { id: 'authoring.problemeditor.settings.hint.noHintSummary', - defaultMessage: 'No Hints', + defaultMessage: 'None', description: 'Summary text for no hints', }, hintSummary: { @@ -104,10 +104,35 @@ export const messages = { defaultMessage: 'Points', description: 'Scoring weight input label', }, - scoringSummary: { - id: 'authoring.problemeditor.settings.scoring.summary', - defaultMessage: '{attempts, plural, =0 {Unlimited} other {#}} attempts - {weight, plural, =0 {Ungraded} other {# points}}', - description: 'Summary text for scoring settings', + unlimitedAttemptsSummary: { + id: 'authoring.problemeditor.settings.scoring.unlimited', + defaultMessage: 'Unlimited attempts', + description: 'Summary text for unlimited attempts', + }, + attemptsSummary: { + id: 'authoring.problemeditor.settings.scoring.attempts', + defaultMessage: '{attempts, plural, =1 {# attempt} other {# attempts}}', + description: 'Summary text for number of attempts', + }, + weightSummary: { + id: 'authoring.problemeditor.settings.scoring.weight', + defaultMessage: '{weight, plural, =0 {Ungraded} other {# points}}', + description: 'Summary text for scoring weight', + }, + scoringSettingsLabel: { + id: 'authoring.problemeditor.settings.scoring.label', + defaultMessage: 'Specify point weight and the number of answer attempts', + description: 'Descriptive text for scoring settings', + }, + attemptsHint: { + id: 'authoring.problemeditor.settings.scoring.attempts.hint', + defaultMessage: 'If a value is not set, unlimited attempts are allowed', + description: 'Summary text for scoring weight', + }, + weightHint: { + id: 'authoring.problemeditor.settings.scoring.weight.hint', + defaultMessage: 'If a value is not set, the problem is worth one point', + description: 'Summary text for scoring weight', }, showAnswerSettingsTitle: { id: 'authoring.problemeditor.settings.showAnswer.title', diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx index ef2fff7e0..088fd3eff 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx @@ -29,7 +29,7 @@ export const HintsCard = ({ /> ))}