From bbb7e895a57406e1beb56a9a01f4c6350a1647ec Mon Sep 17 00:00:00 2001 From: Muhammad Abdullah Waheed <42172960+abdullahwaheed@users.noreply.github.com> Date: Thu, 30 Jun 2022 23:22:29 +0500 Subject: [PATCH] Paragon table deprecation updation (#246) * refactor: updated table deprecation of HistoryTable * refactor: updated table deprecation of OverrideTable * refactor: updated table deprecation of GradebookTable * refactor: updated unit tests of OverrideTable according to new changes * fix: error for css * fix: strict dictionary error for react component * chore: update css and handle syntax errors * chore: update unit test * fix: remove datatable row status and update tests * fix: test coverage Co-authored-by: Leangseu Kim Co-authored-by: Ben Warzeski --- jest.config.js | 1 + package-lock.json | 520 ++++++++++++++++-- package.json | 2 +- .../HistoryTable.jsx | 17 +- .../HistoryTable.test.jsx | 9 +- .../__snapshots__/HistoryTable.test.jsx.snap | 147 +++-- .../OverrideTable/__snapshots__/test.jsx.snap | 19 +- .../EditModal/OverrideTable/index.jsx | 15 +- .../EditModal/OverrideTable/test.jsx | 2 +- .../__snapshots__/test.jsx.snap | 90 +-- .../GradesView/GradebookTable/index.jsx | 29 +- .../GradesView/GradebookTable/messages.js | 5 + .../GradesView/GradebookTable/test.jsx | 43 +- src/components/GradesView/GradesView.scss | 96 +--- src/data/constants/app.js | 16 +- src/testUtils.js | 187 +++++++ src/utils/StrictDict.js | 4 + src/utils/StrictDict.test.js | 3 + 18 files changed, 869 insertions(+), 336 deletions(-) create mode 100644 src/testUtils.js diff --git a/jest.config.js b/jest.config.js index bb3d8b6..4e02eec 100644 --- a/jest.config.js +++ b/jest.config.js @@ -11,5 +11,6 @@ module.exports = createConfig('jest', { coveragePathIgnorePatterns: [ 'src/segment.js', 'src/postcss.config.js', + 'testUtils', // don't unit test jest mocking tools ], }); diff --git a/package-lock.json b/package-lock.json index d3479cc..3f81fc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@edx/brand": "npm:@edx/brand-edx.org@^1.3.2", "@edx/frontend-component-footer": "10.2.1", "@edx/frontend-component-header": "2.4.5", - "@edx/frontend-platform": "1.15.1", + "@edx/frontend-platform": "2.3.0", "@edx/paragon": "19.6.0", "@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/free-brands-svg-icons": "^5.11.2", @@ -3977,30 +3977,31 @@ } }, "node_modules/@edx/frontend-platform": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-1.15.1.tgz", - "integrity": "sha512-vi2iG01z/mvlxKbgozGkZQTbchliq6MJ9tY4CpYy1CMjt5Rn/PDaS2sK6FKhuAlyQAR4ZqFWHrwM1wednG9RQQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-2.3.0.tgz", + "integrity": "sha512-vZAw3eKJgUvD3wu8QOlCbNvuhe9YOGhdVuiTiFGMJKsYagJNMuQZxTJ2DwPCr7/gprJ65mboisJ3BF5IoFzVJA==", "dependencies": { "@cospired/i18n-iso-languages": "2.2.0", - "axios": "0.21.4", + "@formatjs/intl-pluralrules": "^4.3.3", + "@formatjs/intl-relativetimeformat": "^10.0.1", + "axios": "0.26.1", "axios-cache-adapter": "2.7.3", "form-urlencoded": "4.1.4", - "glob": "7.1.7", + "glob": "7.2.0", "history": "4.10.1", "i18n-iso-countries": "4.3.1", "jwt-decode": "3.1.2", - "localforage": "1.9.0", + "localforage": "1.10.0", "localforage-memoryStorageDriver": "0.9.2", "lodash.camelcase": "4.3.0", "lodash.memoize": "4.1.2", "lodash.merge": "4.6.2", "lodash.snakecase": "4.1.1", "pubsub-js": "1.9.4", - "react-intl": "2.9.0", + "react-intl": "^5.25.0", "universal-cookie": "4.0.4" }, "bin": { - "transifex-Makefile": "i18n/scripts/Makefile", "transifex-utils.js": "i18n/scripts/transifex-utils.js" }, "peerDependencies": { @@ -4013,31 +4014,58 @@ "redux": "^4.0.4" } }, - "node_modules/@edx/frontend-platform/node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "node_modules/@edx/frontend-platform/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", "dependencies": { - "follow-redirects": "^1.14.0" + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" } }, - "node_modules/@edx/frontend-platform/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "node_modules/@edx/frontend-platform/node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "follow-redirects": "^1.14.8" + } + }, + "node_modules/@edx/frontend-platform/node_modules/intl-messageformat": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@edx/frontend-platform/node_modules/react-intl": { + "version": "5.25.1", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", + "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl": "2.2.1", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" }, - "engines": { - "node": "*" + "peerDependencies": { + "react": "^16.3.0 || 17 || 18", + "typescript": "^4.5" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@edx/new-relic-source-map-webpack-plugin": { @@ -4109,6 +4137,119 @@ "tslib": "^2.0.1" } }, + "node_modules/@formatjs/fast-memoize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-skeleton-parser": "1.3.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz", + "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "typescript": "^4.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@formatjs/intl-displaynames": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz", + "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-displaynames/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-listformat": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz", + "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-listformat/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@formatjs/intl-numberformat": { "version": "5.7.6", "resolved": "https://registry.npmjs.org/@formatjs/intl-numberformat/-/intl-numberformat-5.7.6.tgz", @@ -4119,6 +4260,64 @@ "tslib": "^2.0.1" } }, + "node_modules/@formatjs/intl-pluralrules": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.3.3.tgz", + "integrity": "sha512-NLZN8gf2qLpCuc0m565IbKLNUarEGOzk0mkdTkE4XTuNCofzoQTurW6lL3fmDlneAoYl2FiTdHa5q4o2vZF50g==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-pluralrules/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-relativetimeformat": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-10.0.1.tgz", + "integrity": "sha512-AABPQtPjFilXegQsnmVHrSlzjFNUffAEk5DgowY6b7WSwDI7g2W6QgW903/lbZ58emhphAbgHdtKeUBXqTiLpw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-relativetimeformat/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl/node_modules/intl-messageformat": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" + } + }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "0.2.36", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", @@ -15550,7 +15749,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -21603,9 +21801,9 @@ } }, "node_modules/localforage": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.9.0.tgz", - "integrity": "sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", "dependencies": { "lie": "3.1.1" } @@ -32867,7 +33065,7 @@ "version": "4.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -37520,48 +37718,74 @@ } }, "@edx/frontend-platform": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-1.15.1.tgz", - "integrity": "sha512-vi2iG01z/mvlxKbgozGkZQTbchliq6MJ9tY4CpYy1CMjt5Rn/PDaS2sK6FKhuAlyQAR4ZqFWHrwM1wednG9RQQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-2.3.0.tgz", + "integrity": "sha512-vZAw3eKJgUvD3wu8QOlCbNvuhe9YOGhdVuiTiFGMJKsYagJNMuQZxTJ2DwPCr7/gprJ65mboisJ3BF5IoFzVJA==", "requires": { "@cospired/i18n-iso-languages": "2.2.0", - "axios": "0.21.4", + "@formatjs/intl-pluralrules": "^4.3.3", + "@formatjs/intl-relativetimeformat": "^10.0.1", + "axios": "0.26.1", "axios-cache-adapter": "2.7.3", "form-urlencoded": "4.1.4", - "glob": "7.1.7", + "glob": "7.2.0", "history": "4.10.1", "i18n-iso-countries": "4.3.1", "jwt-decode": "3.1.2", - "localforage": "1.9.0", + "localforage": "1.10.0", "localforage-memoryStorageDriver": "0.9.2", "lodash.camelcase": "4.3.0", "lodash.memoize": "4.1.2", "lodash.merge": "4.6.2", "lodash.snakecase": "4.1.1", "pubsub-js": "1.9.4", - "react-intl": "2.9.0", + "react-intl": "^5.25.0", "universal-cookie": "4.0.4" }, "dependencies": { - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", "requires": { - "follow-redirects": "^1.14.0" + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" } }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "follow-redirects": "^1.14.8" + } + }, + "intl-messageformat": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" + } + }, + "react-intl": { + "version": "5.25.1", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", + "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl": "2.2.1", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" } } } @@ -37632,6 +37856,141 @@ "tslib": "^2.0.1" } }, + "@formatjs/fast-memoize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@formatjs/icu-messageformat-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-skeleton-parser": "1.3.6", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/icu-skeleton-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/intl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz", + "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "intl-messageformat": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/intl-displaynames": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz", + "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/intl-listformat": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz", + "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/intl-localematcher": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "requires": { + "tslib": "^2.1.0" + } + }, "@formatjs/intl-numberformat": { "version": "5.7.6", "resolved": "https://registry.npmjs.org/@formatjs/intl-numberformat/-/intl-numberformat-5.7.6.tgz", @@ -37642,6 +38001,48 @@ "tslib": "^2.0.1" } }, + "@formatjs/intl-pluralrules": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.3.3.tgz", + "integrity": "sha512-NLZN8gf2qLpCuc0m565IbKLNUarEGOzk0mkdTkE4XTuNCofzoQTurW6lL3fmDlneAoYl2FiTdHa5q4o2vZF50g==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/intl-relativetimeformat": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-10.0.1.tgz", + "integrity": "sha512-AABPQtPjFilXegQsnmVHrSlzjFNUffAEk5DgowY6b7WSwDI7g2W6QgW903/lbZ58emhphAbgHdtKeUBXqTiLpw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, "@fortawesome/fontawesome-common-types": { "version": "0.2.36", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", @@ -46616,7 +47017,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -51397,9 +51797,9 @@ } }, "localforage": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.9.0.tgz", - "integrity": "sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", "requires": { "lie": "3.1.1" } @@ -59948,7 +60348,7 @@ "version": "4.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", - "dev": true + "devOptional": true }, "uglify-js": { "version": "3.15.4", diff --git a/package.json b/package.json index 092704f..758e5b8 100755 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@edx/brand": "npm:@edx/brand-edx.org@^1.3.2", "@edx/frontend-component-footer": "10.2.1", "@edx/frontend-component-header": "2.4.5", - "@edx/frontend-platform": "1.15.1", + "@edx/frontend-platform": "2.3.0", "@edx/paragon": "19.6.0", "@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/free-brands-svg-icons": "^5.11.2", diff --git a/src/components/BulkManagementHistoryView/HistoryTable.jsx b/src/components/BulkManagementHistoryView/HistoryTable.jsx index 429f953..183aa70 100644 --- a/src/components/BulkManagementHistoryView/HistoryTable.jsx +++ b/src/components/BulkManagementHistoryView/HistoryTable.jsx @@ -3,7 +3,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { Table } from '@edx/paragon'; +import { DataTable } from '@edx/paragon'; import { bulkManagementColumns } from 'data/constants/app'; import selectors from 'data/selectors'; @@ -30,14 +30,13 @@ export const mapHistoryRows = ({ export const HistoryTable = ({ bulkManagementHistory, }) => ( - <> - - + ); HistoryTable.defaultProps = { bulkManagementHistory: [], diff --git a/src/components/BulkManagementHistoryView/HistoryTable.test.jsx b/src/components/BulkManagementHistoryView/HistoryTable.test.jsx index 5542c4b..96f5ec4 100644 --- a/src/components/BulkManagementHistoryView/HistoryTable.test.jsx +++ b/src/components/BulkManagementHistoryView/HistoryTable.test.jsx @@ -1,7 +1,7 @@ /* eslint-disable import/no-named-as-default */ import React from 'react'; import { shallow } from 'enzyme'; -import { Table } from '@edx/paragon'; +import { DataTable } from '@edx/paragon'; import selectors from 'data/selectors'; import { bulkManagementColumns } from 'data/constants/app'; @@ -9,13 +9,12 @@ import { bulkManagementColumns } from 'data/constants/app'; import ResultsSummary from './ResultsSummary'; import { HistoryTable, mapStateToProps } from './HistoryTable'; +jest.mock('@edx/paragon', () => ({ DataTable: () => 'DataTable' })); + jest.mock('@edx/frontend-platform/i18n', () => ({ defineMessages: m => m, FormattedMessage: () => 'FormattedMessage', })); -jest.mock('@edx/paragon', () => ({ - Table: () => 'Table', -})); jest.mock('data/selectors', () => ({ __esModule: true, default: { @@ -62,7 +61,7 @@ describe('HistoryTable', () => { describe('history table', () => { let table; beforeEach(() => { - table = el.find(Table); + table = el.find(DataTable); }); describe('data (from bulkManagementHistory.map(this.formatHistoryRow)', () => { const fieldAssertions = [ diff --git a/src/components/BulkManagementHistoryView/__snapshots__/HistoryTable.test.jsx.snap b/src/components/BulkManagementHistoryView/__snapshots__/HistoryTable.test.jsx.snap index 7272aa9..e6f3301 100644 --- a/src/components/BulkManagementHistoryView/__snapshots__/HistoryTable.test.jsx.snap +++ b/src/components/BulkManagementHistoryView/__snapshots__/HistoryTable.test.jsx.snap @@ -42,78 +42,77 @@ Array [ `; exports[`HistoryTable component snapshot snapshot - loads formatted table 1`] = ` - -
- blue.png - , - "resultsSummary": , - "timeUploaded": "65", - "user": - Eifel - , - }, - Object { - "filename": - allStar.jpg - , - "resultsSummary": , - "timeUploaded": "2000s?", - "user": - Smashmouth - , - }, - ] - } - hasFixedColumnWidths={true} - /> - + + blue.png + , + "resultsSummary": , + "timeUploaded": "65", + "user": + Eifel + , + }, + Object { + "filename": + allStar.jpg + , + "resultsSummary": , + "timeUploaded": "2000s?", + "user": + Smashmouth + , + }, + ] + } + hasFixedColumnWidths={true} + itemCount={2} +/> `; diff --git a/src/components/GradesView/EditModal/OverrideTable/__snapshots__/test.jsx.snap b/src/components/GradesView/EditModal/OverrideTable/__snapshots__/test.jsx.snap index d1e953d..1f1169b 100644 --- a/src/components/GradesView/EditModal/OverrideTable/__snapshots__/test.jsx.snap +++ b/src/components/GradesView/EditModal/OverrideTable/__snapshots__/test.jsx.snap @@ -1,40 +1,40 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`OverrideTable Component snapshots basic snapshot shows a row for each entry and one editable row 1`] = ` -
, + "accessor": "date", }, Object { - "key": "grader", - "label": , + "accessor": "grader", }, Object { - "key": "reason", - "label": , + "accessor": "reason", }, Object { - "key": "adjustedGrade", - "label": , + "accessor": "adjustedGrade", }, ] } @@ -59,5 +59,6 @@ exports[`OverrideTable Component snapshots basic snapshot shows a row for each e }, ] } + itemCount={2} /> `; diff --git a/src/components/GradesView/EditModal/OverrideTable/index.jsx b/src/components/GradesView/EditModal/OverrideTable/index.jsx index f06b706..bd5e765 100644 --- a/src/components/GradesView/EditModal/OverrideTable/index.jsx +++ b/src/components/GradesView/EditModal/OverrideTable/index.jsx @@ -3,7 +3,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { Table } from '@edx/paragon'; +import { DataTable } from '@edx/paragon'; import { FormattedMessage } from '@edx/frontend-platform/i18n'; import { gradeOverrideHistoryColumns as columns } from 'data/constants/app'; @@ -27,14 +27,14 @@ export const OverrideTable = ({ return null; } return ( -
, key: columns.date }, - { label: , key: columns.grader }, - { label: , key: columns.reason }, + { Header: , accessor: columns.date }, + { Header: , accessor: columns.grader }, + { Header: , accessor: columns.reason }, { - label: , - key: columns.adjustedGrade, + Header: , + accessor: columns.adjustedGrade, }, ]} data={[ @@ -45,6 +45,7 @@ export const OverrideTable = ({ reason: , }, ]} + itemCount={gradeOverrides.length} /> ); }; diff --git a/src/components/GradesView/EditModal/OverrideTable/test.jsx b/src/components/GradesView/EditModal/OverrideTable/test.jsx index c459440..96149b4 100644 --- a/src/components/GradesView/EditModal/OverrideTable/test.jsx +++ b/src/components/GradesView/EditModal/OverrideTable/test.jsx @@ -8,7 +8,7 @@ import { mapStateToProps, } from '.'; -jest.mock('@edx/paragon', () => ({ Table: () => 'Table' })); +jest.mock('@edx/paragon', () => ({ DataTable: () => 'DataTable' })); jest.mock('./ReasonInput', () => 'ReasonInput'); jest.mock('./AdjustedGradeInput', () => 'AdjustedGradeInput'); diff --git a/src/components/GradesView/GradebookTable/__snapshots__/test.jsx.snap b/src/components/GradesView/GradebookTable/__snapshots__/test.jsx.snap index 702d896..fd0a0d3 100644 --- a/src/components/GradesView/GradebookTable/__snapshots__/test.jsx.snap +++ b/src/components/GradesView/GradebookTable/__snapshots__/test.jsx.snap @@ -4,48 +4,58 @@ exports[`GradebookTable component snapshot - fields1 and 2 between email and tot
-
, + "accessor": "Username", + }, + Object { + "Header": , + "accessor": "Email", + }, + Object { + "Header": "field1", + "accessor": "field1", + }, + Object { + "Header": "field2", + "accessor": "field2", + }, + Object { + "Header": , + "accessor": "Total Grade (%)", + }, + ] + } + data={ + Array [ + "mappedRow: 1", + "mappedRow: 2", + "mappedRow: 3", + ] + } + hasFixedColumnWidths={true} + itemCount={3} + rowHeaderColumnKey="username" > -
, - }, - Object { - "key": "Email", - "label": , - }, - Object { - "key": "field1", - "label": "field1", - }, - Object { - "key": "field2", - "label": "field2", - }, - Object { - "key": "Total Grade (%)", - "label": , - }, - ] + + + } - data={ - Array [ - "mappedRow: 1", - "mappedRow: 2", - "mappedRow: 3", - ] - } - hasFixedColumnWidths={true} - rowHeaderColumnKey="username" /> - + `; diff --git a/src/components/GradesView/GradebookTable/index.jsx b/src/components/GradesView/GradebookTable/index.jsx index f448cdb..21ee194 100644 --- a/src/components/GradesView/GradebookTable/index.jsx +++ b/src/components/GradesView/GradebookTable/index.jsx @@ -3,7 +3,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { Table } from '@edx/paragon'; +import { DataTable } from '@edx/paragon'; import { FormattedMessage } from '@edx/frontend-platform/i18n'; import selectors from 'data/selectors'; @@ -27,6 +27,7 @@ export class GradebookTable extends React.Component { super(props); this.mapHeaders = this.mapHeaders.bind(this); this.mapRows = this.mapRows.bind(this); + this.nullMethod = this.nullMethod.bind(this); } mapHeaders(heading) { @@ -40,7 +41,7 @@ export class GradebookTable extends React.Component { } else { label = heading; } - return { label, key: heading }; + return { Header: label, accessor: heading }; } mapRows(entry) { @@ -59,17 +60,25 @@ export class GradebookTable extends React.Component { return dataRow; } + nullMethod() { + return null; + } + render() { return (
-
-
- + + + + } /> + ); } diff --git a/src/components/GradesView/GradebookTable/messages.js b/src/components/GradesView/GradebookTable/messages.js index 458e8dd..9e58f77 100644 --- a/src/components/GradesView/GradebookTable/messages.js +++ b/src/components/GradesView/GradebookTable/messages.js @@ -31,6 +31,11 @@ const messages = defineMessages({ defaultMessage: 'Total Grade values are always displayed as a percentage', description: 'Gradebook table message that total grades are displayed in percent format', }, + noResultsFound: { + id: 'gradebook.GradesView.table.noResultsFound', + defaultMessage: 'No results found', + description: 'Gradebook table message when no learner results were found', + }, }); export default messages; diff --git a/src/components/GradesView/GradebookTable/test.jsx b/src/components/GradesView/GradebookTable/test.jsx index 88a9d4a..f144913 100644 --- a/src/components/GradesView/GradebookTable/test.jsx +++ b/src/components/GradesView/GradebookTable/test.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { Table } from '@edx/paragon'; +import { DataTable } from '@edx/paragon'; import { FormattedMessage } from '@edx/frontend-platform/i18n'; import selectors from 'data/selectors'; @@ -11,8 +11,12 @@ import Fields from './Fields'; import messages from './messages'; import { GradebookTable, mapStateToProps } from '.'; -jest.mock('@edx/paragon', () => ({ - Table: () => 'Table', +jest.mock('@edx/paragon', () => jest.requireActual('testUtils').mockNestedComponents({ + DataTable: { + Table: 'DataTable.Table', + TableControlBar: 'DataTable.TableControlBar', + EmptyTable: 'DataTable.EmptyTable', + }, })); jest.mock('./Fields', () => ({ __esModule: true, @@ -79,40 +83,45 @@ describe('GradebookTable', () => { }; test('snapshot - fields1 and 2 between email and totalGrade, mocked rows', () => { el = shallow(); + el.instance().nullMethod = jest.fn().mockName('this.nullMethod'); el.instance().mapRows = (entry) => `mappedRow: ${entry.percent}`; expect(el.instance().render()).toMatchSnapshot(); }); + test('null method returns null for stub component', () => { + el = shallow(); + expect(el.instance().nullMethod()).toEqual(null); + }); describe('table columns (mapHeaders)', () => { let headings; beforeEach(() => { el = shallow(); - headings = el.find(Table).props().columns; + headings = el.find(DataTable).props().columns; }); - test('username sets key and replaces label with component', () => { + test('username sets key and replaces Header with component', () => { const heading = headings[0]; - expect(heading.key).toEqual(Headings.username); - expect(heading.label.type).toEqual(LabelReplacements.UsernameLabelReplacement); + expect(heading.accessor).toEqual(Headings.username); + expect(heading.Header.type).toEqual(LabelReplacements.UsernameLabelReplacement); }); - test('email sets key and label from header', () => { + test('email sets key and Header from header', () => { const heading = headings[1]; - expect(heading.key).toEqual(Headings.email); - expect(heading.label).toEqual(); + expect(heading.accessor).toEqual(Headings.email); + expect(heading.Header).toEqual(); }); - test('subsections set key and label from header', () => { - expect(headings[2]).toEqual({ key: fields.field1, label: fields.field1 }); - expect(headings[3]).toEqual({ key: fields.field2, label: fields.field2 }); + test('subsections set key and Header from header', () => { + expect(headings[2]).toEqual({ accessor: fields.field1, Header: fields.field1 }); + expect(headings[3]).toEqual({ accessor: fields.field2, Header: fields.field2 }); }); - test('totalGrade sets key and replaces label with component', () => { + test('totalGrade sets key and replaces Header with component', () => { const heading = headings[4]; - expect(heading.key).toEqual(Headings.totalGrade); - expect(heading.label.type).toEqual(LabelReplacements.TotalGradeLabelReplacement); + expect(heading.accessor).toEqual(Headings.totalGrade); + expect(heading.Header.type).toEqual(LabelReplacements.TotalGradeLabelReplacement); }); }); describe('table data (mapRows)', () => { let rows; beforeEach(() => { el = shallow(); - rows = el.find(Table).props().data; + rows = el.find(DataTable).props().data; }); describe.each([0, 1, 2])('gradeEntry($percent)', (gradeIndex) => { let row; diff --git a/src/components/GradesView/GradesView.scss b/src/components/GradesView/GradesView.scss index 81f21f7..c1bf0c9 100644 --- a/src/components/GradesView/GradesView.scss +++ b/src/components/GradesView/GradesView.scss @@ -31,7 +31,7 @@ .import-grades-btn { margin-left: 20px; } - .intervention-report-description: { + .intervention-report-description { margin-right: 40px; } h4.step-message-1 { @@ -71,100 +71,6 @@ position: relative; } -.gbook { - width: 100%; - - .grade-button { - text-decoration: underline; - } - - .student-key { - font-size: 14px; - } - - #courseGradeTooltipIcon { - float: right; - } - - .table thead tr { - min-height: 60px; - &:nth-child(1) { - position: sticky; - top: 0; - z-index: 10; - background-color: white; - th { - background-color: white; - border-bottom: 1px solid $gray_200; - } - } - } - - thead, tbody, tr, td, th { - display: block; - word-break: break-word; - } - - .table tr th:first-child { - border-bottom: none; - } - .table tr th:first-child, - .table tr td:first-child { - position: sticky; - left: 0; - z-index: 1; // to float over the following children in the side-scrolling case - background: white; - } - - .table tr { - th:nth-child(1), - td:nth-child(1), - th:nth-child(2), - td:nth-child(2) { - width: 240px; - } - th:nth-last-of-type(1) { - width: 150px; - } - th, td { - width: 120px; - } - } - .table tbody th { - font-weight: normal; - } - - .table { - overflow-x: hidden; - - height: 100%; - - tbody { - overflow-y: auto; - display: block; - } - - thead, tbody tr { - display: table; - table-layout: fixed; - } - - th { - vertical-align: top; - font-size: 14px; - } - } - - .link-style { - color: #0075b4; - &:hover, &:focus { - color: #004368; - text-decoration: underline; - } - } - -} - .form-group, .pgn__form-group { label { font-weight: bold; diff --git a/src/data/constants/app.js b/src/data/constants/app.js index 7631535..a548983 100644 --- a/src/data/constants/app.js +++ b/src/data/constants/app.js @@ -32,26 +32,26 @@ export const localFilterKeys = StrictDict({ */ export const bulkManagementColumns = [ { - key: 'filename', - label: 'Gradebook', + accessor: 'filename', + Header: 'Gradebook', columnSortable: false, width: 'col-5', }, { - key: 'resultsSummary', - label: 'Download Summary', + accessor: 'resultsSummary', + Header: 'Download Summary', columnSortable: false, width: 'col', }, { - key: 'user', - label: 'Who', + accessor: 'user', + Header: 'Who', columnSortable: false, width: 'col-1', }, { - key: 'timeUploaded', - label: 'When', + accessor: 'timeUploaded', + Header: 'When', columnSortable: false, width: 'col', }, diff --git a/src/testUtils.js b/src/testUtils.js new file mode 100644 index 0000000..baf986a --- /dev/null +++ b/src/testUtils.js @@ -0,0 +1,187 @@ +import react from 'react'; + +import { StrictDict } from 'utils'; + +/** + * Mocked formatMessage provided by react-intl + */ +export const formatMessage = (msg, values) => { + let message = msg.defaultMessage; + if (values === undefined) { + return message; + } + Object.keys(values).forEach((key) => { + // eslint-disable-next-line + message = message.replace(`{${key}}`, values[key]); + }); + return message; +}; + +/** + * Mock a single component, or a nested component so that its children render nicely + * in snapshots. + * @param {string} name - parent component name + * @param {obj} contents - object of child components with intended component + * render name. + * @return {func} - mock component with nested children. + * + * usage: + * mockNestedComponent('Card', { Body: 'Card.Body', Form: { Control: { Feedback: 'Form.Control.Feedback' }}... }); + * mockNestedComponent('IconButton', 'IconButton'); + */ +export const mockNestedComponent = (name, contents) => { + if (typeof contents !== 'object') { + return contents; + } + const fn = () => name; + Object.defineProperty(fn, 'name', { value: name }); + Object.keys(contents).forEach((nestedName) => { + const value = contents[nestedName]; + fn[nestedName] = typeof value !== 'object' + ? value + : mockNestedComponent(`${name}.${nestedName}`, value); + }); + return fn; +}; + +/** + * Mock a module of components. nested components will be rendered nicely in snapshots. + * @param {obj} mapping - component module mock config. + * @return {obj} - module of flat and nested components that will render nicely in snapshots. + * usage: + * mockNestedComponents({ + * Card: { Body: 'Card.Body' }, + * IconButton: 'IconButton', + * }) + */ +export const mockNestedComponents = (mapping) => Object.entries(mapping).reduce( + (obj, [name, value]) => ({ + ...obj, + [name]: mockNestedComponent(name, value), + }), + {}, +); + +/** + * Mock utility for working with useState in a hooks module. + * Expects/requires an object containing the state object in order to ensure + * the mock behavior works appropriately. + * + * Expected format: + * hooks = { state: { : (val) => React.createRef(val), ... } } + * + * Returns a utility for mocking useState and providing access to specific state values + * and setState methods, as well as allowing per-test configuration of useState value returns. + * + * Example usage: + * // hooks.js + * import * as module from './hooks'; + * const state = { + * isOpen: (val) => React.useState(val), + * hasDoors: (val) => React.useState(val), + * selected: (val) => React.useState(val), + * }; + * ... + * export const exampleHook = () => { + * const [isOpen, setIsOpen] = module.state.isOpen(false); + * if (!isOpen) { return null; } + * return { isOpen, setIsOpen }; + * } + * ... + * + * // hooks.test.js + * import * as hooks from './hooks'; + * const state = new MockUseState(hooks) + * ... + * describe('state hooks', () => { + * state.testGetter(state.keys.isOpen); + * state.testGetter(state.keys.hasDoors); + * state.testGetter(state.keys.selected); + * }); + * describe('exampleHook', () => { + * beforeEach(() => { state.mock(); }); + * it('returns null if isOpen is default value', () => { + * expect(hooks.exampleHook()).toEqual(null); + * }); + * it('returns isOpen and setIsOpen if isOpen is not null', () => { + * state.mockVal(state.keys.isOpen, true); + * expect(hooks.exampleHook()).toEqual({ + * isOpen: true, + * setIsOpen: state.setState[state.keys.isOpen], + * }); + * }); + * afterEach(() => { state.restore(); }); + * }); + * + * @param {obj} hooks - hooks module containing a 'state' object + */ +export class MockUseState { + constructor(hooks) { + this.hooks = hooks; + this.oldState = null; + this.setState = {}; + this.stateVals = {}; + + this.mock = this.mock.bind(this); + this.restore = this.restore.bind(this); + this.mockVal = this.mockVal.bind(this); + this.testGetter = this.testGetter.bind(this); + } + + /** + * @return {object} - StrictDict of state object keys + */ + get keys() { + return StrictDict(Object.keys(this.hooks.state).reduce( + (obj, key) => ({ ...obj, [key]: key }), + {}, + )); + } + + /** + * Replace the hook module's state object with a mocked version, initialized to default values. + */ + mock() { + this.oldState = this.hooks.state; + Object.keys(this.keys).forEach(key => { + this.hooks.state[key] = jest.fn(val => { + this.stateVals[key] = val; + return [val, this.setState[key]]; + }); + }); + this.setState = Object.keys(this.keys).reduce( + (obj, key) => ({ + ...obj, + [key]: jest.fn(val => { + this.hooks.state[key] = val; + }), + }), + {}, + ); + } + + /** + * Restore the hook module's state object to the actual code. + */ + restore() { + this.hooks.state = this.oldState; + } + + /** + * Mock the state getter associated with a single key to return a specific value one time. + * @param {string} key - state key (from this.keys) + * @param {any} val - new value to be returned by the useState call. + */ + mockVal(key, val) { + this.hooks.state[key].mockReturnValueOnce([val, this.setState[key]]); + } + + testGetter(key) { + test(`${key} state getter should return useState passthrough`, () => { + const testValue = 'some value'; + const useState = (val) => ({ useState: val }); + jest.spyOn(react, 'useState').mockImplementationOnce(useState); + expect(this.hooks.state[key](testValue)).toEqual(useState(testValue)); + }); + } +} diff --git a/src/utils/StrictDict.js b/src/utils/StrictDict.js index dd9588b..a127e48 100644 --- a/src/utils/StrictDict.js +++ b/src/utils/StrictDict.js @@ -4,6 +4,10 @@ const strictGet = (target, name) => { return target; } + if (name === '$$typeof') { + return typeof target; + } + if (name in target || name === '_reactFragment') { return target[name]; } diff --git a/src/utils/StrictDict.test.js b/src/utils/StrictDict.test.js index 276f50d..1a61649 100644 --- a/src/utils/StrictDict.test.js +++ b/src/utils/StrictDict.test.js @@ -45,6 +45,9 @@ describe('StrictDict', () => { it('allows entry listing', () => { expect(Object.entries(dict)).toEqual(Object.entries(rawDict)); }); + it('allows $$typeof access', () => { + expect(dict.$$typeof).toEqual(typeof rawDict); + }); describe('missing key', () => { it('logs error with target, name, and error stack', () => { // eslint-ignore-next-line no-unused-vars