From 688bdb7ec5e64814d13514d14a0b74dfba2247db Mon Sep 17 00:00:00 2001
From: Leangseu Kim <83240113+leangseu-edx@users.noreply.github.com>
Date: Wed, 17 Apr 2024 09:43:39 -0400
Subject: [PATCH 1/3] chore: add @openedx/frontend-plugin-framework
chore: move plugin page setting button to a props
chore: split out app setting modal for reusability
chore: add implementation of WTC plugin
chore: update app setting form
chore: implement the plugin form with mock
chore: follow the UI design
chore: remove translation plugin and move it into frontend-plugin instead
---
.gitignore | 3 +
example.env.config.jsx | 24 ++
package-lock.json | 292 +++++++++++++-----
package.json | 6 +-
.../app-settings-modal/AppSettingsModal.jsx | 112 ++-----
.../AppSettingsModalBase.jsx | 63 ++++
src/pages-and-resources/pages/PageCard.jsx | 60 ++--
src/pages-and-resources/pages/PageGrid.jsx | 2 +
.../pages/PageSettingButton.jsx | 59 ++++
9 files changed, 418 insertions(+), 203 deletions(-)
create mode 100644 example.env.config.jsx
create mode 100644 src/pages-and-resources/app-settings-modal/AppSettingsModalBase.jsx
create mode 100644 src/pages-and-resources/pages/PageSettingButton.jsx
diff --git a/.gitignore b/.gitignore
index 02542e96c3..925d1768a4 100755
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,6 @@ temp/babel-plugin-react-intl
# Messages .json files fetched by atlas
src/i18n/messages/
+
+# environment js config
+env.config.jsx
diff --git a/example.env.config.jsx b/example.env.config.jsx
new file mode 100644
index 0000000000..e57c7d7668
--- /dev/null
+++ b/example.env.config.jsx
@@ -0,0 +1,24 @@
+import WholeCourseTranslation from '@edx/course-app-translation-plugin';
+import { PLUGIN_OPERATIONS, DIRECT_PLUGIN } from '@openedx/frontend-plugin-framework';
+
+// Load environment variables from .env file
+const config = {
+ ...process.env,
+ pluginSlots: {
+ additional_course_plugin: {
+ plugins: [
+ {
+ op: PLUGIN_OPERATIONS.Insert,
+ widget: {
+ id: 'whole-course-translation-plugin',
+ type: DIRECT_PLUGIN,
+ priority: 1,
+ RenderWidget: WholeCourseTranslation,
+ },
+ },
+ ],
+ },
+ },
+};
+
+export default config;
diff --git a/package-lock.json b/package-lock.json
index 062af01e6e..a6d3b3765e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -37,6 +37,7 @@
"@openedx-plugins/course-app-teams": "file:plugins/course-apps/teams",
"@openedx-plugins/course-app-wiki": "file:plugins/course-apps/wiki",
"@openedx-plugins/course-app-xpert_unit_summary": "file:plugins/course-apps/xpert_unit_summary",
+ "@openedx/frontend-plugin-framework": "^1.1.0",
"@openedx/paragon": "^22.2.1",
"@reduxjs/toolkit": "1.9.7",
"@tanstack/react-query": "4.36.1",
@@ -50,10 +51,11 @@
"lodash": "4.17.21",
"meilisearch": "^0.38.0",
"moment": "2.29.4",
- "prop-types": "15.7.2",
+ "prop-types": "^15.8.1",
"react": "17.0.2",
"react-datepicker": "^4.13.0",
"react-dom": "17.0.2",
+ "react-error-boundary": "^4.0.13",
"react-helmet": "^6.1.0",
"react-redux": "7.2.9",
"react-responsive": "9.0.2",
@@ -2377,16 +2379,6 @@
"react-dom": "^16.9.0 || ^17.0.0"
}
},
- "node_modules/@edx/frontend-component-ai-translations/node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
"node_modules/@edx/frontend-component-ai-translations/node_modules/react-responsive": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
@@ -3495,16 +3487,6 @@
"react": ">=16.3"
}
},
- "node_modules/@fortawesome/react-fontawesome/node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
"node_modules/@fullhuman/postcss-purgecss": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-5.0.0.tgz",
@@ -4705,6 +4687,174 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@openedx/frontend-plugin-framework": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@openedx/frontend-plugin-framework/-/frontend-plugin-framework-1.1.0.tgz",
+ "integrity": "sha512-WT2Up9UR1ctQLeN43iCwEF6BOmU3zxL+s+clDeS4zp/aPue3v5ObTbzRsuJGlFg9gA6lY1C6Yh2+QQ6w2sK2aw==",
+ "dependencies": {
+ "@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
+ "@edx/frontend-component-footer": "13.0.3",
+ "@edx/frontend-component-header": "5.0.2",
+ "classnames": "^2.3.2",
+ "core-js": "3.36.0",
+ "react-redux": "7.2.9",
+ "redux": "4.2.1",
+ "regenerator-runtime": "0.14.1"
+ },
+ "peerDependencies": {
+ "@edx/frontend-platform": "^7.0.0",
+ "@openedx/paragon": "^21.0.0 || ^22.0.0",
+ "prop-types": "^15.8.0",
+ "react": "^17.0.0",
+ "react-dom": "^17.0.0",
+ "react-error-boundary": "^4.0.11"
+ }
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/@edx/frontend-component-footer": {
+ "version": "13.0.3",
+ "resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-13.0.3.tgz",
+ "integrity": "sha512-09vX6qC7AcDwG02qhBzKr4x58hpe9FXZrA9ui2cJnsG53pKaNL+wvOSRtDUBNexCf+y/iPg+8RgR+4alkzhZhw==",
+ "dependencies": {
+ "@fortawesome/fontawesome-svg-core": "6.5.1",
+ "@fortawesome/free-brands-svg-icons": "6.5.1",
+ "@fortawesome/free-regular-svg-icons": "6.5.1",
+ "@fortawesome/free-solid-svg-icons": "6.5.1",
+ "@fortawesome/react-fontawesome": "0.2.0",
+ "lodash": "^4.17.21"
+ },
+ "peerDependencies": {
+ "@edx/frontend-platform": "^7.0.0",
+ "@openedx/paragon": ">= 21.11.3 < 23.0.0",
+ "prop-types": "^15.5.10",
+ "react": "^16.9.0 || ^17.0.0",
+ "react-dom": "^16.9.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/@edx/frontend-component-header": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-5.0.2.tgz",
+ "integrity": "sha512-73fNNc1X/tevb3/hw7+s22T+nPGlW1yXA7zsT9eRzdH7rBxONfp0Jz7yEdeBvTax9a96PaOht45DA6GX9eG4KA==",
+ "dependencies": {
+ "@fortawesome/fontawesome-svg-core": "6.5.1",
+ "@fortawesome/free-brands-svg-icons": "6.5.1",
+ "@fortawesome/free-regular-svg-icons": "6.5.1",
+ "@fortawesome/free-solid-svg-icons": "6.5.1",
+ "@fortawesome/react-fontawesome": "^0.2.0",
+ "axios-mock-adapter": "1.22.0",
+ "babel-polyfill": "6.26.0",
+ "react-responsive": "8.2.0",
+ "react-transition-group": "4.4.5"
+ },
+ "peerDependencies": {
+ "@edx/frontend-platform": "^7.0.0",
+ "@openedx/paragon": ">= 21.5.7 < 23.0.0",
+ "prop-types": "^15.5.10",
+ "react": "^16.9.0 || ^17.0.0",
+ "react-dom": "^16.9.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz",
+ "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==",
+ "hasInstallScript": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/fontawesome-svg-core": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz",
+ "integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/free-brands-svg-icons": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.1.tgz",
+ "integrity": "sha512-093l7DAkx0aEtBq66Sf19MgoZewv1zeY9/4C7vSKPO4qMwEsW/2VYTUTpBtLwfb9T2R73tXaRDPmE4UqLCYHfg==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/free-regular-svg-icons": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz",
+ "integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/free-solid-svg-icons": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz",
+ "integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/classnames": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/core-js": {
+ "version": "3.36.0",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.0.tgz",
+ "integrity": "sha512-mt7+TUBbTFg5+GngsAxeKBTl5/VS0guFeJacYge9OmHb+m058UwwIm41SE9T4Den7ClatV57B6TYTuJ0CX1MAw==",
+ "hasInstallScript": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/react-responsive": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
+ "integrity": "sha512-iagCqVrw4QSjhxKp3I/YK6+ODkWY6G+YPElvdYKiUUbywwh9Ds0M7r26Fj2/7dWFFbOpcGnJE6uE7aMck8j5Qg==",
+ "dependencies": {
+ "hyphenate-style-name": "^1.0.0",
+ "matchmediaquery": "^0.3.0",
+ "prop-types": "^15.6.1",
+ "shallow-equal": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/redux": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
+ "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
+ "dependencies": {
+ "@babel/runtime": "^7.9.2"
+ }
+ },
+ "node_modules/@openedx/frontend-plugin-framework/node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+ },
"node_modules/@openedx/paragon": {
"version": "22.2.1",
"resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.2.1.tgz",
@@ -4824,16 +4974,6 @@
"node": ">=10"
}
},
- "node_modules/@openedx/paragon/node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
"node_modules/@openedx/paragon/node_modules/react-responsive": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
@@ -5386,6 +5526,22 @@
}
}
},
+ "node_modules/@testing-library/react-hooks/node_modules/react-error-boundary": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
+ "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.12.5"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ },
+ "peerDependencies": {
+ "react": ">=16.13.1"
+ }
+ },
"node_modules/@testing-library/react/node_modules/@testing-library/dom": {
"version": "8.20.1",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz",
@@ -10062,16 +10218,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/eslint-plugin-react/node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
"node_modules/eslint-plugin-react/node_modules/resolve": {
"version": "2.0.0-next.5",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
@@ -17422,13 +17568,13 @@
}
},
"node_modules/prop-types": {
- "version": "15.7.2",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
- "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
- "react-is": "^16.8.1"
+ "react-is": "^16.13.1"
}
},
"node_modules/prop-types-extra": {
@@ -17878,28 +18024,13 @@
"react": ">= 16.8 || 18.0.0"
}
},
- "node_modules/react-dropzone/node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
"node_modules/react-error-boundary": {
- "version": "3.1.4",
- "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
- "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
- "dev": true,
+ "version": "4.0.13",
+ "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz",
+ "integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==",
"dependencies": {
"@babel/runtime": "^7.12.5"
},
- "engines": {
- "node": ">=10",
- "npm": ">=6"
- },
"peerDependencies": {
"react": ">=16.13.1"
}
@@ -17996,14 +18127,35 @@
"react": ">=0.14.0"
}
},
- "node_modules/react-imask/node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "node_modules/react-instantsearch": {
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/react-instantsearch/-/react-instantsearch-7.7.1.tgz",
+ "integrity": "sha512-o6nLY4IZWql6m0LYFSKpPKlAZ8zV3fwnwgswGs1okdw2skb3TXB535/mQCQZF39YjrUqBc3thl/YMnEDnKtVaQ==",
"dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
+ "@babel/runtime": "^7.1.2",
+ "instantsearch-ui-components": "0.4.0",
+ "instantsearch.js": "4.66.1",
+ "react-instantsearch-core": "7.7.1"
+ },
+ "peerDependencies": {
+ "algoliasearch": ">= 3.1 < 5",
+ "react": ">= 16.8.0 < 19",
+ "react-dom": ">= 16.8.0 < 19"
+ }
+ },
+ "node_modules/react-instantsearch-core": {
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/react-instantsearch-core/-/react-instantsearch-core-7.7.1.tgz",
+ "integrity": "sha512-OTvf/QtJT5zd+EQW+osjPPFNr7Vo9FAzy/zUxeeP+87IS6tiUpQQEDhgFFYBbvU5+97pYl9YmvGQARakNDHJOw==",
+ "dependencies": {
+ "@babel/runtime": "^7.1.2",
+ "algoliasearch-helper": "3.17.0",
+ "instantsearch.js": "4.66.1",
+ "use-sync-external-store": "^1.0.0"
+ },
+ "peerDependencies": {
+ "algoliasearch": ">= 3.1 < 5",
+ "react": ">= 16.8.0 < 19"
}
},
"node_modules/react-intl": {
diff --git a/package.json b/package.json
index 75fa7904e0..371b6885bb 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
"scripts": {
"build": "fedx-scripts webpack",
"i18n_extract": "fedx-scripts formatjs extract",
- "stylelint": "stylelint \"src/**/*.scss\" \"scss/**/*.scss\" --config .stylelintrc.json",
+ "stylelint": "stylelint \"plugins/**/*.scss\" \"src/**/*.scss\" \"scss/**/*.scss\" --config .stylelintrc.json",
"lint": "npm run stylelint && fedx-scripts eslint --ext .js --ext .jsx .",
"lint:fix": "npm run stylelint && fedx-scripts eslint --ext .js --ext .jsx . --fix",
"snapshot": "TZ=UTC fedx-scripts jest --updateSnapshot",
@@ -64,6 +64,7 @@
"@openedx-plugins/course-app-teams": "file:plugins/course-apps/teams",
"@openedx-plugins/course-app-wiki": "file:plugins/course-apps/wiki",
"@openedx-plugins/course-app-xpert_unit_summary": "file:plugins/course-apps/xpert_unit_summary",
+ "@openedx/frontend-plugin-framework": "^1.1.0",
"@openedx/paragon": "^22.2.1",
"@reduxjs/toolkit": "1.9.7",
"@tanstack/react-query": "4.36.1",
@@ -77,10 +78,11 @@
"lodash": "4.17.21",
"meilisearch": "^0.38.0",
"moment": "2.29.4",
- "prop-types": "15.7.2",
+ "prop-types": "^15.8.1",
"react": "17.0.2",
"react-datepicker": "^4.13.0",
"react-dom": "17.0.2",
+ "react-error-boundary": "^4.0.13",
"react-helmet": "^6.1.0",
"react-redux": "7.2.9",
"react-responsive": "9.0.2",
diff --git a/src/pages-and-resources/app-settings-modal/AppSettingsModal.jsx b/src/pages-and-resources/app-settings-modal/AppSettingsModal.jsx
index 477b269bb9..276ce4b100 100644
--- a/src/pages-and-resources/app-settings-modal/AppSettingsModal.jsx
+++ b/src/pages-and-resources/app-settings-modal/AppSettingsModal.jsx
@@ -1,11 +1,9 @@
-import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import { useIntl } from '@edx/frontend-platform/i18n';
import {
- ActionRow,
Alert,
Badge,
Form,
Hyperlink,
- ModalDialog,
StatefulButton,
TransitionReplace,
} from '@openedx/paragon';
@@ -31,81 +29,10 @@ import { updateSavingStatus } from '../data/slice';
import { updateAppStatus } from '../data/thunks';
import AppConfigFormDivider from '../discussions/app-config-form/apps/shared/AppConfigFormDivider';
import { PagesAndResourcesContext } from '../PagesAndResourcesProvider';
+import AppSettingsModalBase from './AppSettingsModalBase';
import messages from './messages';
-const AppSettingsForm = ({
- 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
- // eslint-disable-next-line react/forbid-prop-types
- formikProps: PropTypes.object.isRequired,
- showForm: PropTypes.bool.isRequired,
- children: PropTypes.func,
-};
-
-AppSettingsForm.defaultProps = {
- children: null,
-};
-
-const AppSettingsModalBase = ({
- intl, title, onClose, variant, isMobile, children, footer,
-}) => (
-
-
-
- {title}
-
-
-
- {children}
-
-
-
-
- {intl.formatMessage(messages.cancel)}
-
- {footer}
-
-
-
-);
-
-AppSettingsModalBase.propTypes = {
- intl: intlShape.isRequired,
- title: PropTypes.string.isRequired,
- onClose: PropTypes.func.isRequired,
- variant: PropTypes.oneOf(['default', 'dark']).isRequired,
- isMobile: PropTypes.bool.isRequired,
- children: PropTypes.node.isRequired,
- footer: PropTypes.node,
-};
-
-AppSettingsModalBase.defaultProps = {
- footer: null,
-};
-
const AppSettingsModal = ({
- intl,
appId,
title,
children,
@@ -121,6 +48,7 @@ const AppSettingsModal = ({
enableReinitialize,
hideAppToggle,
}) => {
+ const { formatMessage } = useIntl();
const { courseId } = useContext(PagesAndResourcesContext);
const loadingStatus = useSelector(getLoadingStatus);
const updateSettingsRequestStatus = useSelector(getSavingStatus);
@@ -199,14 +127,12 @@ const AppSettingsModal = ({
onClose={onClose}
variant={modalVariant}
isMobile={isMobile}
- isFullscreenOnMobile
- intl={intl}
footer={(
- {formikProps.errors.enabled?.title || intl.formatMessage(messages.errorSavingTitle)}
+ {formikProps.errors.enabled?.title || formatMessage(messages.errorSavingTitle)}
- {formikProps.errors.enabled?.message || intl.formatMessage(messages.errorSavingMessage)}
+ {formikProps.errors.enabled?.message || formatMessage(messages.errorSavingMessage)}
)}
{!hideAppToggle && (
@@ -233,7 +159,7 @@ const AppSettingsModal = ({
{enableAppLabel}
{formikProps.values.enabled && (
- {intl.formatMessage(messages.enabled)}
+ {formatMessage(messages.enabled)}
)}
@@ -249,9 +175,19 @@ const AppSettingsModal = ({
{bodyChildren}
{(formikProps.values.enabled || configureBeforeEnable) && children
&& }
-
- {children}
-
+ {
+ children && (
+
+ {formikProps.values.enabled || configureBeforeEnable ? (
+
+ {children(formikProps)}
+
+ ) : (
+
+ )}
+
+ )
+ }
)}
@@ -260,7 +196,6 @@ const AppSettingsModal = ({
}
return (
{
+ const { formatMessage } = useIntl();
+ return (
+
+
+ {title}
+
+ {children}
+
+
+
+ {formatMessage(messages.cancel)}
+
+ {footer}
+
+
+
+ );
+};
+
+AppSettingsModalBase.defaultProps = {
+ isOpen: true,
+};
+
+AppSettingsModalBase.propTypes = {
+ title: PropTypes.string.isRequired,
+ onClose: PropTypes.func.isRequired,
+ variant: PropTypes.oneOf(['default', 'dark']).isRequired,
+ isMobile: PropTypes.bool.isRequired,
+ children: PropTypes.node.isRequired,
+ footer: PropTypes.node,
+ isOpen: PropTypes.bool,
+};
+
+AppSettingsModalBase.defaultProps = {
+ footer: null,
+};
+
+export default AppSettingsModalBase;
diff --git a/src/pages-and-resources/pages/PageCard.jsx b/src/pages-and-resources/pages/PageCard.jsx
index 02688b0aec..8ea154c111 100644
--- a/src/pages-and-resources/pages/PageCard.jsx
+++ b/src/pages-and-resources/pages/PageCard.jsx
@@ -1,15 +1,11 @@
import classNames from 'classnames';
-import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
-import {
- Badge, Card, Icon, IconButton, Hyperlink,
-} from '@openedx/paragon';
-import { ArrowForward, Settings } from '@openedx/paragon/icons';
+import { useIntl } from '@edx/frontend-platform/i18n';
+import { Badge, Card } from '@openedx/paragon';
import PropTypes from 'prop-types';
-import React, { useContext } from 'react';
-import { useNavigate } from 'react-router-dom';
+import React from 'react';
import messages from '../messages';
-import { PagesAndResourcesContext } from '../PagesAndResourcesProvider';
import { useIsDesktop } from '../../utils';
+import PageSettingButton from './PageSettingButton';
import './PageCard.scss';
const CoursePageShape = PropTypes.shape({
@@ -19,45 +15,21 @@ const CoursePageShape = PropTypes.shape({
enabled: PropTypes.bool.isRequired,
legacyLink: PropTypes.string,
allowedOperations: PropTypes.shape({
- enable: PropTypes.bool.isRequired,
- configure: PropTypes.bool.isRequired,
- }).isRequired,
+ enable: PropTypes.bool,
+ configure: PropTypes.bool,
+ }),
});
export { CoursePageShape };
const PageCard = ({
- intl,
page,
+ settingButton,
}) => {
- const { path: pagesAndResourcesPath } = useContext(PagesAndResourcesContext);
+ const { formatMessage } = useIntl();
const isDesktop = useIsDesktop();
- const navigate = useNavigate();
- // eslint-disable-next-line react/no-unstable-nested-components
- const SettingsButton = () => {
- if (page.legacyLink) {
- return (
-
-
-
- );
- }
- return (page.allowedOperations.configure || page.allowedOperations.enable) && (
- navigate(`${pagesAndResourcesPath}/${page.id}/settings`)}
- />
- );
- };
+ const SettingButton = settingButton || ;
return (
- {intl.formatMessage(messages.enabled)}
+ {formatMessage(messages.enabled)}
)}
- actions={
}
+ actions={{SettingButton}
}
size="sm"
/>
@@ -85,9 +57,13 @@ const PageCard = ({
);
};
+PageCard.defaultProps = {
+ settingButton: null,
+};
+
PageCard.propTypes = {
- intl: intlShape.isRequired,
page: CoursePageShape.isRequired,
+ settingButton: PropTypes.node,
};
-export default injectIntl(PageCard);
+export default PageCard;
diff --git a/src/pages-and-resources/pages/PageGrid.jsx b/src/pages-and-resources/pages/PageGrid.jsx
index 7191498f49..33c5772efe 100644
--- a/src/pages-and-resources/pages/PageGrid.jsx
+++ b/src/pages-and-resources/pages/PageGrid.jsx
@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import { injectIntl } from '@edx/frontend-platform/i18n';
import { CardGrid } from '@openedx/paragon';
+import { PluginSlot } from '@openedx/frontend-plugin-framework';
import PageCard, { CoursePageShape } from './PageCard';
const PageGrid = ({ pages }) => (
@@ -15,6 +16,7 @@ const PageGrid = ({ pages }) => (
{pages.map((page) => (
))}
+
);
diff --git a/src/pages-and-resources/pages/PageSettingButton.jsx b/src/pages-and-resources/pages/PageSettingButton.jsx
new file mode 100644
index 0000000000..931181d802
--- /dev/null
+++ b/src/pages-and-resources/pages/PageSettingButton.jsx
@@ -0,0 +1,59 @@
+import React, { useContext } from 'react';
+import PropTypes from 'prop-types';
+
+import { useIntl } from '@edx/frontend-platform/i18n';
+import { Icon, IconButton, Hyperlink } from '@openedx/paragon';
+import { ArrowForward, Settings } from '@openedx/paragon/icons';
+import { useNavigate } from 'react-router-dom';
+import messages from '../messages';
+import { PagesAndResourcesContext } from '../PagesAndResourcesProvider';
+
+const PageSettingButton = ({
+ id,
+ legacyLink,
+ allowedOperations,
+}) => {
+ const { formatMessage } = useIntl();
+ const { path: pagesAndResourcesPath } = useContext(PagesAndResourcesContext);
+ const navigate = useNavigate();
+
+ if (legacyLink) {
+ return (
+
+
+
+ );
+ } if (!(allowedOperations?.configure || allowedOperations?.enable)) {
+ return null;
+ }
+ return (
+ navigate(`${pagesAndResourcesPath}/${id}/settings`)}
+ />
+ );
+};
+
+PageSettingButton.defaultProps = {
+ legacyLink: null,
+ allowedOperations: null,
+};
+
+PageSettingButton.propTypes = {
+ id: PropTypes.string.isRequired,
+ legacyLink: PropTypes.string,
+ allowedOperations: PropTypes.shape({
+ configure: PropTypes.bool,
+ enable: PropTypes.bool,
+ }),
+};
+
+export default PageSettingButton;
From b1cfedd8eabe16b5e3c35d3e9424e5bb8fd27e99 Mon Sep 17 00:00:00 2001
From: Leangseu Kim <83240113+leangseu-edx@users.noreply.github.com>
Date: Wed, 17 Apr 2024 09:57:14 -0400
Subject: [PATCH 2/3] chore: add eslint ignore for env.config.jsx
---
.eslintignore | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.eslintignore b/.eslintignore
index 6e1aa5ede1..9c46e7b475 100755
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,4 +1,6 @@
coverage/*
dist/
node_modules/
-jest.config.js
\ No newline at end of file
+jest.config.js
+env.config.jsx
+example.env.config.jsx
From a88a759d304647ecc8c0704dac69427db782628f Mon Sep 17 00:00:00 2001
From: Leangseu Kim <83240113+leangseu-edx@users.noreply.github.com>
Date: Wed, 24 Apr 2024 11:47:30 -0400
Subject: [PATCH 3/3] chore: update package-lock.json
---
package-lock.json | 31 -------------------------------
1 file changed, 31 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index a6d3b3765e..b3dc197513 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18127,37 +18127,6 @@
"react": ">=0.14.0"
}
},
- "node_modules/react-instantsearch": {
- "version": "7.7.1",
- "resolved": "https://registry.npmjs.org/react-instantsearch/-/react-instantsearch-7.7.1.tgz",
- "integrity": "sha512-o6nLY4IZWql6m0LYFSKpPKlAZ8zV3fwnwgswGs1okdw2skb3TXB535/mQCQZF39YjrUqBc3thl/YMnEDnKtVaQ==",
- "dependencies": {
- "@babel/runtime": "^7.1.2",
- "instantsearch-ui-components": "0.4.0",
- "instantsearch.js": "4.66.1",
- "react-instantsearch-core": "7.7.1"
- },
- "peerDependencies": {
- "algoliasearch": ">= 3.1 < 5",
- "react": ">= 16.8.0 < 19",
- "react-dom": ">= 16.8.0 < 19"
- }
- },
- "node_modules/react-instantsearch-core": {
- "version": "7.7.1",
- "resolved": "https://registry.npmjs.org/react-instantsearch-core/-/react-instantsearch-core-7.7.1.tgz",
- "integrity": "sha512-OTvf/QtJT5zd+EQW+osjPPFNr7Vo9FAzy/zUxeeP+87IS6tiUpQQEDhgFFYBbvU5+97pYl9YmvGQARakNDHJOw==",
- "dependencies": {
- "@babel/runtime": "^7.1.2",
- "algoliasearch-helper": "3.17.0",
- "instantsearch.js": "4.66.1",
- "use-sync-external-store": "^1.0.0"
- },
- "peerDependencies": {
- "algoliasearch": ">= 3.1 < 5",
- "react": ">= 16.8.0 < 19"
- }
- },
"node_modules/react-intl": {
"version": "6.6.4",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.6.4.tgz",