From 10ae2848bcf2dfbccdd83724a3cb8704862209fd Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Mon, 13 Apr 2026 16:17:50 -0700 Subject: [PATCH 1/5] build: install dprint to use for code formatting --- dprint.json | 37 +++++++++ package-lock.json | 200 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 238 insertions(+) create mode 100644 dprint.json diff --git a/dprint.json b/dprint.json new file mode 100644 index 0000000000..c03caf9410 --- /dev/null +++ b/dprint.json @@ -0,0 +1,37 @@ +{ + "lineWidth": 120, + "indentWidth": 2, + "useTabs": false, + "typescript": { + "quoteStyle": "alwaysSingle", + "jsx.quoteStyle": "preferDouble", + "semiColons": "always", + "trailingCommas": "onlyMultiLine", + "useBraces": "always", + "operatorPosition": "maintain", + "arrowFunction.useParentheses": "maintain", + "module.sortImportDeclarations": "maintain", + "importDeclaration.sortNamedImports": "maintain", + "importDeclaration.preferSingleLine": false, + "exportDeclaration.sortNamedExports": "maintain" + }, + "json": { + }, + "markdown": { + }, + "excludes": [ + "**/node_modules", + "**/*-lock.json", + "**/dist", + "**/coverage", + "jest.config.js", + "env.config.*", + "example.env.config.*", + "module.config.js" + ], + "plugins": [ + "https://plugins.dprint.dev/typescript-0.95.15.wasm", + "https://plugins.dprint.dev/json-0.21.3.wasm", + "https://plugins.dprint.dev/markdown-0.21.1.wasm" + ] +} diff --git a/package-lock.json b/package-lock.json index fb0dba2d4f..f60f8dfbda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -91,6 +91,7 @@ "@types/react": "^18", "@types/react-dom": "^18", "axios-mock-adapter": "2.1.0", + "dprint": "^0.54.0", "eslint-import-resolver-webpack": "^0.13.8", "fetch-mock-jest": "^1.5.1", "jest-canvas-mock": "^2.5.2", @@ -2543,6 +2544,181 @@ "react": ">=16.8.0" } }, + "node_modules/@dprint/darwin-arm64": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@dprint/darwin-arm64/-/darwin-arm64-0.54.0.tgz", + "integrity": "sha512-yqRI4enH+BDp+4+ZsPVdZM5h873JK1lN7li9l9A5u4C4cvh1oEsiBWAzEPccRkJ2ctF8LgaizBSxO38sqEVYbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@dprint/darwin-x64": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@dprint/darwin-x64/-/darwin-x64-0.54.0.tgz", + "integrity": "sha512-W9BARpgHypcQwatg5mnHaCpX6pLX5dBxxiv+tZKruhOmq8MKYOrAYDXlceMuHSowmWREfUF5yL4SRgXDGI6WQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@dprint/linux-arm64-glibc": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@dprint/linux-arm64-glibc/-/linux-arm64-glibc-0.54.0.tgz", + "integrity": "sha512-VhM7p70VFuNqxZMdiv1e+nMboPj/hMFlTIBWrRaX7+6VThs9mJr9+94wrUeXgfnfsyaEKSbRFa/dru1PINoSNw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-arm64-musl": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@dprint/linux-arm64-musl/-/linux-arm64-musl-0.54.0.tgz", + "integrity": "sha512-QS1A74Lv60/L9oemHCzbHgOLbV2smSJG5IxS5fjf8ZWetyUt918WDzIHBilz/+uiB+OlW2UVTsm952UG0YOrLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-loong64-glibc": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@dprint/linux-loong64-glibc/-/linux-loong64-glibc-0.54.0.tgz", + "integrity": "sha512-8Myka2/0KbhuZnEKL6jagPXTgDKVpd/tfXDRa0oibUBgaqOSku6iRMzHGa/PhqHL+s14Gcp+/cIHz0zU3Tkgug==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-loong64-musl": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@dprint/linux-loong64-musl/-/linux-loong64-musl-0.54.0.tgz", + "integrity": "sha512-/AN3xCuMhC4PK7Pbj7/4zBuhFGr4m0OHV/5uGTfzpkKX/3+AXoyKl7PbT2VlNMGXAK0kuRThfjtx23gIwlWk7Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-riscv64-glibc": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@dprint/linux-riscv64-glibc/-/linux-riscv64-glibc-0.54.0.tgz", + "integrity": "sha512-Aw2vXzzwFDpPbXh6ajsSabVCkCc66C3hCyMKprR/IxYvFtjYX80nh1ox0c7iaw6c4HacHMRLGw7FUSXvomPaEQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-x64-glibc": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@dprint/linux-x64-glibc/-/linux-x64-glibc-0.54.0.tgz", + "integrity": "sha512-zZqj3wQELOX8n6QfT2uuWoMf64Wv0lMXNyam3btm+PKkg0P6a54TPL09Bs9XsViOdxgTcamsiQ7HlErt/LEjIA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-x64-musl": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@dprint/linux-x64-musl/-/linux-x64-musl-0.54.0.tgz", + "integrity": "sha512-it6Qdt06dyW2adbAXpOCb7/KQLxlm4i1UphUAWqWsZk4t3EYetyAza9J0g3Vu9itIWSEIo9MnccgANckQJ6+qw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/win32-arm64": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@dprint/win32-arm64/-/win32-arm64-0.54.0.tgz", + "integrity": "sha512-F5kjV/6I9YtNOTDWHUpTqM2HHHS510BPL7z4NJuU0nDnaVeks7GwNEltGr56CcsG8XQYhkiAsqZytPu6AhA2hQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@dprint/win32-x64": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@dprint/win32-x64/-/win32-x64-0.54.0.tgz", + "integrity": "sha512-AAr2ye/DtgYXDplRoPS+5U++x7T6W4a3I9FvTFWFxziFmUptvAg5G2c4FcXoAduSruhYZJvjDZrLseR2c3IwXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@edx/brand": { "name": "@openedx/brand-openedx", "version": "1.2.3", @@ -11333,6 +11509,30 @@ "webpack": "^4 || ^5" } }, + "node_modules/dprint": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/dprint/-/dprint-0.54.0.tgz", + "integrity": "sha512-sIy25poR2gRP/tWPTgP0MPeJoJcpv0xzYDcsboapvthbEt1Qw3Al252CA0xFyIh2cYEGGKyBJtKokryv4ERlJw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "dprint": "bin.cjs" + }, + "optionalDependencies": { + "@dprint/darwin-arm64": "0.54.0", + "@dprint/darwin-x64": "0.54.0", + "@dprint/linux-arm64-glibc": "0.54.0", + "@dprint/linux-arm64-musl": "0.54.0", + "@dprint/linux-loong64-glibc": "0.54.0", + "@dprint/linux-loong64-musl": "0.54.0", + "@dprint/linux-riscv64-glibc": "0.54.0", + "@dprint/linux-x64-glibc": "0.54.0", + "@dprint/linux-x64-musl": "0.54.0", + "@dprint/win32-arm64": "0.54.0", + "@dprint/win32-x64": "0.54.0" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/package.json b/package.json index b3e8472959..249c601d5a 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,7 @@ "@types/react": "^18", "@types/react-dom": "^18", "axios-mock-adapter": "2.1.0", + "dprint": "^0.54.0", "eslint-import-resolver-webpack": "^0.13.8", "fetch-mock-jest": "^1.5.1", "jest-canvas-mock": "^2.5.2", From 2ddc7e2dda61043be0c38b66c66cd9185678783f Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Mon, 13 Apr 2026 16:26:49 -0700 Subject: [PATCH 2/5] build: fully replace eslint with oxlint + dprint --- .eslintignore | 6 ------ .eslintrc.js | 44 -------------------------------------------- Makefile | 4 +--- package.json | 6 ++---- 4 files changed, 3 insertions(+), 57 deletions(-) delete mode 100755 .eslintignore delete mode 100644 .eslintrc.js diff --git a/.eslintignore b/.eslintignore deleted file mode 100755 index 9c46e7b475..0000000000 --- a/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -coverage/* -dist/ -node_modules/ -jest.config.js -env.config.jsx -example.env.config.jsx diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index e3a4764294..0000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,44 +0,0 @@ -const path = require('path'); -// eslint-disable-next-line import/no-extraneous-dependencies -const { createConfig } = require('@openedx/frontend-build'); - -module.exports = createConfig( - 'eslint', - { - rules: { - 'jsx-a11y/label-has-associated-control': [2, { - controlComponents: ['Input'], - }], - 'template-curly-spacing': 'off', - 'react-hooks/exhaustive-deps': 'off', - 'no-restricted-exports': 'off', - // There is no reason to disallow this syntax anymore; we don't use regenerator-runtime in new browsers - 'no-restricted-syntax': 'off', - 'no-restricted-imports': ['error', { - patterns: [ - { - group: ['@edx/frontend-platform/i18n'], - importNames: ['injectIntl'], - message: "Use 'useIntl' hook instead of injectIntl.", - }, - ], - }], - }, - settings: { - // Import URLs should be resolved using aliases - 'import/resolver': { - webpack: { - config: path.resolve(__dirname, 'webpack.dev.config.js'), - }, - }, - }, - overrides: [ - { - files: ['plugins/**/*.test.jsx'], - rules: { - 'import/no-extraneous-dependencies': 'off', - }, - }, - ], - }, -); diff --git a/Makefile b/Makefile index c0fcdbdb66..66878b437d 100644 --- a/Makefile +++ b/Makefile @@ -51,9 +51,7 @@ validate-no-uncommitted-package-lock-changes: validate: make validate-no-uncommitted-package-lock-changes npm run i18n_extract -# We are trying out oxlint. Now that it's been working well for a while with both oxlint and eslint, we have disabled -# eslint, and after a few weeks we'll evaluate whether any problems are slipping through if only oxlint is used. - npm run oxlint + npm run lint npm run types npm run test:ci npm run build diff --git a/package.json b/package.json index 249c601d5a..1f7e08c2ad 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,8 @@ "build": "fedx-scripts webpack", "i18n_extract": "fedx-scripts formatjs extract --include=plugins", "stylelint": "stylelint \"plugins/**/*.scss\" \"src/**/*.scss\" \"scss/**/*.scss\" --config .stylelintrc.json", - "lint": "npm run stylelint && fedx-scripts eslint --ext .js --ext .jsx --ext .ts --ext .tsx .", - "oxlint": "oxlint --type-aware --deny-warnings", - "lint:fix": "npm run stylelint -- --fix && fedx-scripts eslint --fix --ext .js --ext .jsx --ext .ts --ext .tsx .", + "lint": "dprint check && oxlint --type-aware --deny-warnings && npm run stylelint", + "lint:fix": "dprint fmt && oxlint --type-aware --fix", "start": "fedx-scripts webpack-dev-server --progress", "start:with-theme": "paragon install-theme && npm start && npm install", "dev": "PUBLIC_PATH=/authoring/ MFE_CONFIG_API_URL='http://localhost:8000/api/mfe_config/v1' fedx-scripts webpack-dev-server --progress --host apps.local.openedx.io", @@ -117,7 +116,6 @@ "@types/react-dom": "^18", "axios-mock-adapter": "2.1.0", "dprint": "^0.54.0", - "eslint-import-resolver-webpack": "^0.13.8", "fetch-mock-jest": "^1.5.1", "jest-canvas-mock": "^2.5.2", "jest-expect-message": "^1.1.3", From fc43652be48770286a27c839ad409cd419c1f9f1 Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Mon, 13 Apr 2026 17:34:32 -0700 Subject: [PATCH 3/5] fix: an issue with invalid comments that dprint was making worse --- src/optimizer-page/scan-results/manualIcon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optimizer-page/scan-results/manualIcon.tsx b/src/optimizer-page/scan-results/manualIcon.tsx index 673139d378..68132485b7 100644 --- a/src/optimizer-page/scan-results/manualIcon.tsx +++ b/src/optimizer-page/scan-results/manualIcon.tsx @@ -8,7 +8,7 @@ const ManualIcon: React.FC> = (props) => ( viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" - {...props} // This allows passing additional props like className, style, etc. + {...props /* This allows passing additional props like className, style, etc.*/} > Date: Mon, 13 Apr 2026 18:09:46 -0700 Subject: [PATCH 4/5] build: use a different dprint config for messages files to allow longer lines --- dprint.json | 3 ++- dprint.messages.json | 10 ++++++++++ package.json | 4 ++-- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 dprint.messages.json diff --git a/dprint.json b/dprint.json index c03caf9410..17e17d9b14 100644 --- a/dprint.json +++ b/dprint.json @@ -27,7 +27,8 @@ "jest.config.js", "env.config.*", "example.env.config.*", - "module.config.js" + "module.config.js", + "**/messages.{ts,js}" ], "plugins": [ "https://plugins.dprint.dev/typescript-0.95.15.wasm", diff --git a/dprint.messages.json b/dprint.messages.json new file mode 100644 index 0000000000..aaf431c60e --- /dev/null +++ b/dprint.messages.json @@ -0,0 +1,10 @@ +{ + // For messages files, we allow unlimited line length, to keep the file format consistent. + // For now a separate file is required, but this could be simplified when + // https://github.com/dprint/dprint/issues/996 is implemented or if we change to a different formatter. + "extends": "dprint.json", + "lineWidth": 10000, + "includes": ["src/**/messages.{ts,js}"] + // Because this inherits "excludes" from "dprint.json", it's necessary to use "--excludes-override none" + // to run this on the command line. +} diff --git a/package.json b/package.json index 1f7e08c2ad..0c7ccbc2f9 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ "build": "fedx-scripts webpack", "i18n_extract": "fedx-scripts formatjs extract --include=plugins", "stylelint": "stylelint \"plugins/**/*.scss\" \"src/**/*.scss\" \"scss/**/*.scss\" --config .stylelintrc.json", - "lint": "dprint check && oxlint --type-aware --deny-warnings && npm run stylelint", - "lint:fix": "dprint fmt && oxlint --type-aware --fix", + "lint": "dprint check && dprint --config dprint.messages.json check --excludes-override none && oxlint --type-aware --deny-warnings && npm run stylelint", + "lint:fix": "dprint fmt && dprint --config dprint.messages.json fmt --excludes-override none && oxlint --type-aware --fix", "start": "fedx-scripts webpack-dev-server --progress", "start:with-theme": "paragon install-theme && npm start && npm install", "dev": "PUBLIC_PATH=/authoring/ MFE_CONFIG_API_URL='http://localhost:8000/api/mfe_config/v1' fedx-scripts webpack-dev-server --progress --host apps.local.openedx.io", From 3b6f0c24d8963404144bf63e8ce224085b664703 Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Mon, 13 Apr 2026 18:16:40 -0700 Subject: [PATCH 5/5] chore: run 'npm lint:fix' --- .github/pull_request_template.md | 4 +- .oxlintrc.json | 8 +- plugins/course-apps/calculator/package.json | 28 +- plugins/course-apps/dates/package.json | 28 +- plugins/course-apps/edxnotes/package.json | 28 +- .../learning_assistant/Settings.jsx | 5 +- .../learning_assistant/Settings.test.jsx | 13 +- .../learning_assistant/package.json | 31 +- plugins/course-apps/live/BBBSettings.jsx | 70 +- plugins/course-apps/live/BbbSettings.test.jsx | 29 +- plugins/course-apps/live/Settings.jsx | 21 +- plugins/course-apps/live/ZoomSettings.jsx | 44 +- plugins/course-apps/live/constants.js | 5 +- plugins/course-apps/live/package.json | 38 +- plugins/course-apps/ora_settings/Settings.jsx | 19 +- .../ora_settings/Settings.test.jsx | 12 +- plugins/course-apps/ora_settings/package.json | 33 +- plugins/course-apps/proctoring/Settings.jsx | 87 ++- .../course-apps/proctoring/Settings.test.jsx | 7 +- plugins/course-apps/proctoring/package.json | 34 +- plugins/course-apps/progress/Settings.jsx | 26 +- plugins/course-apps/progress/package.json | 30 +- plugins/course-apps/teams/GroupEditor.jsx | 60 +- .../course-apps/teams/GroupEditor.test.jsx | 11 +- plugins/course-apps/teams/Settings.jsx | 93 +-- plugins/course-apps/teams/package.json | 34 +- plugins/course-apps/wiki/Settings.jsx | 24 +- plugins/course-apps/wiki/package.json | 30 +- .../xpert_unit_summary/Settings.test.jsx | 172 +++-- .../xpert_unit_summary/data/thunks.js | 15 +- .../xpert_unit_summary/package.json | 36 +- .../settings-modal/SettingsModal.jsx | 73 +- src/CourseAuthoringContext.tsx | 9 +- src/CourseAuthoringPage.test.tsx | 20 +- src/CourseAuthoringPage.tsx | 20 +- src/CourseAuthoringRoutes.test.tsx | 5 +- src/CourseAuthoringRoutes.tsx | 137 +++- src/__mocks__/clipboardSection.ts | 3 +- src/__mocks__/clipboardSubsection.ts | 3 +- src/__mocks__/clipboardUnit.ts | 3 +- src/__mocks__/index.ts | 4 +- .../AccessibilityBody/AccessibilityBody.tsx | 4 +- .../AccessibilityForm.test.tsx | 7 +- .../AccessibilityForm/AccessibilityForm.tsx | 29 +- .../AccessibilityForm/hooks.ts | 7 +- .../AdvancedSettings.test.tsx | 32 +- src/advanced-settings/AdvancedSettings.tsx | 54 +- src/advanced-settings/data/api.ts | 3 +- src/advanced-settings/data/apiHooks.ts | 8 +- src/advanced-settings/messages.ts | 2 +- .../modal-error/ModalError.jsx | 12 +- .../modal-error/ModalError.test.jsx | 6 +- .../modal-error/ModalErrorListItem.jsx | 3 +- .../setting-card/SettingCard.test.jsx | 13 +- .../setting-card/SettingCard.tsx | 4 +- .../settings-sidebar/SettingsSidebar.test.jsx | 4 +- src/advanced-settings/utils.js | 4 +- src/authz/data/api.ts | 7 +- src/authz/data/apiHooks.test.tsx | 2 +- src/authz/data/apiHooks.ts | 15 +- src/certificates/Certificates.test.jsx | 11 +- src/certificates/Certificates.tsx | 11 +- src/certificates/__mocks__/index.ts | 2 +- src/certificates/__mocks__/signatories.ts | 12 +- .../CertificateCreateForm.jsx | 10 +- .../CertificatesCreateForm.test.jsx | 24 +- .../hooks/useCertificateCreateForm.jsx | 4 +- .../CertificateDetails.jsx | 11 +- .../CertificateDetails.test.jsx | 19 +- .../CertificateDetailsForm.test.jsx | 15 +- .../CertificateEditForm.jsx | 12 +- .../CertificateEditForm.test.jsx | 19 +- .../CertificateSection.jsx | 5 +- .../CertificateSignatories.jsx | 72 +- .../CertificateSignatories.test.jsx | 15 +- .../hooks/useCreateSignatory.jsx | 6 +- .../hooks/useEditSignatory.jsx | 6 +- .../signatory/Signatory.jsx | 17 +- .../signatory/Signatory.test.jsx | 21 +- .../signatory/SignatoryForm.jsx | 11 +- .../signatory/SignatoryForm.test.jsx | 36 +- .../CertificateWithoutModes.test.jsx | 15 +- .../certificates-list/CertificatesList.jsx | 5 +- .../CertificatesList.test.jsx | 34 +- .../hooks/useCertificatesList.jsx | 5 +- src/certificates/data/api.ts | 6 +- src/certificates/data/selectors.ts | 12 +- .../EmptyCertificatesWithModes.test.jsx | 15 +- src/certificates/hooks/useCertificates.jsx | 13 +- .../sidebar-block/SidebarBlock.test.jsx | 7 +- .../layout/header-buttons/HeaderButtons.jsx | 5 +- .../header-buttons/HeaderButtons.test.jsx | 15 +- .../header-buttons/hooks/useHeaderButtons.jsx | 5 +- src/certificates/utils.js | 25 +- src/constants.ts | 9 +- .../CompareContainersWidget.test.tsx | 82 ++- .../CompareContainersWidget.tsx | 50 +- .../ContainerRow.test.tsx | 107 ++- src/container-comparison/ContainerRow.tsx | 67 +- src/container-comparison/data/api.mock.ts | 2 +- src/container-comparison/types.ts | 36 +- src/container-comparison/utils.ts | 6 +- .../ContentTagsCollapsible.d.ts | 18 +- .../ContentTagsCollapsible.jsx | 64 +- .../ContentTagsCollapsible.test.jsx | 16 +- .../ContentTagsCollapsibleHelper.jsx | 29 +- .../ContentTagsDrawer.test.jsx | 24 +- src/content-tags-drawer/ContentTagsDrawer.tsx | 154 ++-- .../ContentTagsDrawerHelper.jsx | 14 +- .../ContentTagsDrawerSheet.jsx | 3 +- .../ContentTagsDropDownSelector.jsx | 65 +- .../ContentTagsDropDownSelector.test.jsx | 50 +- .../ContentTagsSnippet.test.tsx | 5 +- .../ContentTagsSnippet.tsx | 6 +- src/content-tags-drawer/TagsTree.test.jsx | 5 +- src/content-tags-drawer/TagsTree.tsx | 19 +- src/content-tags-drawer/__mocks__/index.ts | 6 +- src/content-tags-drawer/data/api.mocks.ts | 63 +- src/content-tags-drawer/data/api.ts | 15 +- .../data/apiHooks.test.jsx | 6 +- src/content-tags-drawer/data/apiHooks.ts | 21 +- src/content-tags-drawer/index.ts | 2 +- .../tags-sidebar-controls/TagsSidebarBody.tsx | 16 +- .../TagsSidebarHeader.jsx | 2 +- .../TagsSidebarHeader.test.jsx | 6 +- .../tags-sidebar-controls/index.tsx | 2 +- .../ChecklistSection/ChecklistItemBody.jsx | 37 +- .../ChecklistSection/ChecklistItemComment.jsx | 35 +- .../ChecklistSection/ChecklistSection.jsx | 58 +- .../ChecklistSection.test.jsx | 5 +- .../ChecklistSection/hooks.jsx | 4 +- .../utils/courseChecklistValidators.js | 21 +- .../utils/getFilteredChecklist.js | 30 +- .../utils/getFilteredChecklist.test.js | 26 +- src/course-checklist/CourseChecklist.test.jsx | 5 +- src/course-checklist/data/api.ts | 5 +- src/course-libraries/CourseLibraries.test.tsx | 62 +- src/course-libraries/CourseLibraries.tsx | 52 +- .../LegacyLibContentBlockAlert.tsx | 16 +- src/course-libraries/OutOfSyncAlert.tsx | 26 +- src/course-libraries/ReviewTabContent.tsx | 50 +- .../__mocks__/libBlockMetadata.json | 42 +- .../__mocks__/publishableEntityLinks.json | 2 +- src/course-libraries/data/api.mocks.ts | 16 +- src/course-libraries/data/api.ts | 11 +- src/course-libraries/data/apiHooks.test.tsx | 12 +- src/course-libraries/data/apiHooks.ts | 80 ++- src/course-outline/CourseOutline.test.tsx | 262 +++++-- src/course-outline/CourseOutline.tsx | 284 ++++---- src/course-outline/CourseOutlineContext.tsx | 34 +- .../OutlineAddChildButtons.test.tsx | 113 +-- src/course-outline/OutlineAddChildButtons.tsx | 147 ++-- .../__mocks__/courseOutlineIndex.ts | 91 ++- src/course-outline/__mocks__/courseSection.ts | 6 +- .../__mocks__/courseSubsection.ts | 3 +- src/course-outline/__mocks__/index.ts | 4 +- .../card-header/CardHeader.test.tsx | 15 +- src/course-outline/card-header/CardHeader.tsx | 172 ++--- src/course-outline/data/api.ts | 104 +-- src/course-outline/data/apiHooks.ts | 134 ++-- src/course-outline/data/thunk.ts | 8 +- src/course-outline/data/types.ts | 64 +- .../drag-helper/DragContextProvider.tsx | 6 +- .../drag-helper/DraggableList.tsx | 24 +- .../drag-helper/SortableItem.tsx | 6 +- src/course-outline/drag-helper/utils.test.ts | 7 +- src/course-outline/drag-helper/utils.ts | 61 +- .../EmptyPlaceholder.test.tsx | 16 +- .../EnableHighlightsModal.jsx | 9 +- .../EnableHighlightsModal.test.jsx | 25 +- .../header-navigations/HeaderActions.test.tsx | 42 +- .../header-navigations/HeaderActions.tsx | 29 +- .../HeaderNavigations.test.tsx | 31 +- .../header-navigations/HeaderNavigations.tsx | 47 +- .../highlights-modal/HighlightsModal.test.tsx | 13 +- .../highlights-modal/HighlightsModal.tsx | 17 +- src/course-outline/hooks.jsx | 2 +- src/course-outline/index.ts | 4 +- .../outline-sidebar/AddSidebar.test.tsx | 44 +- .../outline-sidebar/AddSidebar.tsx | 23 +- .../outline-sidebar/OutlineAlignSidebar.tsx | 8 +- .../outline-sidebar/OutlineHelpSidebar.tsx | 4 +- .../outline-sidebar/OutlineSidebar.test.tsx | 15 +- .../outline-sidebar/OutlineSidebarContext.tsx | 38 +- .../OutlineSidebarPagesContext.tsx | 11 +- .../info-sidebar/InfoSidebar.test.tsx | 6 +- .../info-sidebar/InfoSidebar.tsx | 4 +- .../info-sidebar/SectionInfoSidebar.tsx | 4 +- .../info-sidebar/SectionSettings.test.tsx | 24 +- .../info-sidebar/SectionSettings.tsx | 21 +- .../info-sidebar/SubsectionInfoSidebar.tsx | 41 +- .../info-sidebar/SubsectionSettings.test.tsx | 22 +- .../info-sidebar/SubsectionSettings.tsx | 100 +-- .../info-sidebar/UnitInfoSidebar.test.tsx | 46 +- .../info-sidebar/UnitInfoSidebar.tsx | 41 +- .../sharedSettings/ReleaseSection.test.tsx | 5 +- .../sharedSettings/ReleaseSection.tsx | 1 - .../sharedSettings/VisibilitySection.test.tsx | 5 +- .../sharedSettings/VisibilitySection.tsx | 34 +- src/course-outline/page-alerts/PageAlerts.jsx | 55 +- .../page-alerts/PageAlerts.test.jsx | 31 +- .../publish-modal/PublishModal.test.tsx | 7 +- .../publish-modal/PublishModal.tsx | 47 +- .../section-card/SectionCard.test.tsx | 61 +- .../section-card/SectionCard.tsx | 51 +- .../status-bar/LegacyStatusBar.test.tsx | 33 +- .../status-bar/LegacyStatusBar.tsx | 81 ++- .../NotificationStatusIcon.test.tsx | 7 +- .../status-bar/NotificationStatusIcon.tsx | 2 +- .../status-bar/StatusBar.test.tsx | 17 +- src/course-outline/status-bar/StatusBar.tsx | 16 +- src/course-outline/status-bar/hooks.ts | 4 +- .../subsection-card/SubsectionCard.test.tsx | 61 +- .../subsection-card/SubsectionCard.tsx | 63 +- .../unit-card/UnitCard.test.tsx | 57 +- src/course-outline/unit-card/UnitCard.tsx | 40 +- .../utils/getChecklistForStatusBar.js | 5 +- .../utils/getChecklistValues.js | 30 +- .../utils/getChecklistValues.test.js | 26 +- .../utils/getErrorDetails.test.js | 5 +- .../xblock-status/GradingTypeAndDueDate.jsx | 6 +- .../xblock-status/StatusMessages.tsx | 10 +- .../xblock-status/XBlockStatus.test.jsx | 23 +- .../xblock-status/XBlockStatus.tsx | 8 +- .../CourseRerunForm.test.jsx | 7 +- src/course-rerun/course-rerun-form/index.jsx | 9 +- src/course-team/CourseTeam.test.tsx | 13 +- src/course-team/CourseTeam.tsx | 52 +- .../add-team-member/AddTeamMember.test.jsx | 11 +- .../add-user-form/AddUserForm.test.tsx | 17 +- src/course-team/add-user-form/messages.ts | 1 - .../course-team-member/CourseTeamMember.jsx | 48 +- .../CourseTeamMember.test.jsx | 31 +- src/course-team/data/api.ts | 6 +- src/course-team/hooks.tsx | 2 +- src/course-team/info-modal/InfoModal.jsx | 4 +- src/course-team/info-modal/InfoModal.test.jsx | 29 +- src/course-team/utils.js | 12 +- src/course-unit/CourseUnit.test.tsx | 668 ++++++++++++------ src/course-unit/CourseUnit.tsx | 96 +-- .../SubsectionUnitRedirect.test.tsx | 15 +- .../__mocks__/courseOutlineInfo.ts | 471 +++++++----- .../__mocks__/courseSectionVertical.ts | 69 +- src/course-unit/__mocks__/index.ts | 6 +- .../add-component/AddComponent.test.tsx | 47 +- .../add-component/AddComponent.tsx | 189 ++--- .../add-component/add-component-btn/index.jsx | 5 +- .../ComponentModalView.jsx | 9 +- .../add-component-modals/ModalContainer.jsx | 14 +- .../breadcrumbs/Breadcrumbs.test.tsx | 11 +- src/course-unit/breadcrumbs/Breadcrumbs.tsx | 73 +- .../components/FileList.jsx | 4 +- .../paste-notification/components/index.ts | 2 +- .../clipboard/paste-notification/index.jsx | 23 +- .../clipboard/paste-notification/utils.js | 3 +- src/course-unit/course-sequence/Sequence.jsx | 6 +- src/course-unit/course-sequence/hooks.js | 5 +- .../SequenceNavigation.jsx | 13 +- .../SequenceNavigationTabs.jsx | 32 +- src/course-unit/data/api.ts | 15 +- src/course-unit/data/apiHooks.ts | 46 +- src/course-unit/data/selectors.js | 5 +- src/course-unit/data/types.ts | 2 +- .../HeaderNavigations.test.tsx | 19 +- .../header-navigations/HeaderNavigations.tsx | 26 +- .../header-title/HeaderTitle.test.tsx | 25 +- src/course-unit/header-title/HeaderTitle.tsx | 49 +- src/course-unit/hooks.tsx | 25 +- src/course-unit/iframeEvents.ts | 18 +- .../legacy-sidebar/SidebarSection.tsx | 2 +- .../legacy-sidebar/SplitTestSidebarInfo.tsx | 4 +- .../legacy-sidebar/components/SidebarBody.jsx | 51 +- .../legacy-sidebar/components/index.ts | 4 +- .../sidebar-footer/ActionButtons.test.jsx | 19 +- .../sidebar-footer/ActionButtons.tsx | 6 +- .../components/sidebar-footer/index.tsx | 34 +- src/course-unit/legacy-sidebar/hooks.jsx | 8 +- src/course-unit/legacy-sidebar/index.tsx | 6 +- .../components/CategoryIndicator.tsx | 2 +- .../move-modal/components/EmptyMessage.tsx | 2 +- .../move-modal/components/index.ts | 2 +- src/course-unit/move-modal/hooks.tsx | 32 +- src/course-unit/move-modal/index.tsx | 52 +- src/course-unit/move-modal/moveModal.test.tsx | 31 +- src/course-unit/move-modal/utils.test.ts | 5 +- src/course-unit/move-modal/utils.ts | 12 +- src/course-unit/preview-changes/index.tsx | 103 +-- src/course-unit/unit-sidebar/AddSidebar.tsx | 6 +- .../unit-sidebar/UnitAlignSidebar.test.tsx | 15 +- .../unit-sidebar/UnitAlignSidebar.tsx | 7 +- src/course-unit/unit-sidebar/UnitSidebar.tsx | 6 +- .../unit-sidebar/UnitSidebarContext.tsx | 12 +- .../unit-info/ComponentInfoSidebar.test.tsx | 30 +- .../unit-info/ComponentInfoSidebar.tsx | 15 +- .../unit-info/GenericUnitInfoSettings.tsx | 13 +- .../unit-info/PublishControls.tsx | 4 +- .../unit-info/UnitInfoSidebar.test.tsx | 44 +- .../unit-info/UnitInfoSidebar.tsx | 9 +- .../unit-info/UnitVisibilityInfo.tsx | 93 +-- src/course-unit/utils.ts | 4 +- .../hooks/useMessageHandlers.tsx | 16 +- .../xblock-container-iframe/index.tsx | 6 +- .../xblock-container-iframe/types.ts | 2 +- src/course-updates/CourseUpdates.test.tsx | 6 +- src/course-updates/CourseUpdates.tsx | 46 +- src/course-updates/__mocks__/courseUpdates.js | 21 +- src/course-updates/__mocks__/index.ts | 2 +- .../course-handouts/CourseHandouts.test.jsx | 21 +- .../course-update/CourseUpdate.test.jsx | 25 +- src/course-updates/data/api.js | 3 +- .../delete-modal/DeleteModal.jsx | 4 +- .../delete-modal/DeleteModal.test.jsx | 21 +- src/course-updates/update-form/UpdateForm.jsx | 22 +- .../update-form/UpdateForm.test.jsx | 31 +- src/course-updates/update-form/utils.js | 10 +- src/custom-pages/CustomPageCard.jsx | 4 +- src/custom-pages/CustomPageCard.test.jsx | 38 +- src/custom-pages/CustomPages.test.tsx | 4 +- src/custom-pages/CustomPages.tsx | 23 +- src/custom-pages/data/api.js | 4 - src/custom-pages/data/thunks.js | 3 +- .../factories/mockApiResponses.jsx | 4 +- src/custom-pages/messages.ts | 2 +- src/data/api.mocks.ts | 6 +- src/data/api.ts | 23 +- src/data/apiHooks.test.tsx | 14 +- src/data/apiHooks.ts | 34 +- src/data/types.ts | 32 +- src/editors/AdvancedEditor.test.tsx | 2 +- src/editors/AdvancedEditor.tsx | 4 +- src/editors/EditorContainer.test.tsx | 13 +- src/editors/EditorPage.test.tsx | 2 +- src/editors/VideoSelector.jsx | 4 +- src/editors/api.ts | 7 +- .../components/CancelConfirmModal.tsx | 14 +- .../EditConfirmationButtons.test.tsx | 5 +- .../TitleHeader/EditableHeader.test.tsx | 5 +- .../components/TitleHeader/hooks.js | 4 +- .../components/TitleHeader/hooks.test.js | 10 +- .../components/TitleHeader/index.test.tsx | 4 +- .../components/TitleHeader/messages.ts | 1 - .../containers/EditorContainer/hooks.ts | 33 +- .../containers/EditorContainer/index.test.tsx | 11 +- .../containers/EditorContainer/index.tsx | 6 +- src/editors/containers/GameEditor/index.jsx | 5 +- src/editors/containers/PdfEditor/api.ts | 42 +- .../PdfEditor/components/PdfEditingModal.tsx | 5 +- src/editors/containers/PdfEditor/contexts.tsx | 38 +- .../containers/PdfEditor/index.test.tsx | 121 ++-- .../AnswerWidget/AnswerOption.jsx | 12 +- .../AnswerWidget/AnswerOption.test.tsx | 4 +- .../AnswerWidget/AnswersContainer.jsx | 66 +- .../AnswerWidget/AnswersContainer.test.tsx | 5 +- .../AnswerWidget/components/Checker/index.jsx | 4 +- .../components/Checker/index.test.tsx | 5 +- .../components/Feedback/FeedbackBox.jsx | 70 +- .../components/Feedback/FeedbackBox.test.tsx | 12 +- .../components/Feedback/FeedbackControl.jsx | 8 +- .../Feedback/FeedbackControl.test.tsx | 31 +- .../components/Feedback/messages.ts | 1 - .../EditProblemView/AnswerWidget/hooks.js | 14 +- .../AnswerWidget/hooks.test.js | 10 +- .../ExplanationWidget/index.jsx | 2 +- .../ExplanationWidget/messages.ts | 1 - .../EditProblemView/QuestionWidget/index.jsx | 2 +- .../QuestionWidget/messages.ts | 1 - .../SettingsWidget/CardSection.jsx | 9 +- .../SettingsWidget/CardSection.test.tsx | 18 +- .../SettingsWidget/SettingsOption.jsx | 21 +- .../SettingsWidget/SettingsOption.test.tsx | 6 +- .../EditProblemView/SettingsWidget/hooks.js | 8 +- .../SettingsWidget/hooks.test.js | 55 +- .../EditProblemView/SettingsWidget/index.jsx | 38 +- .../SettingsWidget/index.test.tsx | 14 +- .../SettingsWidget/messages.ts | 3 +- .../GeneralFeedback/hooks.js | 4 +- .../GeneralFeedback/hooks.test.js | 2 +- .../GeneralFeedback/index.test.tsx | 5 +- .../GeneralFeedback/messages.ts | 1 - .../GroupFeedback/GroupFeedbackRow.jsx | 6 +- .../settingsComponents/GroupFeedback/hooks.js | 4 +- .../GroupFeedback/hooks.test.js | 2 +- .../GroupFeedback/index.test.tsx | 15 +- .../GroupFeedback/messages.ts | 1 - .../settingsComponents/HintRow.test.tsx | 5 +- .../Randomization/hooks.test.js | 2 +- .../Randomization/index.jsx | 19 +- .../Randomization/index.test.tsx | 5 +- .../Randomization/messages.ts | 1 - .../settingsComponents/ResetCard.jsx | 7 +- .../settingsComponents/ResetCard.test.jsx | 14 +- .../settingsComponents/ScoringCard.jsx | 5 +- .../settingsComponents/ScoringCard.test.jsx | 5 +- .../settingsComponents/ShowAnswerCard.jsx | 28 +- .../ShowAnswerCard.test.tsx | 4 +- .../settingsComponents/SwitchEditorCard.jsx | 21 +- .../SwitchEditorCard.test.tsx | 13 +- .../settingsComponents/TimerCard.test.tsx | 4 +- .../settingsComponents/Tolerance/index.jsx | 49 +- .../Tolerance/index.test.jsx | 68 +- .../settingsComponents/Tolerance/messages.ts | 3 - .../settingsComponents/TypeRow.jsx | 4 +- .../settingsComponents/TypeRow.test.tsx | 17 +- .../components/EditProblemView/hooks.js | 22 +- .../components/EditProblemView/hooks.test.js | 17 +- .../components/EditProblemView/index.jsx | 89 +-- .../components/EditProblemView/index.test.tsx | 42 +- .../SelectTypeWrapper/index.test.tsx | 4 +- .../SelectTypeWrapper/messages.ts | 1 - .../content/AdvanceTypeSelect.test.tsx | 5 +- .../content/AdvanceTypeSelect.tsx | 16 +- .../SelectTypeModal/content/Preview.jsx | 3 +- .../SelectTypeModal/content/Preview.test.tsx | 3 +- .../content/ProblemTypeSelect.test.tsx | 5 +- .../SelectTypeModal/content/messages.ts | 1 - .../components/SelectTypeModal/hooks.js | 10 +- .../components/SelectTypeModal/index.tsx | 14 +- .../ProblemEditor/data/OLXParser.js | 32 +- .../ProblemEditor/data/OLXParser.test.js | 10 +- .../ProblemEditor/data/ReactStateOLXParser.js | 32 +- .../data/ReactStateOLXParser.test.js | 19 +- .../data/ReactStateSettingsParser.js | 34 +- .../ProblemEditor/data/SettingsParser.js | 5 +- .../containers/ProblemEditor/data/apiHooks.ts | 30 +- .../data/mockData/editorTestData.js | 21 +- .../data/mockData/olxTestData.js | 153 ++-- .../data/mockData/problemTestData.js | 72 +- .../data/reactStateOLXHelpers.js | 8 +- .../data/reactStateOLXHelpers.test.js | 108 ++- .../containers/ProblemEditor/index.test.tsx | 16 +- .../containers/ProblemEditor/index.tsx | 21 +- .../containers/ProblemEditor/messages.ts | 1 - src/editors/containers/TextEditor/hooks.js | 8 +- src/editors/containers/TextEditor/index.jsx | 9 +- .../containers/TextEditor/index.test.tsx | 10 +- src/editors/containers/TextEditor/messages.ts | 1 - .../components/SelectVideoModal.jsx | 4 +- .../components/SelectVideoModal.test.jsx | 9 +- .../components/VideoEditorModal.test.tsx | 27 +- .../components/VideoEditorModal.tsx | 13 +- .../VideoSettingsModal/ErrorSummary.test.tsx | 4 +- .../components/DurationWidget/hooks.js | 17 +- .../components/DurationWidget/hooks.test.js | 2 +- .../components/HandoutWidget/hooks.jsx | 19 +- .../components/HandoutWidget/index.jsx | 130 ++-- .../components/HandoutWidget/index.test.tsx | 4 +- .../components/HandoutWidget/messages.ts | 1 - .../components/LicenseWidget/LicenseBlurb.jsx | 24 +- .../LicenseWidget/LicenseBlurb.test.tsx | 17 +- .../LicenseWidget/LicenseDetails.jsx | 46 +- .../LicenseWidget/LicenseDetails.test.tsx | 8 +- .../LicenseWidget/LicenseDisplay.jsx | 4 +- .../LicenseWidget/LicenseSelector.jsx | 38 +- .../LicenseWidget/LicenseSelector.test.tsx | 5 +- .../components/LicenseWidget/hooks.jsx | 3 +- .../components/LicenseWidget/index.jsx | 58 +- .../components/LicenseWidget/messages.ts | 1 - .../components/SocialShareWidget/index.jsx | 71 +- .../SocialShareWidget/index.test.tsx | 352 +++++---- .../components/ThumbnailWidget/hooks.js | 20 +- .../components/ThumbnailWidget/hooks.test.jsx | 23 +- .../components/ThumbnailWidget/index.jsx | 124 ++-- .../components/ThumbnailWidget/index.test.tsx | 6 +- .../components/ThumbnailWidget/messages.ts | 10 +- .../ImportTranscriptCard.test.tsx | 8 +- .../TranscriptWidget/LanguageSelector.jsx | 32 +- .../LanguageSelector.test.jsx | 9 +- .../TranscriptWidget/Transcript.jsx | 37 +- .../TranscriptWidget/Transcript.test.jsx | 5 +- .../TranscriptActionMenu.test.tsx | 24 +- .../components/TranscriptWidget/index.jsx | 100 +-- .../TranscriptWidget/index.test.tsx | 24 +- .../components/TranscriptWidget/messages.ts | 1 - .../components/VideoPreviewWidget/index.jsx | 5 +- .../components/VideoSourceWidget/index.jsx | 48 +- .../VideoSourceWidget/index.test.tsx | 105 ++- .../components/VideoSourceWidget/messages.ts | 1 - .../VideoSettingsModal/components/handlers.js | 3 +- .../VideoSettingsModal/components/hooks.js | 26 +- .../components/hooks.test.js | 7 +- .../components/VideoSettingsModal/index.tsx | 4 +- src/editors/containers/VideoEditor/hooks.ts | 2 +- src/editors/containers/VideoEditor/index.tsx | 41 +- src/editors/containers/VideoGallery/hooks.js | 14 +- src/editors/containers/VideoGallery/index.jsx | 4 +- .../containers/VideoGallery/index.test.jsx | 8 +- .../containers/VideoGallery/messages.ts | 12 +- .../VideoUploadEditor/VideoUploader.jsx | 18 +- .../VideoUploadEditor/VideoUploader.test.jsx | 20 +- .../containers/VideoUploadEditor/index.jsx | 41 +- .../VideoUploadEditor/index.test.jsx | 15 +- .../constants/advancedOlxTemplates/index.ts | 7 +- .../basicProblemTemplates/dropdown.js | 4 +- .../constants/basicProblemTemplates/index.ts | 6 +- .../basicProblemTemplates/multiSelect.js | 6 +- .../basicProblemTemplates/numeric.js | 4 +- .../basicProblemTemplates/singleSelect.js | 6 +- .../basicProblemTemplates/textInput.js | 6 +- src/editors/data/constants/problem.ts | 372 +++++----- src/editors/data/constants/requests.ts | 64 +- src/editors/data/constants/tinyMCE.js | 10 +- src/editors/data/redux/app/reducer.ts | 6 +- src/editors/data/redux/app/selectors.test.ts | 13 +- src/editors/data/redux/app/selectors.ts | 17 +- src/editors/data/redux/game/reducers.js | 6 +- src/editors/data/redux/index.ts | 43 +- .../data/redux/problem/reducers.test.ts | 3 +- src/editors/data/redux/problem/reducers.ts | 6 +- src/editors/data/redux/requests/reducer.js | 6 +- .../data/redux/requests/selectors.test.js | 10 +- src/editors/data/redux/thunkActions/app.js | 82 ++- .../data/redux/thunkActions/app.test.js | 2 +- .../data/redux/thunkActions/problem.test.ts | 21 +- .../data/redux/thunkActions/problem.ts | 98 +-- .../data/redux/thunkActions/requests.js | 32 +- .../data/redux/thunkActions/requests.test.js | 69 +- .../data/redux/thunkActions/testUtils.js | 11 +- src/editors/data/redux/thunkActions/video.js | 85 ++- .../data/redux/thunkActions/video.test.js | 20 +- src/editors/data/redux/video/reducer.js | 6 +- src/editors/data/redux/video/selectors.js | 18 +- src/editors/data/services/cms/api.test.ts | 24 +- src/editors/data/services/cms/api.ts | 146 ++-- src/editors/data/services/cms/urls.test.ts | 29 +- src/editors/data/services/cms/urls.ts | 5 +- src/editors/decisions/0002-text-editor-adr.md | 27 +- ...5-internal-editor-testability-decisions.md | 46 +- src/editors/editorTestRender.tsx | 3 +- src/editors/example.jsx | 5 +- src/editors/hooks.ts | 9 +- src/editors/setupEditorTest.js | 149 ++-- .../sharedComponents/BaseModal/messages.ts | 1 - src/editors/sharedComponents/Button/index.jsx | 6 +- .../CheckboxField/CheckboxField.tsx | 16 +- .../sharedComponents/CodeEditor/index.jsx | 5 +- .../CodeEditor/index.test.tsx | 15 +- .../sharedComponents/CodeEditor/messages.ts | 1 - .../CollapsibleFormWidget.jsx | 14 +- .../CollapsibleFormWidget.test.tsx | 6 +- .../ErrorAlerts/ErrorAlert.test.tsx | 5 +- .../ErrorAlerts/ErrorAlert.tsx | 8 +- .../ErrorAlerts/FetchErrorAlert.jsx | 1 - .../sharedComponents/ErrorAlerts/messages.ts | 1 - .../ErrorBoundary/ErrorPage.jsx | 25 +- .../ErrorBoundary/index.test.jsx | 7 +- .../sharedComponents/FileInput/index.test.jsx | 4 +- .../ImageSettingsModal/AltTextControls.jsx | 10 +- .../AltTextControls.test.tsx | 8 +- .../ImageSettingsModal/DimensionControls.jsx | 10 +- .../DimensionControls.test.tsx | 10 +- .../ImageSettingsModal/hooks.js | 34 +- .../ImageSettingsModal/hooks.test.js | 15 +- .../ImageSettingsModal/index.jsx | 4 +- .../ImageSettingsModal/messages.ts | 3 +- .../SelectImageModal/hooks.js | 19 +- .../SelectImageModal/hooks.test.js | 22 +- .../SelectImageModal/messages.ts | 13 +- .../ImageUploadModal/index.jsx | 32 +- .../ImageUploadModal/index.test.tsx | 13 +- .../sharedComponents/RawEditor/index.jsx | 19 +- .../sharedComponents/RawEditor/index.test.jsx | 17 +- .../SelectableBox/FormCheckbox.jsx | 10 +- .../SelectableBox/FormCheckboxSetContext.jsx | 5 +- .../SelectableBox/FormControlSet.jsx | 21 +- .../SelectableBox/FormGroupContext.jsx | 11 +- .../SelectableBox/FormRadio.jsx | 4 +- .../SelectableBox/FormRadioSetContext.jsx | 5 +- .../SelectableBox/FormText.jsx | 7 +- .../sharedComponents/SelectableBox/README.md | 34 +- .../SelectableBox/fieldUtils.js | 23 +- .../tests/SelectableBox.test.jsx | 4 +- .../tests/SelectableBoxSet.test.jsx | 20 +- .../SelectionModal/Gallery.jsx | 15 +- .../SelectionModal/Gallery.test.tsx | 16 +- .../SelectionModal/GalleryCard.jsx | 52 +- .../SelectionModal/GalleryCard.test.tsx | 12 +- .../SelectionModal/SearchSort.jsx | 79 ++- .../SelectionModal/SearchSort.test.jsx | 9 +- .../sharedComponents/SelectionModal/index.jsx | 14 +- .../SelectionModal/index.test.jsx | 36 +- .../SourceCodeModal/hooks.test.js | 9 +- .../SourceCodeModal/index.jsx | 6 +- .../SourceCodeModal/index.test.tsx | 14 +- .../SourceCodeModal/messages.ts | 1 - .../sharedComponents/TextField/TextField.tsx | 24 +- .../customTinyMcePlugins/embedIframePlugin.js | 11 +- .../embedIframePlugin.test.js | 4 +- .../TinyMceWidget/hooks.test.js | 69 +- .../sharedComponents/TinyMceWidget/hooks.ts | 37 +- .../TinyMceWidget/index.test.tsx | 3 +- .../TinyMceWidget/pluginConfig.js | 49 +- .../TypeaheadDropdown/index.jsx | 14 +- .../TypeaheadDropdown/index.test.jsx | 30 +- .../UploadWidget/UploadWidget.test.tsx | 32 +- .../UploadWidget/UploadWidget.tsx | 114 +-- .../sharedComponents/UploadWidget/messages.ts | 8 +- src/editors/testUtils.js | 25 +- src/editors/utils/camelizeKeys.test.js | 30 +- src/editors/utils/convertMarkdownToXML.js | 144 ++-- .../utils/convertMarkdownToXML.test.ts | 7 +- src/editors/utils/formatLibraryImgRequest.ts | 33 +- .../utils/formatLibreryImgRequest.test.ts | 5 +- src/editors/utils/index.ts | 10 +- src/editors/utils/keyStore.ts | 13 +- src/editors/utils/snakeCaseKeys.test.js | 30 +- src/export-page/CourseExportPage.test.tsx | 26 +- src/export-page/CourseExportPage.tsx | 9 +- src/export-page/data/api.ts | 2 +- src/export-page/data/apiHooks.ts | 5 +- .../export-modal-error/ExportModalError.tsx | 34 +- .../export-sidebar/ExportSidebar.test.tsx | 15 +- .../export-sidebar/ExportSidebar.tsx | 4 +- .../export-stepper/ExportStepper.test.tsx | 15 +- .../export-stepper/ExportStepper.tsx | 31 +- .../files-page/CourseFilesTable.tsx | 20 +- .../files-page/FileThumbnail.jsx | 44 +- src/files-and-videos/files-page/FilesPage.jsx | 4 +- .../files-page/FilesPage.test.jsx | 94 ++- src/files-and-videos/files-page/data/api.js | 4 - .../files-page/data/thunks.js | 8 +- .../generic/DeleteConfirmationModal.jsx | 8 +- .../generic/DeleteConfirmationModal.test.jsx | 3 +- src/files-and-videos/generic/FileInput.tsx | 29 +- src/files-and-videos/generic/FileMenu.jsx | 42 +- src/files-and-videos/generic/FileTable.jsx | 72 +- src/files-and-videos/generic/InfoModal.jsx | 4 +- .../generic/UsageMetricsMessage.jsx | 26 +- src/files-and-videos/generic/constants.ts | 58 +- src/files-and-videos/generic/index.ts | 2 +- .../generic/table-components/FilterStatus.jsx | 17 +- .../generic/table-components/Footer.jsx | 5 +- .../generic/table-components/GalleryCard.jsx | 20 +- .../generic/table-components/TableActions.jsx | 18 +- .../table-components/TableActions.test.jsx | 13 +- .../SortAndFilterModal.jsx | 14 +- .../table-custom-columns/AccessColumn.jsx | 10 +- .../table-custom-columns/MoreInfoColumn.jsx | 85 +-- .../table-custom-columns/index.ts | 9 +- .../videos-page/CourseVideosTable.tsx | 138 ++-- .../videos-page/VideoThumbnail.jsx | 99 +-- .../videos-page/VideosPage.test.tsx | 32 +- src/files-and-videos/videos-page/data/api.js | 10 +- .../videos-page/data/api.test.ts | 12 +- .../videos-page/data/thunks.js | 7 +- .../videos-page/data/utils.js | 2 +- src/files-and-videos/videos-page/index.ts | 7 +- .../info-sidebar/TranscriptTab.test.jsx | 46 +- .../info-sidebar/VideoInfoModalSidebar.jsx | 6 +- .../transcript-item/LanguageSelect.jsx | 4 +- .../transcript-item/Transcript.jsx | 120 ++-- .../transcript-item/TranscriptMenu.jsx | 4 +- .../transcript-settings/FormDropdown.jsx | 10 +- .../ThreePlayMediaForm.jsx | 68 +- .../TranscriptSettings.jsx | 91 +-- .../TranscriptSettings.test.jsx | 4 +- .../upload-modal/UploadStatusIcon.jsx | 6 +- src/frontend-platform.d.ts | 1 - src/generic/DraggableList/SortableItem.tsx | 45 +- src/generic/FieldFeedback.jsx | 32 +- src/generic/FormikControl.tsx | 8 +- src/generic/Loading.tsx | 4 +- src/generic/WysiwygEditor.jsx | 8 +- .../AlertAgreementGatedFeature.tsx | 14 +- src/generic/alert-error/AlertError.test.tsx | 2 +- src/generic/alert-message/index.tsx | 11 +- .../ComponentCountSnippet.test.tsx | 5 +- .../clipboard/hooks/useClipboard.test.tsx | 8 +- src/generic/clipboard/hooks/useClipboard.ts | 10 +- .../components/PopoverContent.tsx | 8 +- .../components/WhatsInClipboard.tsx | 4 +- .../paste-component/components/index.ts | 2 +- .../clipboard/paste-component/index.tsx | 5 +- .../clipboard/paste-component/messages.ts | 2 +- src/generic/component-count/index.tsx | 14 +- .../configure-modal/AdvancedTab.test.jsx | 25 +- src/generic/configure-modal/AdvancedTab.tsx | 226 +++--- src/generic/configure-modal/BasicTab.jsx | 96 +-- .../configure-modal/ConfigureModal.test.tsx | 153 ++-- .../configure-modal/ConfigureModal.tsx | 20 +- .../configure-modal/PrereqSettings.jsx | 12 +- src/generic/configure-modal/UnitTab.tsx | 79 ++- src/generic/configure-modal/VisibilityTab.jsx | 43 +- .../course-stepper/CourseStepper.test.tsx | 10 +- src/generic/course-stepper/index.tsx | 76 +- src/generic/course-upload-image/data/api.js | 9 +- src/generic/course-upload-image/index.jsx | 102 +-- .../CreateOrRerunCourseForm.jsx | 130 ++-- .../CreateOrRerunCourseForm.test.jsx | 25 +- .../factories/mockApiResponses.jsx | 3 +- src/generic/create-or-rerun-course/hooks.jsx | 7 +- src/generic/data/api.mock.ts | 3 +- src/generic/data/api.ts | 20 +- src/generic/delete-modal/DeleteModal.test.jsx | 27 +- src/generic/delete-modal/DeleteModal.tsx | 4 +- src/generic/divider/Divider.jsx | 4 +- src/generic/help-sidebar/HelpSidebar.test.tsx | 13 +- src/generic/help-sidebar/index.ts | 2 +- src/generic/hooks/context/hooks.test.tsx | 6 +- src/generic/hooks/context/iFrameContext.tsx | 9 +- src/generic/hooks/tests/hooks.test.tsx | 17 +- .../hooks/tests/useEventListener.test.tsx | 4 +- .../InplaceTextEditor.test.tsx | 6 +- .../internet-connection-alert/index.jsx | 5 +- src/generic/key-utils.test.ts | 162 +++-- src/generic/key-utils.ts | 4 +- .../LibraryReferenceCard.test.tsx | 48 +- .../LibraryReferenceCard.tsx | 15 +- .../loading-button/LoadingButton.test.tsx | 32 +- src/generic/loading-button/index.tsx | 6 +- src/generic/modal-dropzone/ModalDropzone.jsx | 77 +- src/generic/modal-dropzone/data/api.js | 9 +- .../modal-dropzone/useModalDropzone.jsx | 28 +- src/generic/modal-notification/index.jsx | 14 +- src/generic/model-store/hooks.js | 7 +- src/generic/model-store/index.ts | 5 +- src/generic/processing-notification/index.tsx | 5 +- src/generic/promptIfDirty/usePromptIfDirty.ts | 2 +- src/generic/resizable/Resizable.tsx | 7 +- .../saving-error-alert/SavingErrorAlert.jsx | 4 +- src/generic/sidebar/BlockCardButton.tsx | 6 +- src/generic/sidebar/InfoSidebarMenu.tsx | 7 +- src/generic/sidebar/Sidebar.test.tsx | 5 +- src/generic/sidebar/Sidebar.tsx | 74 +- src/generic/sidebar/SidebarContent.tsx | 2 +- src/generic/sidebar/SidebarSection.tsx | 11 +- src/generic/sidebar/SidebarTitle.tsx | 6 +- src/generic/sidebar/index.tsx | 6 +- src/generic/sidebar/messages.ts | 2 +- src/generic/sub-header/SubHeader.tsx | 10 +- src/generic/tag-count/index.tsx | 16 +- src/generic/toast-context/index.test.tsx | 26 +- src/generic/toast-context/index.tsx | 2 +- src/generic/types.ts | 2 +- src/generic/unlink-modal/UnlinkModal.test.tsx | 44 +- src/generic/unlink-modal/UnlinkModal.tsx | 16 +- src/generic/unlink-modal/data/apiHooks.ts | 8 +- src/generic/unlink-modal/index.tsx | 2 +- .../UpstreamInfoIcon.test.tsx | 6 +- src/generic/upstream-info-icon/index.tsx | 26 +- src/grading-settings/GradingSettings.jsx | 5 +- src/grading-settings/GradingSettings.test.jsx | 6 +- .../assignments/AssignmentItem.jsx | 7 +- .../assignments/AssignmentTypeName.jsx | 7 +- .../assignment-section/index.jsx | 19 +- .../assignment-section/messages.ts | 4 +- src/grading-settings/data/api.js | 3 +- .../deadline-section/DeadlineSection.test.jsx | 53 +- .../deadline-section/index.jsx | 12 +- .../grading-scale/GradingScale.jsx | 10 +- .../components/GradingScaleHandle.jsx | 14 +- .../components/GradingScaleSegment.tsx | 46 +- .../grading-scale/components/index.jsx | 2 +- .../grading-scale/react-ranger.js | 133 ++-- src/grading-settings/grading-scale/utils.js | 30 +- .../grading-sidebar/GradingSidebar.test.jsx | 4 +- src/grading-settings/messages.ts | 2 +- .../GroupConfigurations.test.jsx | 11 +- .../groupConfigurationResponseMock.js | 3 +- .../common/TitleButton.jsx | 10 +- .../common/UsageList.test.jsx | 11 +- .../ContentGroupCard.jsx | 122 ++-- .../ContentGroupCard.test.jsx | 30 +- .../ContentGroupForm.jsx | 6 +- .../ContentGroupForm.test.jsx | 32 +- .../ContentGroupsSection.test.jsx | 19 +- .../content-groups-section/index.jsx | 58 +- src/group-configurations/data/api.js | 3 +- .../EmptyPlaceholder.test.jsx | 11 +- .../EnrollmentTrackGroupsSection.test.jsx | 17 +- .../ExperimentCard.jsx | 135 ++-- .../ExperimentCard.test.jsx | 23 +- .../ExperimentConfigurationsSection.test.jsx | 19 +- .../ExperimentForm.jsx | 6 +- .../ExperimentForm.test.jsx | 23 +- .../ExperimentFormGroups.jsx | 6 +- .../constants.ts | 10 +- .../index.jsx | 62 +- .../utils.js | 12 +- .../utils.test.js | 48 +- .../validation.js | 79 ++- .../group-configuration-sidebar/index.jsx | 11 +- .../group-configuration-sidebar/utils.jsx | 6 +- src/group-configurations/index.tsx | 5 +- src/head/Head.test.jsx | 6 +- src/header/Header.tsx | 62 +- src/header/hooks.test.tsx | 59 +- src/header/hooks.tsx | 34 +- src/help-urls/__mocks__/helpUrls.js | 24 +- src/help-urls/hooks.tsx | 2 +- src/hooks.ts | 27 +- src/import-page/CourseImportContext.tsx | 10 +- src/import-page/CourseImportPage.test.tsx | 15 +- src/import-page/CourseImportPage.tsx | 3 +- src/import-page/data/api.ts | 4 +- .../file-section/FileSection.test.tsx | 15 +- .../import-stepper/ImportStepper.test.tsx | 15 +- .../import-stepper/ImportStepper.tsx | 41 +- src/index.jsx | 27 +- .../ConfirmationView.tsx | 8 +- .../LegacyLibMigrationPage.test.tsx | 26 +- .../LegacyLibMigrationPage.tsx | 38 +- src/library-authoring/EmptyStates.tsx | 3 +- .../LibraryAuthoringPage.test.tsx | 62 +- .../LibraryAuthoringPage.tsx | 82 +-- .../LibraryBlock/LibraryBlock.tsx | 9 +- src/library-authoring/LibraryContent.test.tsx | 12 +- src/library-authoring/LibraryContent.tsx | 2 +- src/library-authoring/LibraryLayout.tsx | 10 +- .../__mocks__/libraryComponentsMock.ts | 3 +- .../add-content/AddContent.test.tsx | 13 +- .../add-content/AddContent.tsx | 132 ++-- .../add-content/AddContentWorkflow.test.tsx | 11 +- .../PickLibraryContentModal.test.tsx | 57 +- .../add-content/PickLibraryContentModal.tsx | 9 +- src/library-authoring/add-content/messages.ts | 12 +- .../backup-restore/LibraryBackupPage.test.tsx | 20 +- .../backup-restore/LibraryBackupPage.tsx | 3 +- .../backup-restore/data/api.ts | 11 +- .../backup-restore/data/hooks.test.tsx | 6 +- .../backup-restore/data/hooks.ts | 35 +- .../collections/CollectionDetails.test.tsx | 29 +- .../collections/CollectionDetails.tsx | 26 +- .../collections/CollectionInfo.tsx | 6 +- .../collections/CollectionInfoHeader.test.tsx | 29 +- .../LibraryCollectionPage.test.tsx | 20 +- .../collections/LibraryCollectionPage.tsx | 82 ++- .../common/context/ComponentPickerContext.tsx | 4 +- .../common/context/LibraryContext.tsx | 12 +- .../common/context/MultiLibraryContext.tsx | 2 +- .../common/context/PublishedFilterContext.tsx | 2 +- .../common/context/SidebarContext.tsx | 4 +- .../ComponentAdvancedAssets.tsx | 40 +- .../ComponentAdvancedInfo.test.tsx | 43 +- .../component-info/ComponentAdvancedInfo.tsx | 56 +- .../component-info/ComponentDetails.test.tsx | 33 +- .../component-info/ComponentInfo.test.tsx | 2 +- .../component-info/ComponentInfo.tsx | 32 +- .../ComponentInfoHeader.test.tsx | 29 +- .../ComponentManagement.test.tsx | 31 +- .../component-info/ComponentManagement.tsx | 7 +- .../component-info/ComponentPreview.test.tsx | 35 +- .../component-info/ComponentUsage.tsx | 46 +- .../component-info/ComponentUsageTab.test.tsx | 33 +- .../component-picker/ComponentPicker.test.tsx | 66 +- .../component-picker/ComponentPicker.tsx | 38 +- .../component-picker/SelectLibrary.tsx | 22 +- src/library-authoring/components/BaseCard.tsx | 27 +- .../components/CollectionCard.test.tsx | 30 +- .../components/CollectionCard.tsx | 16 +- .../components/ComponentCard.test.tsx | 23 +- .../components/ComponentCard.tsx | 14 +- .../components/ComponentDeleter.test.tsx | 23 +- .../components/ComponentDeleter.tsx | 5 +- .../components/ComponentEditorModal.tsx | 2 +- .../components/ComponentMenu.tsx | 6 +- .../components/PublicReadToggle.tsx | 4 +- .../containers/ContainerCard.test.tsx | 12 +- .../containers/ContainerCard.tsx | 105 +-- .../containers/ContainerDeleter.test.tsx | 28 +- .../containers/ContainerDeleter.tsx | 8 +- .../containers/ContainerInfo.test.tsx | 71 +- .../containers/ContainerInfo.tsx | 40 +- .../containers/ContainerInfoHeader.test.tsx | 29 +- .../containers/ContainerOrganize.test.tsx | 31 +- .../containers/ContainerOrganize.tsx | 4 +- .../containers/ContainerRemover.test.tsx | 14 +- .../containers/ContainerRemover.tsx | 13 +- src/library-authoring/containers/index.tsx | 4 +- .../CreateCollectionModal.tsx | 24 +- .../CreateContainerModal.test.tsx | 5 +- .../create-container/CreateContainerModal.tsx | 14 +- .../CreateLegacyLibrary.tsx | 73 +- .../create-legacy-library/data/api.ts | 6 +- .../create-library/CreateLibrary.test.tsx | 62 +- .../create-library/CreateLibrary.tsx | 118 ++-- .../create-library/CreateLibraryModal.tsx | 2 +- .../create-library/data/api.ts | 8 +- .../create-library/data/apiHooks.test.tsx | 27 +- .../create-library/data/apiHooks.ts | 30 +- src/library-authoring/data/api.mocks.ts | 294 ++++---- src/library-authoring/data/api.ts | 146 ++-- src/library-authoring/data/apiHooks.ts | 95 +-- .../generic/filter-by-published/index.tsx | 9 +- .../generic/history-widget/index.tsx | 36 +- .../ManageCollections.test.tsx | 87 ++- .../manage-collections/ManageCollections.tsx | 13 +- .../ParentBreadcrumbs.test.tsx | 20 +- .../generic/parent-breadcrumbs/index.tsx | 6 +- .../publish-status-buttons/PublishedChip.tsx | 4 +- .../generic/publish-status-buttons/index.tsx | 2 +- .../generic/status-widget/index.tsx | 49 +- .../hierarchy/ItemHierarchy.tsx | 14 +- .../hierarchy/ItemHierarchyPublisher.tsx | 4 +- src/library-authoring/hierarchy/messages.ts | 60 +- .../CourseImportHomePage.test.tsx | 2 +- .../import-course/CourseImportHomePage.tsx | 28 +- .../import-course/ImportDetailsPage.test.tsx | 44 +- .../import-course/ImportDetailsPage.tsx | 44 +- .../import-course/ImportedCourseCard.test.tsx | 2 +- .../import-course/ImportedCourseCard.tsx | 6 +- .../import-course/PlaceholderCard.tsx | 2 +- .../import-course/messages.ts | 28 +- .../stepper/ImportStepperPage.test.tsx | 29 +- .../stepper/ImportStepperPage.tsx | 90 +-- .../stepper/ReviewImportDetails.test.tsx | 66 +- .../stepper/ReviewImportDetails.tsx | 38 +- .../import-course/stepper/SummaryCard.tsx | 44 +- src/library-authoring/index.tsx | 6 +- .../PreviewChangesEmbed.tsx | 12 +- .../CollectionDropdownFilter.test.tsx | 16 +- .../CollectionDropdownFilter.tsx | 18 +- .../LibraryDropdownFilter.test.tsx | 23 +- .../library-filters/LibraryDropdownFilter.tsx | 12 +- .../library-filters/MainFilters.tsx | 23 +- .../library-filters/SidebarFilters.tsx | 29 +- .../library-filters/index.tsx | 2 +- .../library-info/LibraryInfo.test.tsx | 7 +- .../library-info/LibraryInfo.tsx | 7 +- .../library-info/LibraryInfoHeader.test.tsx | 7 +- .../library-info/LibraryInfoHeader.tsx | 2 +- .../library-sidebar/LibrarySidebar.tsx | 7 +- .../library-team/AddLibraryTeamMember.tsx | 12 +- .../library-team/LibraryTeam.test.tsx | 19 +- .../library-team/LibraryTeam.tsx | 62 +- .../library-team/LibraryTeamMember.tsx | 82 +-- .../library-team/LibraryTeamModal.tsx | 2 +- .../library-team/messages.ts | 6 +- src/library-authoring/routes.test.tsx | 4 +- src/library-authoring/routes.ts | 25 +- .../LibraryContainerChildren.tsx | 40 +- .../LibrarySectionPage.tsx | 4 +- .../LibrarySectionSubsectionPage.test.tsx | 7 +- .../LibrarySubsectionPage.tsx | 13 +- .../units/LibraryUnitBlocks.tsx | 23 +- .../units/LibraryUnitPage.test.tsx | 12 +- .../units/LibraryUnitPage.tsx | 17 +- .../CourseOptimizerPage.test.js | 16 +- src/optimizer-page/CourseOptimizerPage.tsx | 36 +- src/optimizer-page/data/api.test.js | 16 +- src/optimizer-page/data/api.ts | 21 +- src/optimizer-page/data/slice.test.ts | 13 +- src/optimizer-page/data/slice.ts | 2 +- src/optimizer-page/data/thunks.ts | 7 +- src/optimizer-page/mocks/mockApiResponse.js | 12 +- .../scan-results/BrokenLinkTable.test.tsx | 52 +- .../scan-results/BrokenLinkTable.tsx | 86 +-- .../scan-results/CustomIcon.jsx | 4 +- .../scan-results/ScanResults.test.js | 234 +++--- .../scan-results/ScanResults.tsx | 388 +++++----- .../scan-results/SectionCollapsible.test.tsx | 11 +- .../scan-results/SectionCollapsible.tsx | 46 +- .../scan-results/filterModal.jsx | 27 +- src/optimizer-page/types.ts | 12 +- src/optimizer-page/utils.ts | 43 +- .../PagesAndResources.test.tsx | 20 +- src/pages-and-resources/PagesAndResources.tsx | 62 +- .../PagesAndResourcesProvider.tsx | 2 +- src/pages-and-resources/SettingsComponent.jsx | 14 +- .../app-settings-modal/AppSettingsModal.jsx | 47 +- src/pages-and-resources/data/thunks.js | 9 +- .../discussions/DiscussionsSettings.jsx | 26 +- .../discussions/DiscussionsSettings.test.jsx | 100 ++- .../app-config-form/AppConfigForm.jsx | 25 +- .../AppConfigFormSaveButton.jsx | 9 +- .../apps/lti/LtiConfigForm.jsx | 29 +- .../apps/openedx/OpenedXConfigForm.jsx | 27 +- .../apps/openedx/OpenedXConfigForm.test.jsx | 34 +- .../apps/shared/AppExternalLinks.jsx | 10 +- .../apps/shared/DiscussionRestriction.jsx | 33 +- .../shared/DiscussionRestriction.test.jsx | 39 +- .../apps/shared/DivisionByGroupFields.jsx | 121 ++-- .../RestrictDatesInput.jsx | 5 +- .../DiscussionTopics.test.jsx | 9 +- .../shared/discussion-topics/TopicItem.jsx | 11 +- .../discussions/app-config-form/messages.ts | 6 +- .../discussions/app-config-form/utils.js | 11 +- .../discussions/app-list/AppCard.jsx | 13 +- .../discussions/app-list/AppCard.test.jsx | 5 +- .../discussions/app-list/AppList.jsx | 67 +- .../discussions/app-list/AppList.test.jsx | 13 +- .../discussions/app-list/FeatureList.test.jsx | 5 +- .../discussions/app-list/FeaturesList.jsx | 4 +- .../discussions/app-list/FeaturesTable.jsx | 46 +- .../app-list/FeaturesTable.test.jsx | 3 +- .../discussions/data/api.js | 3 +- .../discussions/data/constants.ts | 5 +- .../discussions/data/redux.test.js | 95 +-- .../discussions/data/thunks.js | 9 +- .../discussions/factories/mockApiResponses.js | 6 +- src/pages-and-resources/discussions/index.ts | 2 +- .../discussions/messages.ts | 4 +- src/pages-and-resources/pages/PageGrid.jsx | 17 +- .../README.md | 3 +- .../AdditionalCoursePluginSlot/README.md | 46 +- .../README.md | 25 +- .../README.md | 15 +- .../index.tsx | 8 +- .../CourseAuthoringUnitSidebarSlot/README.md | 35 +- .../README.v1.md | 38 +- .../CourseAuthoringUnitSidebarSlot/index.tsx | 14 +- src/plugin-slots/CourseFilesSlot/README.md | 14 +- .../CourseOutlineHeaderActionsSlot/README.md | 28 +- .../index.test.tsx | 16 +- .../CourseOutlineHeaderActionsSlot/index.tsx | 14 +- .../CourseOutlinePageAlertsSlot/README.md | 10 +- .../CourseOutlinePageAlertsSlot/index.tsx | 4 +- .../README.md | 17 +- .../README.md | 19 +- .../CourseUnitHeaderActionsSlot/README.md | 23 +- .../CourseUnitHeaderActionsSlot/index.jsx | 11 +- src/plugin-slots/CourseVideosSlot/README.md | 14 +- src/plugin-slots/EditFileAlertsSlot/README.md | 10 +- .../EditVideoAlertsSlot/README.md | 10 +- src/plugin-slots/PageBannerSlot/Readme.md | 5 +- src/plugin-slots/PageBannerSlot/index.tsx | 3 +- src/plugin-slots/README.md | 24 +- src/plugin-slots/StudioFooterSlot/README.md | 15 +- .../ScheduleAndDetails.test.jsx | 18 +- .../__mocks__/courseDetails.js | 8 +- .../__mocks__/courseSettings.js | 3 +- .../basic-section/index.jsx | 5 +- .../credit-section/messages.ts | 3 +- .../details-section/index.jsx | 4 +- src/schedule-and-details/index.jsx | 17 +- .../InstructorsSection.test.jsx | 20 +- .../InstructorContainer.test.jsx | 15 +- .../instructor-container/index.jsx | 10 +- .../IntroducingSection.test.jsx | 5 +- .../ExtendedCourseDetails.test.jsx | 15 +- .../introducing-section/index.jsx | 4 +- .../IntroductionVideo.test.jsx | 5 +- .../license-section/constants.ts | 4 +- .../license-section/hooks.jsx | 13 +- .../license-commons-options/index.jsx | 5 +- .../license-icons/LicenseIcons.test.jsx | 5 +- .../license-section/license-icons/index.jsx | 7 +- .../license-selector/index.jsx | 4 +- .../pacing-section/index.jsx | 4 +- .../pacing-section/messages.ts | 3 +- .../RequirementsSection.test.jsx | 9 +- .../entrance-exam/EntranceExam.test.jsx | 8 +- .../GradeRequirements.test.jsx | 5 +- .../grade-requirements/messages.ts | 3 +- .../CertificateDisplayRow.jsx | 5 +- .../schedule-row/ScheduleRow.jsx | 4 +- .../schedule-sidebar/messages.ts | 2 +- src/search-manager/BlockTypeLabel.tsx | 2 +- src/search-manager/ClearFiltersButton.tsx | 8 +- src/search-manager/FilterByBlockType.tsx | 51 +- src/search-manager/FilterByPublished.tsx | 6 +- src/search-manager/FilterByTags.tsx | 100 +-- src/search-manager/Highlight.tsx | 9 +- src/search-manager/SearchFilterWidget.tsx | 22 +- src/search-manager/SearchKeywordsField.tsx | 8 +- src/search-manager/SearchManager.ts | 27 +- src/search-manager/SearchSortWidget.tsx | 8 +- src/search-manager/Stats.tsx | 4 +- src/search-manager/data/api.mock.ts | 4 +- src/search-manager/data/api.ts | 112 +-- src/search-manager/data/apiHooks.test.tsx | 3 +- src/search-manager/data/apiHooks.ts | 64 +- src/search-manager/hooks.ts | 7 +- src/search-manager/index.ts | 23 +- src/search-modal/EmptyStates.tsx | 10 +- src/search-modal/SearchModal.tsx | 4 +- src/search-modal/SearchResult.tsx | 18 +- src/search-modal/SearchResults.tsx | 3 +- src/search-modal/SearchUI.test.tsx | 156 ++-- src/search-modal/SearchUI.tsx | 20 +- .../__mocks__/empty-search-result.json | 2 +- src/search-modal/__mocks__/facet-search.json | 12 +- src/setupTest.js | 3 +- src/store.ts | 2 +- src/studio-home/StudioHome.tsx | 6 +- src/studio-home/__mocks__/studioHomeMock.ts | 9 +- src/studio-home/card-item/index.tsx | 87 +-- .../CollapsibleStateWithAction.test.jsx | 11 +- .../collapsible-state-with-action/index.jsx | 44 +- src/studio-home/data/api.mocks.ts | 28 +- src/studio-home/data/api.ts | 11 +- src/studio-home/data/thunks.js | 7 +- .../factories/mockApiResponses.tsx | 3 +- .../home-sidebar/HomeSidebar.test.jsx | 15 +- src/studio-home/home-sidebar/index.jsx | 10 +- .../processing-courses/course-item/index.jsx | 16 +- .../tabs-section/TabsSection.test.tsx | 65 +- .../contact-administrator/index.jsx | 8 +- .../courses-filter-menu/index.jsx | 6 +- .../courses-filter-menu/index.test.jsx | 17 +- .../courses-imported-filter-modal/context.tsx | 13 +- .../courses-imported-filter-modal/index.tsx | 6 +- .../courses-order-filter-menu/index.test.jsx | 17 +- .../courses-types-filter-menu/index.test.jsx | 17 +- .../courses-filters/index.test.jsx | 48 +- .../courses-tab/courses-filters/index.tsx | 8 +- .../courses-tab/courses-filters/messages.ts | 1 - .../tabs-section/courses-tab/index.tsx | 227 +++--- src/studio-home/tabs-section/index.tsx | 4 +- .../tabs-section/libraries-tab/index.tsx | 202 +++--- .../WelcomeLibrariesV2Alert.tsx | 42 +- .../tabs-section/libraries-v2-tab/index.tsx | 211 +++--- .../libraries-v2-filters/index.test.tsx | 37 +- .../libraries-v2-filters/index.tsx | 4 +- .../libraries-v2-filter-menu/index.tsx | 8 +- src/studio-home/tabs-section/utils.js | 13 +- src/taxonomy/TaxonomyLayout.tsx | 7 +- src/taxonomy/TaxonomyListPage.test.tsx | 29 +- src/taxonomy/TaxonomyListPage.tsx | 14 +- src/taxonomy/data/api.ts | 31 +- src/taxonomy/data/apiHooks.test.jsx | 2 +- src/taxonomy/data/apiHooks.ts | 93 ++- src/taxonomy/export-modal/index.jsx | 2 +- src/taxonomy/import-tags/ImportTagsWizard.jsx | 155 ++-- .../import-tags/ImportTagsWizard.test.jsx | 35 +- src/taxonomy/import-tags/messages.ts | 22 +- src/taxonomy/index.ts | 4 +- src/taxonomy/manage-orgs/ManageOrgsModal.jsx | 43 +- src/taxonomy/manage-orgs/data/api.test.jsx | 1 - src/taxonomy/manage-orgs/data/api.ts | 11 +- src/taxonomy/manage-orgs/messages.ts | 4 +- .../tag-list/OptionalExpandLink.test.tsx | 13 +- src/taxonomy/tag-list/TagListTable.test.jsx | 43 +- src/taxonomy/tag-list/TagListTable.tsx | 35 +- src/taxonomy/tag-list/constants.ts | 7 +- src/taxonomy/tag-list/hooks.test.tsx | 2 +- src/taxonomy/tag-list/hooks.ts | 16 +- src/taxonomy/tag-list/mockData.ts | 120 ++-- src/taxonomy/tag-list/tagColumns.tsx | 2 +- src/taxonomy/tag-list/tagTree.ts | 6 +- src/taxonomy/taxonomy-card/index.jsx | 23 +- .../taxonomy-detail/TaxonomyDetailPage.jsx | 8 +- src/taxonomy/taxonomy-menu/TaxonomyMenu.jsx | 45 +- .../taxonomy-menu/TaxonomyMenu.test.tsx | 23 +- src/taxonomy/tree-table/CreateRow.test.tsx | 2 +- src/taxonomy/tree-table/EditableCell.test.tsx | 2 +- src/taxonomy/tree-table/NestedRows.test.tsx | 2 +- src/taxonomy/tree-table/NestedRows.tsx | 84 +-- src/taxonomy/tree-table/TableBody.tsx | 50 +- src/taxonomy/tree-table/TableView.test.tsx | 2 +- src/taxonomy/tree-table/TableView.tsx | 15 +- src/taxonomy/tree-table/index.ts | 2 +- src/testUtils.tsx | 18 +- src/textbooks/Textbook.test.jsx | 16 +- src/textbooks/Textbooks.jsx | 36 +- src/textbooks/data/api.js | 6 +- src/textbooks/data/thunk.test.js | 5 +- .../EmptyPlaceholder.test.jsx | 11 +- src/textbooks/textbook-card/TextbooksCard.jsx | 110 +-- .../textbook-card/TextbooksCard.test.jsx | 28 +- src/textbooks/textbook-form/TextbookForm.jsx | 10 +- .../textbook-form/TextbookForm.test.jsx | 30 +- src/textbooks/textbook-form/validations.js | 19 +- src/textbooks/utils.js | 4 +- src/types.ts | 8 +- src/utils.tsx | 15 +- tsconfig.json | 32 +- 1156 files changed, 18053 insertions(+), 13014 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5c58eb3c34..0d03424ff8 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,8 +5,9 @@ Design decisions and their rationales should be documented in the repo (docstrin [OEP-19](https://open-edx-proposals.readthedocs.io/en/latest/oep-0019-bp-developer-documentation.html), and can be linked here. Useful information to include: + - Which user roles will this change impact? Common user roles are "Learner", "Course Author", -"Developer", and "Operator". + "Developer", and "Operator". - Include screenshots for changes to the UI (ideally, both "before" and "after" screenshots, if applicable). ## Supporting information @@ -21,6 +22,7 @@ Please provide detailed step-by-step instructions for manually testing this chan ## Other information Include anything else that will help reviewers and consumers understand the change. + - Does this change depend on other changes elsewhere? - Any special concerns or limitations? For example: deprecations, migrations, security, or accessibility. diff --git a/.oxlintrc.json b/.oxlintrc.json index 7aa558b4d9..9b11fe5569 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -7,7 +7,7 @@ "rules": { "eslint/no-unused-vars": ["warn", { // Allow using {ignoredProp, ...keepTheRest} to omit a prop like 'ignoredProp' from an object. - "ignoreRestSiblings": true, + "ignoreRestSiblings": true }], // We disable exhaustive-deps because: it's noisy, and we often include extra deps when we want a memoized thing to // re-calculate after some change, even if we're not using that thing in the calculation. @@ -21,11 +21,11 @@ // complete, but we rarely if ever want that; we usually want to // continue invalidating more things immediately. So we don't usually // want to await this. - "invalidateQueries", + "invalidateQueries" ] }] }, "ignorePatterns": [ - "webpack.dev-tutor.config.js", + "webpack.dev-tutor.config.js" ] -} \ No newline at end of file +} diff --git a/plugins/course-apps/calculator/package.json b/plugins/course-apps/calculator/package.json index dec9813433..7c1784273f 100644 --- a/plugins/course-apps/calculator/package.json +++ b/plugins/course-apps/calculator/package.json @@ -1,17 +1,17 @@ { - "name": "@openedx-plugins/course-app-calculator", - "version": "0.1.0", - "description": "Calculator configuration for courses using it", - "peerDependencies": { - "@edx/frontend-app-authoring": "*", - "@edx/frontend-platform": "*", - "@openedx/paragon": "*", - "prop-types": "*", - "react": "*" - }, - "peerDependenciesMeta": { - "@edx/frontend-app-authoring": { - "optional": true - } + "name": "@openedx-plugins/course-app-calculator", + "version": "0.1.0", + "description": "Calculator configuration for courses using it", + "peerDependencies": { + "@edx/frontend-app-authoring": "*", + "@edx/frontend-platform": "*", + "@openedx/paragon": "*", + "prop-types": "*", + "react": "*" + }, + "peerDependenciesMeta": { + "@edx/frontend-app-authoring": { + "optional": true } + } } diff --git a/plugins/course-apps/dates/package.json b/plugins/course-apps/dates/package.json index f3678afe18..3e944a64a2 100644 --- a/plugins/course-apps/dates/package.json +++ b/plugins/course-apps/dates/package.json @@ -1,17 +1,17 @@ { - "name": "@openedx-plugins/course-app-dates", - "version": "0.1.0", - "description": "Dates configuration for courses using it", - "peerDependencies": { - "@edx/frontend-app-authoring": "*", - "@edx/frontend-platform": "*", - "@openedx/paragon": "*", - "prop-types": "*", - "react": "*" - }, - "peerDependenciesMeta": { - "@edx/frontend-app-authoring": { - "optional": true - } + "name": "@openedx-plugins/course-app-dates", + "version": "0.1.0", + "description": "Dates configuration for courses using it", + "peerDependencies": { + "@edx/frontend-app-authoring": "*", + "@edx/frontend-platform": "*", + "@openedx/paragon": "*", + "prop-types": "*", + "react": "*" + }, + "peerDependenciesMeta": { + "@edx/frontend-app-authoring": { + "optional": true } + } } diff --git a/plugins/course-apps/edxnotes/package.json b/plugins/course-apps/edxnotes/package.json index 39c643d015..d15c7852fe 100644 --- a/plugins/course-apps/edxnotes/package.json +++ b/plugins/course-apps/edxnotes/package.json @@ -1,17 +1,17 @@ { - "name": "@openedx-plugins/course-app-edxnotes", - "version": "0.1.0", - "description": "edxnotes configuration for courses using it", - "peerDependencies": { - "@edx/frontend-app-authoring": "*", - "@edx/frontend-platform": "*", - "@openedx/paragon": "*", - "prop-types": "*", - "react": "*" - }, - "peerDependenciesMeta": { - "@edx/frontend-app-authoring": { - "optional": true - } + "name": "@openedx-plugins/course-app-edxnotes", + "version": "0.1.0", + "description": "edxnotes configuration for courses using it", + "peerDependencies": { + "@edx/frontend-app-authoring": "*", + "@edx/frontend-platform": "*", + "@openedx/paragon": "*", + "prop-types": "*", + "react": "*" + }, + "peerDependenciesMeta": { + "@edx/frontend-app-authoring": { + "optional": true } + } } diff --git a/plugins/course-apps/learning_assistant/Settings.jsx b/plugins/course-apps/learning_assistant/Settings.jsx index 7b9bb916b3..6aba6ab2da 100644 --- a/plugins/course-apps/learning_assistant/Settings.jsx +++ b/plugins/course-apps/learning_assistant/Settings.jsx @@ -15,7 +15,7 @@ const LearningAssistantSettings = ({ onClose }) => { const intl = useIntl(); // We need to render more than one link, so we use the bodyChildren prop. - const bodyChildren = ( + const bodyChildren = appInfo?.documentationLinks?.learnMoreOpenaiDataPrivacy && appInfo?.documentationLinks?.learnMoreOpenai ? (
@@ -41,8 +41,7 @@ const LearningAssistantSettings = ({ onClose }) => { )}
) - : null - ); + : null; return ( { }; +const onClose = () => {}; describe('Learning Assistant Settings', () => { it('renders', async () => { const initialState = { models: { courseApps: { - learning_assistant: - { + learning_assistant: { id: 'learning_assistant', enabled: true, name: 'Learning Assistant', @@ -38,10 +37,12 @@ describe('Learning Assistant Settings', () => { render(); const toggleDescription = 'Reinforce learning concepts by sharing text-based course content ' - + 'with OpenAI (via API) to power an in-course Learning Assistant. Learners can leave feedback about the quality ' - + 'of the AI-powered experience for use by edX to improve the performance of the tool.'; + + 'with OpenAI (via API) to power an in-course Learning Assistant. Learners can leave feedback about the quality ' + + 'of the AI-powered experience for use by edX to improve the performance of the tool.'; - await waitFor(() => expect(screen.getByRole('heading', { name: 'Configure Learning Assistant' })).toBeInTheDocument()); + await waitFor(() => + expect(screen.getByRole('heading', { name: 'Configure Learning Assistant' })).toBeInTheDocument() + ); await waitFor(() => expect(screen.getByText(toggleDescription)).toBeInTheDocument()); await waitFor(() => expect(screen.getByText('Learn more about how OpenAI handles data')).toBeInTheDocument()); await waitFor(() => expect(screen.getByText('Learn more about OpenAI API data privacy')).toBeInTheDocument()); diff --git a/plugins/course-apps/learning_assistant/package.json b/plugins/course-apps/learning_assistant/package.json index 1ad1a000a9..07d90b0b3b 100644 --- a/plugins/course-apps/learning_assistant/package.json +++ b/plugins/course-apps/learning_assistant/package.json @@ -1,19 +1,18 @@ { - "name": "@openedx-plugins/course-app-learning_assistant", - "version": "0.1.0", - "description": "Learning Assistant configuration for courses using it", - "peerDependencies": { - "@edx/frontend-app-authoring": "*", - "@edx/frontend-platform": "*", - "@openedx/paragon": "*", - "prop-types": "*", - "react": "*", - "yup": "*" - }, - "peerDependenciesMeta": { - "@edx/frontend-app-authoring": { - "optional": true - } + "name": "@openedx-plugins/course-app-learning_assistant", + "version": "0.1.0", + "description": "Learning Assistant configuration for courses using it", + "peerDependencies": { + "@edx/frontend-app-authoring": "*", + "@edx/frontend-platform": "*", + "@openedx/paragon": "*", + "prop-types": "*", + "react": "*", + "yup": "*" + }, + "peerDependenciesMeta": { + "@edx/frontend-app-authoring": { + "optional": true } + } } - \ No newline at end of file diff --git a/plugins/course-apps/live/BBBSettings.jsx b/plugins/course-apps/live/BBBSettings.jsx index e81e3aa03c..c5cc14fe9b 100644 --- a/plugins/course-apps/live/BBBSettings.jsx +++ b/plugins/course-apps/live/BBBSettings.jsx @@ -42,15 +42,17 @@ const BbbSettings = ({ }; return ( <> - {isPiiDisabled ? ( -

- {intl.formatMessage(messages.requestPiiSharingEnableForBbb, { provider: providerNames[values.provider] })} -

- ) : ( -

- {intl.formatMessage(messages.providerHelperText, { providerName: providerNames[values.provider] })} -

- )} + {isPiiDisabled ? + ( +

+ {intl.formatMessage(messages.requestPiiSharingEnableForBbb, { provider: providerNames[values.provider] })} +

+ ) : + ( +

+ {intl.formatMessage(messages.providerHelperText, { providerName: providerNames[values.provider] })} +

+ )} @@ -78,33 +80,35 @@ const BbbSettings = ({ showLaunchIcon className="text-primary-500 pt-2" > - { intl.formatMessage(messages.learnMore, { providerName: 'plans' }) } + {intl.formatMessage(messages.learnMore, { providerName: 'plans' })} <> - {isPiiDisabled ? ( -

- {intl.formatMessage(messages.piiSharingEnableHelpTextBbb)} -

- ) : ( - <> - {bbbPlan === bbbPlanTypes.commercial && } - {bbbPlan === bbbPlanTypes.free && ( - - {intl.formatMessage(messages.freePlanMessage)} - - {intl.formatMessage(messages.privacyPolicy)} - - - )} - - )} + {isPiiDisabled ? + ( +

+ {intl.formatMessage(messages.piiSharingEnableHelpTextBbb)} +

+ ) : + ( + <> + {bbbPlan === bbbPlanTypes.commercial && } + {bbbPlan === bbbPlanTypes.free && ( + + {intl.formatMessage(messages.freePlanMessage)} + + {intl.formatMessage(messages.privacyPolicy)} + + + )} + + )} ); diff --git a/plugins/course-apps/live/BbbSettings.test.jsx b/plugins/course-apps/live/BbbSettings.test.jsx index 1336513139..91ae0e439c 100644 --- a/plugins/course-apps/live/BbbSettings.test.jsx +++ b/plugins/course-apps/live/BbbSettings.test.jsx @@ -63,8 +63,14 @@ const mockStore = async ({ const fetchProviderConfigUrl = `${providersApiUrl}/${courseId}/`; const fetchLiveConfigUrl = `${providerConfigurationApiUrl}/${courseId}/`; - axiosMock.onGet(fetchProviderConfigUrl).reply(200, configurationProviders(emailSharing, usernameSharing, 'big_blue_button', isFreeTier)); - axiosMock.onGet(fetchLiveConfigUrl).reply(200, generateLiveConfigurationApiResponse(enabled, piiSharingAllowed, 'bigBlueButton', isFreeTier)); + axiosMock.onGet(fetchProviderConfigUrl).reply( + 200, + configurationProviders(emailSharing, usernameSharing, 'big_blue_button', isFreeTier), + ); + axiosMock.onGet(fetchLiveConfigUrl).reply( + 200, + generateLiveConfigurationApiResponse(enabled, piiSharingAllowed, 'bigBlueButton', isFreeTier), + ); await executeThunk(fetchLiveProviders(courseId), store.dispatch); await executeThunk(fetchLiveConfiguration(courseId), store.dispatch); @@ -88,14 +94,17 @@ describe('BBB Settings', () => { expect(container.querySelector('select[name="tierType"]')).not.toBeDisabled(); }); - test.each([[true, 3], [false, 2]])('Plan dropdown should display correct number of options', async (isFreeTier, noOfOptions) => { - await mockStore({ emailSharing: true, isFreeTier }); - renderComponent(); - const spinner = getByRole(container, 'status'); - await waitForElementToBeRemoved(spinner); - const dropDown = queryByTestId(container, 'plansDropDown'); - expect(getAllByRole(dropDown, 'option').length).toBe(noOfOptions); - }); + test.each([[true, 3], [false, 2]])( + 'Plan dropdown should display correct number of options', + async (isFreeTier, noOfOptions) => { + await mockStore({ emailSharing: true, isFreeTier }); + renderComponent(); + const spinner = getByRole(container, 'status'); + await waitForElementToBeRemoved(spinner); + const dropDown = queryByTestId(container, 'plansDropDown'); + expect(getAllByRole(dropDown, 'option').length).toBe(noOfOptions); + }, + ); test( 'Connect to support and PII sharing message is visible and plans selection is disabled, When pii sharing is disabled, ', diff --git a/plugins/course-apps/live/Settings.jsx b/plugins/course-apps/live/Settings.jsx index 5295d1b420..06ebe67bfd 100644 --- a/plugins/course-apps/live/Settings.jsx +++ b/plugins/course-apps/live/Settings.jsx @@ -30,7 +30,10 @@ const LiveSettings = ({ const { courseId } = useCourseAuthoringContext(); const availableProviders = useSelector((state) => state.live.appIds); const { - piiSharingAllowed, selectedAppId, enabled, status, + piiSharingAllowed, + selectedAppId, + enabled, + status, } = useSelector(state => state.live); const appConfig = useModel('liveAppConfigs', selectedAppId); @@ -52,15 +55,18 @@ const LiveSettings = ({ const validationSchema = { enabled: Yup.boolean(), consumerKey: Yup.string().when(['provider', 'tierType'], { - is: (provider, tier) => provider === 'zoom' || (provider === 'big_blue_button' && tier === bbbPlanTypes.commercial), + is: (provider, tier) => + provider === 'zoom' || (provider === 'big_blue_button' && tier === bbbPlanTypes.commercial), then: Yup.string().required(intl.formatMessage(messages.consumerKeyRequired)), }), consumerSecret: Yup.string().when(['provider', 'tierType'], { - is: (provider, tier) => provider === 'zoom' || (provider === 'big_blue_button' && tier === bbbPlanTypes.commercial), + is: (provider, tier) => + provider === 'zoom' || (provider === 'big_blue_button' && tier === bbbPlanTypes.commercial), then: Yup.string().notRequired(intl.formatMessage(messages.consumerSecretRequired)), }), launchUrl: Yup.string().when(['provider', 'tierType'], { - is: (provider, tier) => provider === 'zoom' || (provider === 'big_blue_button' && tier === bbbPlanTypes.commercial), + is: (provider, tier) => + provider === 'zoom' || (provider === 'big_blue_button' && tier === bbbPlanTypes.commercial), then: Yup.string().required(intl.formatMessage(messages.launchUrlRequired)), }), launchEmail: Yup.string(), @@ -96,9 +102,7 @@ const LiveSettings = ({ enableReinitialize > {({ values, setFieldValue }) => ( - (status === RequestStatus.IN_PROGRESS) ? ( - - ) : ( + (status === RequestStatus.IN_PROGRESS) ? : ( <>

{intl.formatMessage(messages.selectProvider)}

))} - {values.provider === 'zoom' ? + {values.provider === 'zoom' ? + : ( - {!values.piiSharingEnable ? ( -

- {intl.formatMessage(messages.requestPiiSharingEnable, { provider: providerNames[values.provider] })} -

- ) : ( - <> - {(values.piiSharingEmail || values.piiSharingUsername) - && ( -

- {intl.formatMessage(messages.providerHelperText, { providerName: providerNames[values.provider] })} -

- )} - - - - )} + {!values.piiSharingEnable ? + ( +

+ {intl.formatMessage(messages.requestPiiSharingEnable, { provider: providerNames[values.provider] })} +

+ ) : + ( + <> + {(values.piiSharingEmail || values.piiSharingUsername) + && ( +

+ {intl.formatMessage(messages.providerHelperText, { providerName: providerNames[values.provider] })} +

+ )} + + + + )} ); }; diff --git a/plugins/course-apps/live/constants.js b/plugins/course-apps/live/constants.js index 3a1294eb65..3b012a244a 100644 --- a/plugins/course-apps/live/constants.js +++ b/plugins/course-apps/live/constants.js @@ -1,5 +1,8 @@ import { - GoogleMeet, MicrosoftTeams, Zoom, Bbb, + GoogleMeet, + MicrosoftTeams, + Zoom, + Bbb, } from '@openedx/paragon/icons'; export const iconsSrc = { diff --git a/plugins/course-apps/live/package.json b/plugins/course-apps/live/package.json index ac9485ea7e..35a3f33ad4 100644 --- a/plugins/course-apps/live/package.json +++ b/plugins/course-apps/live/package.json @@ -1,22 +1,22 @@ { - "name": "@openedx-plugins/course-app-live", - "version": "0.1.0", - "description": "Live course configuration for courses using it", - "peerDependencies": { - "@edx/frontend-app-authoring": "*", - "@edx/frontend-platform": "*", - "@openedx/paragon": "*", - "@reduxjs/toolkit": "*", - "lodash": "*", - "prop-types": "*", - "react": "*", - "react-redux": "*", - "react-router-dom": "*", - "yup": "*" - }, - "peerDependenciesMeta": { - "@edx/frontend-app-authoring": { - "optional": true - } + "name": "@openedx-plugins/course-app-live", + "version": "0.1.0", + "description": "Live course configuration for courses using it", + "peerDependencies": { + "@edx/frontend-app-authoring": "*", + "@edx/frontend-platform": "*", + "@openedx/paragon": "*", + "@reduxjs/toolkit": "*", + "lodash": "*", + "prop-types": "*", + "react": "*", + "react-redux": "*", + "react-router-dom": "*", + "yup": "*" + }, + "peerDependenciesMeta": { + "@edx/frontend-app-authoring": { + "optional": true } + } } diff --git a/plugins/course-apps/ora_settings/Settings.jsx b/plugins/course-apps/ora_settings/Settings.jsx index 386f3d7420..0ea271817d 100644 --- a/plugins/course-apps/ora_settings/Settings.jsx +++ b/plugins/course-apps/ora_settings/Settings.jsx @@ -5,7 +5,13 @@ import { useIntl } from '@edx/frontend-platform/i18n'; import { useDispatch, useSelector } from 'react-redux'; import { - ActionRow, Alert, Badge, Form, Hyperlink, ModalDialog, StatefulButton, + ActionRow, + Alert, + Badge, + Form, + Hyperlink, + ModalDialog, + StatefulButton, } from '@openedx/paragon'; import { Info } from '@openedx/paragon/icons'; import { updateModel, useModel } from 'CourseAuthoring/generic/model-store'; @@ -54,7 +60,8 @@ const ORASettings = ({ onClose }) => { success = await dispatch(updateModel({ modelType: 'courseApps', model: { - id: appId, enabled: formValues.enableFlexiblePeerGrade, + id: appId, + enabled: formValues.enableFlexiblePeerGrade, }, })); } @@ -88,7 +95,7 @@ const ORASettings = ({ onClose }) => { {formatMessage(messages.enableFlexPeerGradeLabel)} {formValues.enableFlexiblePeerGrade && ( @@ -97,8 +104,8 @@ const ORASettings = ({ onClose }) => { )} - )} - helpText={( + } + helpText={

{formatMessage(messages.enableFlexPeerGradeHelp)}

@@ -112,7 +119,7 @@ const ORASettings = ({ onClose }) => {
- )} + } onChange={handleChange} checked={formValues.enableFlexiblePeerGrade} /> diff --git a/plugins/course-apps/ora_settings/Settings.test.jsx b/plugins/course-apps/ora_settings/Settings.test.jsx index 787042f527..82c7f7b497 100644 --- a/plugins/course-apps/ora_settings/Settings.test.jsx +++ b/plugins/course-apps/ora_settings/Settings.test.jsx @@ -38,7 +38,14 @@ const renderComponent = () => ( - } /> + + + + } + /> @@ -113,7 +120,8 @@ describe('ORASettings', () => { renderComponent(); const errorAlert = screen.getByRole('alert'); - expect(within(errorAlert).getByText('We encountered a technical error when loading this page.', { exact: false })).toBeVisible(); + expect(within(errorAlert).getByText('We encountered a technical error when loading this page.', { exact: false })) + .toBeVisible(); }); it('Displays Permissions Error Alert', async () => { diff --git a/plugins/course-apps/ora_settings/package.json b/plugins/course-apps/ora_settings/package.json index 3f93dcb722..d9dee7e483 100644 --- a/plugins/course-apps/ora_settings/package.json +++ b/plugins/course-apps/ora_settings/package.json @@ -1,20 +1,19 @@ { - "name": "@openedx-plugins/course-app-ora_settings", - "version": "0.1.0", - "description": "Open Response Assessment configuration for courses using it", - "peerDependencies": { - "@edx/frontend-app-authoring": "*", - "@edx/frontend-platform": "*", - "@openedx/paragon": "*", - "prop-types": "*", - "react": "*", - "react-redux": "*", - "yup": "*" - }, - "peerDependenciesMeta": { - "@edx/frontend-app-authoring": { - "optional": true - } + "name": "@openedx-plugins/course-app-ora_settings", + "version": "0.1.0", + "description": "Open Response Assessment configuration for courses using it", + "peerDependencies": { + "@edx/frontend-app-authoring": "*", + "@edx/frontend-platform": "*", + "@openedx/paragon": "*", + "prop-types": "*", + "react": "*", + "react-redux": "*", + "yup": "*" + }, + "peerDependenciesMeta": { + "@edx/frontend-app-authoring": { + "optional": true } + } } - \ No newline at end of file diff --git a/plugins/course-apps/proctoring/Settings.jsx b/plugins/course-apps/proctoring/Settings.jsx index fd1369f4e8..3a8bcda11c 100644 --- a/plugins/course-apps/proctoring/Settings.jsx +++ b/plugins/course-apps/proctoring/Settings.jsx @@ -1,5 +1,8 @@ import React, { - useContext, useEffect, useRef, useState, + useContext, + useEffect, + useRef, + useState, } from 'react'; import classNames from 'classnames'; import EmailValidator from 'email-validator'; @@ -10,7 +13,13 @@ import { getConfig } from '@edx/frontend-platform'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n'; import { - ActionRow, Alert, Badge, Form, Hyperlink, ModalDialog, StatefulButton, + ActionRow, + Alert, + Badge, + Form, + Hyperlink, + ModalDialog, + StatefulButton, } from '@openedx/paragon'; import ExamsApiService from 'CourseAuthoring/data/services/ExamsApiService'; @@ -120,7 +129,9 @@ const ProctoringSettings = ({ onClose }) => { } if (requiresEscalationEmailProviders.includes(formValues.proctoringProvider)) { - studioDataToPostBack.proctored_exam_settings.proctoring_escalation_email = formValues.escalationEmail === '' ? null : formValues.escalationEmail; + studioDataToPostBack.proctored_exam_settings.proctoring_escalation_email = formValues.escalationEmail === '' + ? null + : formValues.escalationEmail; } // only save back to exam service if necessary @@ -161,14 +172,20 @@ const ProctoringSettings = ({ onClose }) => { && !(formValues.escalationEmail === '' && !formValues.enableProctoredExams) ) { if (formValues.escalationEmail === '') { - const errorMessage = intl.formatMessage(messages['authoring.proctoring.escalationemail.error.blank'], { proctoringProviderName: getProviderDisplayLabel(formValues.proctoringProvider) }); + const errorMessage = intl.formatMessage(messages['authoring.proctoring.escalationemail.error.blank'], { + proctoringProviderName: getProviderDisplayLabel(formValues.proctoringProvider), + }); setFormStatus({ isValid: false, errors: { formEscalationEmail: { dialogErrorMessage: ( - + {errorMessage} ), @@ -183,7 +200,15 @@ const ProctoringSettings = ({ onClose }) => { isValid: false, errors: { formEscalationEmail: { - dialogErrorMessage: ({errorMessage}), + dialogErrorMessage: ( + + {errorMessage} + + ), inputErrorMessage: errorMessage, }, }, @@ -280,30 +305,28 @@ const ProctoringSettings = ({ onClose }) => { name="enableProctoredExams" onChange={handleChange} checked={formValues.enableProctoredExams} - label={( + label={
{intl.formatMessage(messages['authoring.proctoring.enableproctoredexams.label'])} - { - formValues.enableProctoredExams && ( - - {intl.formatMessage(messages['authoring.proctoring.enabled'])} - - ) - } + {formValues.enableProctoredExams && ( + + {intl.formatMessage(messages['authoring.proctoring.enabled'])} + + )}
- )} - helpText={( + } + helpText={

{intl.formatMessage(messages['authoring.proctoring.enableproctoredexams.help'])}

{learnMoreLink}
- )} + } /> {/* PROCTORING PROVIDER */} - { formValues.enableProctoredExams && ( + {formValues.enableProctoredExams && ( <>
@@ -320,11 +343,9 @@ const ProctoringSettings = ({ onClose }) => { {getProctoringProviderOptions(availableProctoringProviders)} - { - cannotEditProctoringProvider() - ? intl.formatMessage(messages['authoring.proctoring.provider.help.aftercoursestart']) - : intl.formatMessage(messages['authoring.proctoring.provider.help']) - } + {cannotEditProctoringProvider() + ? intl.formatMessage(messages['authoring.proctoring.provider.help.aftercoursestart']) + : intl.formatMessage(messages['authoring.proctoring.provider.help'])} @@ -351,17 +372,15 @@ const ProctoringSettings = ({ onClose }) => { {Object.prototype.hasOwnProperty.call(formStatus.errors, 'formEscalationEmail') && ( - { - formStatus.errors.formEscalationEmail - && formStatus.errors.formEscalationEmail.inputErrorMessage - } + {formStatus.errors.formEscalationEmail + && formStatus.errors.formEscalationEmail.inputErrorMessage} )}
)} {/* ALLOW OPTING OUT OF PROCTORED EXAMS */} - { isEdxStaff && formValues.enableProctoredExams && !isLtiProviderSelected && ( + {isEdxStaff && formValues.enableProctoredExams && !isLtiProviderSelected && (
@@ -388,21 +407,15 @@ const ProctoringSettings = ({ onClose }) => { } function renderLoading() { - return ( - - ); + return ; } function renderConnectionError() { - return ( - - ); + return ; } function renderPermissionError() { - return ( - - ); + return ; } function renderSaveSuccess() { diff --git a/plugins/course-apps/proctoring/Settings.test.jsx b/plugins/course-apps/proctoring/Settings.test.jsx index 793d983cd3..a2148e89e2 100644 --- a/plugins/course-apps/proctoring/Settings.test.jsx +++ b/plugins/course-apps/proctoring/Settings.test.jsx @@ -1,5 +1,10 @@ import { - render, screen, cleanup, waitFor, fireEvent, act, + render, + screen, + cleanup, + waitFor, + fireEvent, + act, initializeMocks, } from 'CourseAuthoring/testUtils'; diff --git a/plugins/course-apps/proctoring/package.json b/plugins/course-apps/proctoring/package.json index 55e973ed20..3e8dbb4d40 100644 --- a/plugins/course-apps/proctoring/package.json +++ b/plugins/course-apps/proctoring/package.json @@ -1,20 +1,20 @@ { - "name": "@openedx-plugins/course-app-proctoring", - "version": "0.1.0", - "description": "Proctoring configuration for courses using it", - "peerDependencies": { - "@edx/frontend-app-authoring": "*", - "@edx/frontend-platform": "*", - "@openedx/paragon": "*", - "classnames": "*", - "email-validator": "*", - "react": "*", - "prop-types": "*", - "moment": "*" - }, - "peerDependenciesMeta": { - "@edx/frontend-app-authoring": { - "optional": true - } + "name": "@openedx-plugins/course-app-proctoring", + "version": "0.1.0", + "description": "Proctoring configuration for courses using it", + "peerDependencies": { + "@edx/frontend-app-authoring": "*", + "@edx/frontend-platform": "*", + "@openedx/paragon": "*", + "classnames": "*", + "email-validator": "*", + "react": "*", + "prop-types": "*", + "moment": "*" + }, + "peerDependenciesMeta": { + "@edx/frontend-app-authoring": { + "optional": true } + } } diff --git a/plugins/course-apps/progress/Settings.jsx b/plugins/course-apps/progress/Settings.jsx index 04da126bab..1f01c56c79 100644 --- a/plugins/course-apps/progress/Settings.jsx +++ b/plugins/course-apps/progress/Settings.jsx @@ -29,21 +29,19 @@ const ProgressSettings = ({ onClose }) => { validationSchema={{ enableProgressGraph: Yup.boolean() }} onSettingsSave={handleSettingsSave} > - { - ({ handleChange, handleBlur, values }) => ( - showProgressGraphSetting && ( - - ) + {({ handleChange, handleBlur, values }) => ( + showProgressGraphSetting && ( + ) - } + )} ); }; diff --git a/plugins/course-apps/progress/package.json b/plugins/course-apps/progress/package.json index 678de578b9..ea74a0b0c5 100644 --- a/plugins/course-apps/progress/package.json +++ b/plugins/course-apps/progress/package.json @@ -1,18 +1,18 @@ { - "name": "@openedx-plugins/course-app-progress", - "version": "0.1.0", - "description": "Progress configuration for courses using it", - "peerDependencies": { - "@edx/frontend-app-authoring": "*", - "@edx/frontend-platform": "*", - "@openedx/paragon": "*", - "prop-types": "*", - "react": "*", - "yup": "*" - }, - "peerDependenciesMeta": { - "@edx/frontend-app-authoring": { - "optional": true - } + "name": "@openedx-plugins/course-app-progress", + "version": "0.1.0", + "description": "Progress configuration for courses using it", + "peerDependencies": { + "@edx/frontend-app-authoring": "*", + "@edx/frontend-platform": "*", + "@openedx/paragon": "*", + "prop-types": "*", + "react": "*", + "yup": "*" + }, + "peerDependenciesMeta": { + "@edx/frontend-app-authoring": { + "optional": true } + } } diff --git a/plugins/course-apps/teams/GroupEditor.jsx b/plugins/course-apps/teams/GroupEditor.jsx index 5d4d457b5c..bdf942cc91 100644 --- a/plugins/course-apps/teams/GroupEditor.jsx +++ b/plugins/course-apps/teams/GroupEditor.jsx @@ -30,7 +30,12 @@ const TeamTypeNameMessage = { }; const GroupEditor = ({ - group, onDelete, onChange, onBlur, fieldNameCommonBase, errors, + group, + onDelete, + onChange, + onBlur, + fieldNameCommonBase, + errors, }) => { const intl = useIntl(); const [isDeleting, setDeleting] = useState(false); @@ -69,20 +74,21 @@ const GroupEditor = ({ deleteAlt={intl.formatMessage(messages.deleteAlt)} expandAlt={intl.formatMessage(messages.expandAlt)} collapseAlt={intl.formatMessage(messages.collapseAlt)} - title={ - isOpen - ? ( -
- {intl.formatMessage(messages.configureGroup)} + title={isOpen + ? ( +
+ {intl.formatMessage(messages.configureGroup)} +
+ ) : + ( +
+
+ {intl.formatMessage(TeamTypeNameMessage[group.type ? group.type : GroupTypes.OPEN].label)}
- ) : ( -
-
{intl.formatMessage(TeamTypeNameMessage[group.type ? group.type : GroupTypes.OPEN].label)}
-
{group.name}
-
{group.description}
-
- ) - } +
{group.name}
+
{group.description}
+
+ )} > - {Object.values(GroupTypes).map(groupType => isGroupTypeEnabled(groupType) && ( - - {intl.formatMessage(TeamTypeNameMessage[groupType].label)} - - ))} + {Object.values(GroupTypes).map(groupType => + isGroupTypeEnabled(groupType) && ( + + {intl.formatMessage(TeamTypeNameMessage[groupType].label)} + + ) + )} { errors: {}, }; - const renderComponent = (overrideProps = {}) => render( - - - , - ); + const renderComponent = (overrideProps = {}) => + render( + + + , + ); beforeEach(() => { useFormikContext.mockReturnValue({ diff --git a/plugins/course-apps/teams/Settings.jsx b/plugins/course-apps/teams/Settings.jsx index a81a19a64f..038cf6fb7b 100644 --- a/plugins/course-apps/teams/Settings.jsx +++ b/plugins/course-apps/teams/Settings.jsx @@ -116,52 +116,53 @@ const TeamSettings = ({ onSettingsSave={handleSettingsSave} configureBeforeEnable > - { - ({ - handleChange, handleBlur, values, errors, - }) => ( - <> -

{intl.formatMessage(messages.teamSize)}

- -
-

{intl.formatMessage(messages.groups)}

- {intl.formatMessage(messages.groupsHelp)} - - {({ push, remove }) => ( - <> - {values.groups?.map((group, index) => ( - remove(index)} - onChange={handleChange} - onBlur={handleBlur} - /> - ))} - - - )} - -
- - ) - } + {({ + handleChange, + handleBlur, + values, + errors, + }) => ( + <> +

{intl.formatMessage(messages.teamSize)}

+ +
+

{intl.formatMessage(messages.groups)}

+ {intl.formatMessage(messages.groupsHelp)} + + {({ push, remove }) => ( + <> + {values.groups?.map((group, index) => ( + remove(index)} + onChange={handleChange} + onBlur={handleBlur} + /> + ))} + + + )} + +
+ + )} ); }; diff --git a/plugins/course-apps/teams/package.json b/plugins/course-apps/teams/package.json index 9b377a1763..9975cd8960 100644 --- a/plugins/course-apps/teams/package.json +++ b/plugins/course-apps/teams/package.json @@ -1,20 +1,20 @@ { - "name": "@openedx-plugins/course-app-teams", - "version": "0.1.0", - "description": "Teams configuration for courses using it", - "peerDependencies": { - "@edx/frontend-app-authoring": "*", - "@edx/frontend-platform": "*", - "@openedx/paragon": "*", - "formik": "*", - "prop-types": "*", - "react": "*", - "uuid": "*", - "yup": "*" - }, - "peerDependenciesMeta": { - "@edx/frontend-app-authoring": { - "optional": true - } + "name": "@openedx-plugins/course-app-teams", + "version": "0.1.0", + "description": "Teams configuration for courses using it", + "peerDependencies": { + "@edx/frontend-app-authoring": "*", + "@edx/frontend-platform": "*", + "@openedx/paragon": "*", + "formik": "*", + "prop-types": "*", + "react": "*", + "uuid": "*", + "yup": "*" + }, + "peerDependenciesMeta": { + "@edx/frontend-app-authoring": { + "optional": true } + } } diff --git a/plugins/course-apps/wiki/Settings.jsx b/plugins/course-apps/wiki/Settings.jsx index c1ede73440..ffd62cbf35 100644 --- a/plugins/course-apps/wiki/Settings.jsx +++ b/plugins/course-apps/wiki/Settings.jsx @@ -25,19 +25,17 @@ const WikiSettings = ({ onClose }) => { validationSchema={{ enablePublicWiki: Yup.boolean() }} onSettingsSave={handleSettingsSave} > - { - ({ values, handleChange, handleBlur }) => ( - - ) - } + {({ values, handleChange, handleBlur }) => ( + + )} ); }; diff --git a/plugins/course-apps/wiki/package.json b/plugins/course-apps/wiki/package.json index b7d92a69bb..856aa8598a 100644 --- a/plugins/course-apps/wiki/package.json +++ b/plugins/course-apps/wiki/package.json @@ -1,18 +1,18 @@ { - "name": "@openedx-plugins/course-app-wiki", - "version": "0.1.0", - "description": "Wiki configuration for courses using it", - "peerDependencies": { - "@edx/frontend-app-authoring": "*", - "@edx/frontend-platform": "*", - "@openedx/paragon": "*", - "prop-types": "*", - "react": "*", - "yup": "*" - }, - "peerDependenciesMeta": { - "@edx/frontend-app-authoring": { - "optional": true - } + "name": "@openedx-plugins/course-app-wiki", + "version": "0.1.0", + "description": "Wiki configuration for courses using it", + "peerDependencies": { + "@edx/frontend-app-authoring": "*", + "@edx/frontend-platform": "*", + "@openedx/paragon": "*", + "prop-types": "*", + "react": "*", + "yup": "*" + }, + "peerDependenciesMeta": { + "@edx/frontend-app-authoring": { + "optional": true } + } } diff --git a/plugins/course-apps/xpert_unit_summary/Settings.test.jsx b/plugins/course-apps/xpert_unit_summary/Settings.test.jsx index b831f5ba19..d8ff859913 100644 --- a/plugins/course-apps/xpert_unit_summary/Settings.test.jsx +++ b/plugins/course-apps/xpert_unit_summary/Settings.test.jsx @@ -2,12 +2,19 @@ import ReactDOM from 'react-dom'; import React from 'react'; import { MemoryRouter, Routes, Route } from 'react-router-dom'; import { - getConfig, initializeMockApp, setConfig, + getConfig, + initializeMockApp, + setConfig, } from '@edx/frontend-platform'; import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { AppProvider, PageWrap } from '@edx/frontend-platform/react'; import { - findByTestId, queryByTestId, render, waitFor, getByText, fireEvent, + findByTestId, + queryByTestId, + render, + waitFor, + getByText, + fireEvent, } from '@testing-library/react'; import MockAdapter from 'axios-mock-adapter'; import PagesAndResourcesProvider from 'CourseAuthoring/pages-and-resources/PagesAndResourcesProvider'; @@ -34,11 +41,19 @@ function renderComponent() { } + element={ + + + + } />
} + element={ + +
+ + } /> @@ -49,11 +64,13 @@ function renderComponent() { } function generateCourseLevelAPIResponse({ - success, enabled, + success, + enabled, }) { return { response: { - success, enabled, + success, + enabled, }, }; } @@ -97,10 +114,13 @@ describe('XpertUnitSummarySettings', () => { describe('with successful network connections', () => { beforeEach(() => { axiosMock.onGet(API.getXpertSettingsUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: true, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: true, + }), + ); renderComponent(); }); @@ -113,10 +133,13 @@ describe('XpertUnitSummarySettings', () => { test('Shows switch on if disabled from backend', async () => { axiosMock.onGet(API.getXpertSettingsUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: false, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: false, + }), + ); renderComponent(); await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy()); @@ -131,10 +154,13 @@ describe('XpertUnitSummarySettings', () => { test('Shows disable radio selected if enabled from backend', async () => { axiosMock.onGet(API.getXpertSettingsUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: false, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: false, + }), + ); renderComponent(); await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy()); @@ -145,10 +171,13 @@ describe('XpertUnitSummarySettings', () => { describe('first time course configuration', () => { beforeEach(() => { axiosMock.onGet(API.getXpertSettingsUrl(courseId)) - .reply(400, generateCourseLevelAPIResponse({ - success: false, - enabled: undefined, - })); + .reply( + 400, + generateCourseLevelAPIResponse({ + success: false, + enabled: undefined, + }), + ); renderComponent(); }); @@ -163,16 +192,22 @@ describe('XpertUnitSummarySettings', () => { describe('saving configuration changes', () => { beforeEach(() => { axiosMock.onGet(API.getXpertSettingsUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: false, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: false, + }), + ); axiosMock.onPost(API.getXpertSettingsUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: true, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: true, + }), + ); renderComponent(); }); @@ -192,10 +227,13 @@ describe('XpertUnitSummarySettings', () => { describe('testing configurable gating', () => { beforeEach(async () => { axiosMock.onGet(API.getXpertConfigurationStatusUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: true, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: true, + }), + ); jest.spyOn(API, 'getXpertPluginConfigurable'); await executeThunk(Thunks.fetchXpertPluginConfigurable(courseId), store.dispatch); renderComponent(); @@ -209,16 +247,22 @@ describe('XpertUnitSummarySettings', () => { describe('removing course configuration', () => { beforeEach(() => { axiosMock.onGet(API.getXpertSettingsUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: true, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: true, + }), + ); axiosMock.onDelete(API.getXpertSettingsUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: undefined, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: undefined, + }), + ); renderComponent(); }); @@ -237,16 +281,22 @@ describe('XpertUnitSummarySettings', () => { describe('resetting course units', () => { test('reset all units to be enabled', async () => { axiosMock.onGet(API.getXpertSettingsUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: true, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: true, + }), + ); axiosMock.onPost(API.getXpertSettingsUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: true, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: true, + }), + ); renderComponent(); @@ -259,16 +309,22 @@ describe('XpertUnitSummarySettings', () => { test('reset all units to be disabled', async () => { axiosMock.onGet(API.getXpertSettingsUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: false, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: false, + }), + ); axiosMock.onPost(API.getXpertSettingsUrl(courseId)) - .reply(200, generateCourseLevelAPIResponse({ - success: true, - enabled: false, - })); + .reply( + 200, + generateCourseLevelAPIResponse({ + success: true, + enabled: false, + }), + ); renderComponent(); diff --git a/plugins/course-apps/xpert_unit_summary/data/thunks.js b/plugins/course-apps/xpert_unit_summary/data/thunks.js index eeaa4ee0b1..ec0bd91ff5 100644 --- a/plugins/course-apps/xpert_unit_summary/data/thunks.js +++ b/plugins/course-apps/xpert_unit_summary/data/thunks.js @@ -1,9 +1,16 @@ -import { updateSavingStatus, updateLoadingStatus, updateResetStatus } from 'CourseAuthoring/pages-and-resources/data/slice'; +import { + updateSavingStatus, + updateLoadingStatus, + updateResetStatus, +} from 'CourseAuthoring/pages-and-resources/data/slice'; import { RequestStatus } from 'CourseAuthoring/data/constants'; import { addModel, updateModel } from 'CourseAuthoring/generic/model-store'; import { - getXpertSettings, postXpertSettings, getXpertPluginConfigurable, deleteXpertSettings, + getXpertSettings, + postXpertSettings, + getXpertPluginConfigurable, + deleteXpertSettings, } from './api'; export function updateXpertSettings(courseId, state) { @@ -13,7 +20,9 @@ export function updateXpertSettings(courseId, state) { const { response } = await postXpertSettings(courseId, state); const { success } = response; if (success) { - dispatch(updateModel({ modelType: 'XpertSettings', model: { id: 'xpert-unit-summary', enabled: state.enabled } })); + dispatch( + updateModel({ modelType: 'XpertSettings', model: { id: 'xpert-unit-summary', enabled: state.enabled } }), + ); dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL })); return true; } diff --git a/plugins/course-apps/xpert_unit_summary/package.json b/plugins/course-apps/xpert_unit_summary/package.json index 26c0c7d5b9..121c25e2e3 100644 --- a/plugins/course-apps/xpert_unit_summary/package.json +++ b/plugins/course-apps/xpert_unit_summary/package.json @@ -1,21 +1,21 @@ { - "name": "@openedx-plugins/course-app-xpert_unit_summary", - "version": "0.1.0", - "description": "Xpert Unit Summaries configuration for courses using it", - "peerDependencies": { - "@edx/frontend-app-authoring": "*", - "@edx/frontend-platform": "*", - "@openedx/paragon": "*", - "formik": "*", - "prop-types": "*", - "yup": "*", - "react": "*", - "react-redux": "*", - "react-router-dom": "*" - }, - "peerDependenciesMeta": { - "@edx/frontend-app-authoring": { - "optional": true - } + "name": "@openedx-plugins/course-app-xpert_unit_summary", + "version": "0.1.0", + "description": "Xpert Unit Summaries configuration for courses using it", + "peerDependencies": { + "@edx/frontend-app-authoring": "*", + "@edx/frontend-platform": "*", + "@openedx/paragon": "*", + "formik": "*", + "prop-types": "*", + "yup": "*", + "react": "*", + "react-redux": "*", + "react-router-dom": "*" + }, + "peerDependenciesMeta": { + "@edx/frontend-app-authoring": { + "optional": true } + } } diff --git a/plugins/course-apps/xpert_unit_summary/settings-modal/SettingsModal.jsx b/plugins/course-apps/xpert_unit_summary/settings-modal/SettingsModal.jsx index f4c016b044..e06846dd46 100644 --- a/plugins/course-apps/xpert_unit_summary/settings-modal/SettingsModal.jsx +++ b/plugins/course-apps/xpert_unit_summary/settings-modal/SettingsModal.jsx @@ -14,13 +14,18 @@ import { Hyperlink, } from '@openedx/paragon'; import { - Info, CheckCircleOutline, SpinnerSimple, + Info, + CheckCircleOutline, + SpinnerSimple, } from '@openedx/paragon/icons'; import { Formik } from 'formik'; import PropTypes from 'prop-types'; import React, { - useContext, useEffect, useRef, useState, + useContext, + useEffect, + useRef, + useState, } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import * as Yup from 'yup'; @@ -45,18 +50,21 @@ import ResetIcon from './ResetIcon'; import './SettingsModal.scss'; const AppSettingsForm = ({ - formikProps, children, showForm, -}) => children && ( - - {showForm ? ( - - {children(formikProps)} - - ) : ( - - )} - -); + formikProps, + children, + showForm, +}) => + children && ( + + {showForm ? + ( + + {children(formikProps)} + + ) : + } + + ); AppSettingsForm.propTypes = { // Ignore the warning here since we're just passing along the props as-is and the child component should validate @@ -71,7 +79,12 @@ AppSettingsForm.defaultProps = { }; const SettingsModalBase = ({ - title, onClose, variant, isMobile, children, footer, + title, + onClose, + variant, + isMobile, + children, + footer, }) => { const intl = useIntl(); return ( @@ -156,14 +169,14 @@ const ResetUnitsButton = ({ return ( {intl.formatMessage(messages[messageKey])} - )} + } > @@ -317,7 +328,7 @@ const SettingsModal = ({ isMobile={isMobile} isFullscreenOnMobile intl={intl} - footer={( + footer={ - )} + } > {saveError && ( @@ -344,7 +355,7 @@ const SettingsModal = ({ onChange={formikProps.handleChange} onBlur={formikProps.handleBlur} checked={formikProps.values.enabled} - label={( + label={
{enableAppLabel} {formikProps.values.enabled && ( @@ -353,14 +364,14 @@ const SettingsModal = ({ )}
- )} - helpText={( + } + helpText={

{enableAppHelp}

{helpPrivacyLink} {learnMoreLink}
- )} + } /> {(formikProps.values.enabled || configureBeforeEnable) && ( (undefined); @@ -122,7 +123,9 @@ export function useCourseAuthoringContext(): CourseAuthoringContextData { const ctx = useContext(CourseAuthoringContext); if (ctx === undefined) { /* istanbul ignore next */ - throw new Error('useCourseAuthoringContext() was used in a component without a ancestor.'); + throw new Error( + 'useCourseAuthoringContext() was used in a component without a ancestor.', + ); } return ctx; } diff --git a/src/CourseAuthoringPage.test.tsx b/src/CourseAuthoringPage.test.tsx index a4ef4fc7ed..d7339a283f 100644 --- a/src/CourseAuthoringPage.test.tsx +++ b/src/CourseAuthoringPage.test.tsx @@ -19,11 +19,12 @@ jest.mock('react-router-dom', () => ({ let axiosMock; let store; -const renderComponent = children => render( - - {children} - , -); +const renderComponent = children => + render( + + {children} + , + ); beforeEach(async () => { const mocks = initializeMocks(); @@ -48,8 +49,7 @@ describe('Editor Pages Load no header', () => { const wrapper = renderComponent( - - , + , ); expect(wrapper.queryByRole('status')).not.toBeInTheDocument(); }); @@ -59,8 +59,7 @@ describe('Editor Pages Load no header', () => { const wrapper = renderComponent( - - , + , ); expect(wrapper.queryByRole('status')).toBeInTheDocument(); }); @@ -98,8 +97,7 @@ describe('Course authoring page', () => { const wrapper = renderComponent(
- - , + , ); expect(await wrapper.findByTestId(contentTestId)).toBeInTheDocument(); expect(wrapper.queryByTestId('notFoundAlert')).not.toBeInTheDocument(); diff --git a/src/CourseAuthoringPage.tsx b/src/CourseAuthoringPage.tsx index e8b328d483..7e6d4055bd 100644 --- a/src/CourseAuthoringPage.tsx +++ b/src/CourseAuthoringPage.tsx @@ -35,22 +35,21 @@ const CourseAuthoringPage = ({ children }: Props) => { const isEditor = pathname.includes('/editor'); if (courseDetailStatus === RequestStatus.NOT_FOUND && !isEditor) { - return ( - - ); + return ; } if (courseAppsApiStatus === RequestStatus.DENIED) { - return ( - - ); + return ; } return (
- {/* While V2 Editors are temporarily served from their own pages + { + /* While V2 Editors are temporarily served from their own pages using url pattern containing /editor/, we shouldn't have the header and footer on these pages. - This functionality will be removed in TNL-9591 */} - {inProgress ? !isEditor && + This functionality will be removed in TNL-9591 */ + } + {inProgress ? + !isEditor && : (!isEditor && (
{ size: 'fluid', }} /> - ) - )} + ))} {children} {!inProgress && !isEditor && }
diff --git a/src/CourseAuthoringRoutes.test.tsx b/src/CourseAuthoringRoutes.test.tsx index f7aabc829f..cece487f07 100644 --- a/src/CourseAuthoringRoutes.test.tsx +++ b/src/CourseAuthoringRoutes.test.tsx @@ -1,7 +1,10 @@ import CourseAuthoringRoutes from './CourseAuthoringRoutes'; import { getApiWaffleFlagsUrl } from './data/api'; import { - screen, initializeMocks, render, waitFor, + screen, + initializeMocks, + render, + waitFor, } from './testUtils'; const courseId = 'course-v1:edX+TestX+Test_Course'; diff --git a/src/CourseAuthoringRoutes.tsx b/src/CourseAuthoringRoutes.tsx index 6ca81f930d..d4b9eb3bbd 100644 --- a/src/CourseAuthoringRoutes.tsx +++ b/src/CourseAuthoringRoutes.tsx @@ -1,5 +1,8 @@ import { - Navigate, Routes, Route, useParams, + Navigate, + Routes, + Route, + useParams, } from 'react-router-dom'; import { getConfig } from '@edx/frontend-platform'; import { PageWrap } from '@edx/frontend-platform/react'; @@ -65,7 +68,7 @@ const CourseAuthoringRoutes = () => { @@ -75,27 +78,49 @@ const CourseAuthoringRoutes = () => { - )} + } /> } + element={ + + + + } /> } + element={ + + + + } /> } + element={ + + + + } /> : null} + element={getConfig().ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN === 'true' + ? ( + + + + ) + : null} /> } + element={ + + + + } /> { /> } + element={ + + + + } /> } + element={ + + + + } /> {DECODED_ROUTES.COURSE_UNIT.map((path) => ( } + element={ + + + + + + } /> ))} } + element={ + + + + } /> } + element={ + + + + } /> } + element={ + + + + } /> } + element={ + + + + } /> } + element={ + + + + } /> } + element={ + + + + } /> } + element={ + + + + } /> - )} + } /> - )} + } /> } + element={ + + + + } /> } + element={ + + + + } /> : null} + element={getConfig().ENABLE_CERTIFICATE_PAGE === 'true' + ? ( + + + + ) + : null} /> } + element={ + + + + } /> diff --git a/src/__mocks__/clipboardSection.ts b/src/__mocks__/clipboardSection.ts index b6a1da4ea1..ba9d8cc0f2 100644 --- a/src/__mocks__/clipboardSection.ts +++ b/src/__mocks__/clipboardSection.ts @@ -12,5 +12,6 @@ export default { }, sourceUsageKey: 'block-v1:edX+DemoX+Demo_Course+type@chapter+block@chapter_0270f6de40fc', sourceContextTitle: 'Demonstration Course', - sourceEditUrl: 'http://localhost:18010/container/block-v1:edX+DemoX+Demo_Course+type@chapter+block@chapter_0270f6de40fc', + sourceEditUrl: + 'http://localhost:18010/container/block-v1:edX+DemoX+Demo_Course+type@chapter+block@chapter_0270f6de40fc', }; diff --git a/src/__mocks__/clipboardSubsection.ts b/src/__mocks__/clipboardSubsection.ts index 541e95f979..49fca1495a 100644 --- a/src/__mocks__/clipboardSubsection.ts +++ b/src/__mocks__/clipboardSubsection.ts @@ -12,5 +12,6 @@ export default { }, sourceUsageKey: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@sequential_0270f6de40fc', sourceContextTitle: 'Demonstration Course', - sourceEditUrl: 'http://localhost:18010/container/block-v1:edX+DemoX+Demo_Course+type@sequential+block@sequential_0270f6de40fc', + sourceEditUrl: + 'http://localhost:18010/container/block-v1:edX+DemoX+Demo_Course+type@sequential+block@sequential_0270f6de40fc', }; diff --git a/src/__mocks__/clipboardUnit.ts b/src/__mocks__/clipboardUnit.ts index fb20bde413..e71f4a1cf1 100644 --- a/src/__mocks__/clipboardUnit.ts +++ b/src/__mocks__/clipboardUnit.ts @@ -12,5 +12,6 @@ export default { }, sourceUsageKey: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_0270f6de40fc', sourceContextTitle: 'Demonstration Course', - sourceEditUrl: 'http://localhost:18010/container/block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_0270f6de40fc', + sourceEditUrl: + 'http://localhost:18010/container/block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_0270f6de40fc', }; diff --git a/src/__mocks__/index.ts b/src/__mocks__/index.ts index 14c73faa93..44e19f2e6d 100644 --- a/src/__mocks__/index.ts +++ b/src/__mocks__/index.ts @@ -1,4 +1,4 @@ -export { default as clipboardUnit } from './clipboardUnit'; +export { default as clipboardSection } from './clipboardSection'; export { default as clipboardSubsection } from './clipboardSubsection'; +export { default as clipboardUnit } from './clipboardUnit'; export { default as clipboardXBlock } from './clipboardXBlock'; -export { default as clipboardSection } from './clipboardSection'; diff --git a/src/accessibility-page/AccessibilityBody/AccessibilityBody.tsx b/src/accessibility-page/AccessibilityBody/AccessibilityBody.tsx index 4991a8b387..1277807e08 100644 --- a/src/accessibility-page/AccessibilityBody/AccessibilityBody.tsx +++ b/src/accessibility-page/AccessibilityBody/AccessibilityBody.tsx @@ -7,8 +7,8 @@ const AccessibilityBody = ({ communityAccessibilityLink, email, }: { - communityAccessibilityLink: string, - email: string, + communityAccessibilityLink: string; + email: string; }) => (
diff --git a/src/accessibility-page/AccessibilityForm/AccessibilityForm.test.tsx b/src/accessibility-page/AccessibilityForm/AccessibilityForm.test.tsx index e20efedae8..2afae32a84 100644 --- a/src/accessibility-page/AccessibilityForm/AccessibilityForm.test.tsx +++ b/src/accessibility-page/AccessibilityForm/AccessibilityForm.test.tsx @@ -63,9 +63,10 @@ describe('', () => { it('renders in progress state', async () => { axiosMock.onPost(getZendeskrUrl()).reply( - () => new Promise(() => { - // always in pending - }), + () => + new Promise(() => { + // always in pending + }), ); await user.click(submitButton); diff --git a/src/accessibility-page/AccessibilityForm/AccessibilityForm.tsx b/src/accessibility-page/AccessibilityForm/AccessibilityForm.tsx index f12502d7ba..7053f3cfa2 100644 --- a/src/accessibility-page/AccessibilityForm/AccessibilityForm.tsx +++ b/src/accessibility-page/AccessibilityForm/AccessibilityForm.tsx @@ -1,15 +1,22 @@ import { - FormattedMessage, FormattedDate, FormattedTime, useIntl, + FormattedMessage, + FormattedDate, + FormattedTime, + useIntl, } from '@edx/frontend-platform/i18n'; import { - ActionRow, Alert, Form, Stack, StatefulButton, + ActionRow, + Alert, + Form, + Stack, + StatefulButton, } from '@openedx/paragon'; import { STATEFUL_BUTTON_STATES } from '@src/constants'; import useAccessibility from './hooks'; import messages from './messages'; -const AccessibilityForm = ({ accessibilityEmail }: { accessibilityEmail: string }) => { +const AccessibilityForm = ({ accessibilityEmail }: { accessibilityEmail: string; }) => { const intl = useIntl(); const { errors, @@ -70,10 +77,10 @@ const AccessibilityForm = ({ accessibilityEmail }: { accessibilityEmail: string ), - time_start: (), - day_end: (), - time_end: (), + day_start: , + time_start: , + day_end: , + time_end: , }} />
@@ -118,11 +125,9 @@ const AccessibilityForm = ({ accessibilityEmail }: { accessibilityEmail: string key="save-button" onClick={handleSubmit} disabled={!isFormFilled} - state={ - savingStatus === 'pending' - ? STATEFUL_BUTTON_STATES.pending - : STATEFUL_BUTTON_STATES.default - } + state={savingStatus === 'pending' + ? STATEFUL_BUTTON_STATES.pending + : STATEFUL_BUTTON_STATES.default} {...createButtonState} /> diff --git a/src/accessibility-page/AccessibilityForm/hooks.ts b/src/accessibility-page/AccessibilityForm/hooks.ts index 6d9855a1e2..7b6281dc6d 100644 --- a/src/accessibility-page/AccessibilityForm/hooks.ts +++ b/src/accessibility-page/AccessibilityForm/hooks.ts @@ -23,7 +23,12 @@ const useAccessibility = (initialValues: AccessibilityFormData) => { }); const { - values, errors, touched, handleChange, handleBlur, handleReset, + values, + errors, + touched, + handleChange, + handleBlur, + handleReset, } = useFormik({ initialValues, enableReinitialize: true, diff --git a/src/advanced-settings/AdvancedSettings.test.tsx b/src/advanced-settings/AdvancedSettings.test.tsx index 2d24fb2b7a..8b9d73aa70 100644 --- a/src/advanced-settings/AdvancedSettings.test.tsx +++ b/src/advanced-settings/AdvancedSettings.test.tsx @@ -18,23 +18,25 @@ const mockPathname = '/foo-bar'; const courseId = '123'; // Mock the TextareaAutosize component -jest.mock('react-textarea-autosize', () => jest.fn((props) => ( -