Compare commits

...

52 Commits

Author SHA1 Message Date
Farhaan Bukhsh
b06315373b feat: Add max value warning and disable the submit button
Co-authored-by: lkatsikaris <lkatsikaris@@users.noreply.github.com>

Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
2025-01-12 19:48:42 +05:30
dependabot[bot]
e458d6fb05 build(deps): bump http-proxy-middleware from 2.0.6 to 2.0.7
Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.6 to 2.0.7.
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.7/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.6...v2.0.7)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-11 16:37:56 +05:30
dependabot[bot]
515307c923 build(deps): bump codecov/codecov-action from 4 to 5
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-11 16:30:10 +05:30
dependabot[bot]
e57cab41e5 build(deps): bump dawidd6/action-send-mail from 3 to 4
Bumps [dawidd6/action-send-mail](https://github.com/dawidd6/action-send-mail) from 3 to 4.
- [Release notes](https://github.com/dawidd6/action-send-mail/releases)
- [Commits](https://github.com/dawidd6/action-send-mail/compare/v3...v4)

---
updated-dependencies:
- dependency-name: dawidd6/action-send-mail
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-11 16:29:19 +05:30
edX requirements bot
957991d472 chore: update browserslist DB (#420)
Co-authored-by: jansenk <1639231+jansenk@users.noreply.github.com>
2025-01-11 16:22:42 +05:30
dependabot[bot]
f72faa824c build(deps-dev): bump cross-spawn from 6.0.5 to 6.0.6
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 6.0.5 to 6.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/v6.0.6/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.5...v6.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-11 16:21:45 +05:30
dependabot[bot]
6d21cbb616 build(deps): bump path-to-regexp, express and fetch-mock
Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) to 0.1.12 and updates ancestor dependencies [path-to-regexp](https://github.com/pillarjs/path-to-regexp), [express](https://github.com/expressjs/express) and [fetch-mock](https://github.com/wheresrhys/fetch-mock/tree/HEAD/packages/fetch-mock). These dependencies need to be updated together.


Updates `path-to-regexp` from 2.4.0 to 0.1.12
- [Release notes](https://github.com/pillarjs/path-to-regexp/releases)
- [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md)
- [Commits](https://github.com/pillarjs/path-to-regexp/compare/v2.4.0...v0.1.12)

Updates `express` from 4.21.0 to 4.21.2
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.2/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.21.0...4.21.2)

Updates `fetch-mock` from 6.5.2 to 12.2.0
- [Release notes](https://github.com/wheresrhys/fetch-mock/releases)
- [Changelog](https://github.com/wheresrhys/fetch-mock/blob/main/packages/fetch-mock/CHANGELOG.md)
- [Commits](https://github.com/wheresrhys/fetch-mock/commits/fetch-mock-v12.2.0/packages/fetch-mock)

---
updated-dependencies:
- dependency-name: path-to-regexp
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
- dependency-name: fetch-mock
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-11 16:19:48 +05:30
dependabot[bot]
de445a97be build(deps): bump nanoid from 3.3.7 to 3.3.8
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-11 16:18:17 +05:30
Farhaan Bukhsh
570e843fd4 chore: Removes unwanted dependency.
Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
2024-12-04 21:27:04 +05:30
edX requirements bot
e803c818ab chore: update browserslist DB (#416)
Co-authored-by: jansenk <1639231+jansenk@users.noreply.github.com>
2024-11-13 14:25:40 +05:30
dependabot[bot]
7a0483a896 build(deps): bump actions/checkout from 2 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-06 16:16:27 +05:30
jansenk
201584a7ea chore: update browserslist DB 2024-11-06 15:28:03 +05:30
Emad Rad
c222bec9ec chore: npm publish action removed (#415)
* chore: npm publish action removed

* chore: semantic release removed
2024-11-06 15:19:46 +05:30
Bilal Qamar
215662ba16 test: Remove support for Node 18 (#408) 2024-10-31 14:45:11 -04:00
edX requirements bot
cf5e1a65bf chore: enable github action auto update in dependabot.yml 2024-10-28 16:10:09 +05:30
Brian Smith
e08dc1ddc3 feat(deps): update header to 5.6.0 (#409) 2024-10-22 19:19:22 -04:00
Feanil Patel
3d2dd5006a Merge pull request #403 from openedx/feanil/ubuntu_upgrade
build: Switch to ubuntu-latest for builds
2024-09-20 10:27:51 -04:00
Bilal Qamar
694b1a75fc build: Upgrade to Node 20 (#406) 2024-09-19 17:50:39 -04:00
Feanil Patel
eff1ac0900 Merge branch 'master' into feanil/ubuntu_upgrade 2024-09-13 09:35:09 -04:00
Bilal Qamar
918463de91 test: Add Node 20 to CI matrix (#404) 2024-09-11 13:29:47 -04:00
Feanil Patel
a2d119aa43 build: Switch to ubuntu-latest for builds
This code does not have any dependencies that are specific to any specific
version of ubuntu.  So instead of testing on a specific version and then needing
to do work to keep the versions up-to-date, we switch to the ubuntu-latest
target which should be sufficient for testing purposes.

This work is being done as a part of https://github.com/openedx/platform-roadmap/issues/377

closes https://github.com/openedx/frontend-app-gradebook/issues/401
2024-09-09 10:00:30 -04:00
Bilal Qamar
ccb7865100 feat: updated frontend-build & frontend-platform major versions (#374)
* feat: bumped frontend-platform to v6

* chore: bumped jest to v29

* fix: updated snapshots for failing tests

* refactor: updated frontend-build, updated snapshots

* feat: updated build and platform major versions, along with edx packages

* refactor: updated index.test to resolve failing test

* refactor: major version upgrade for react-unit-test-utils

* refactor: updated package-lock
2024-08-02 16:35:00 +05:00
Adolfo R. Brandes
997d205ac6 build: Update codecov and use token (#398)
Update codecov to the latest version and start using the org-wide token for uploads.

See https://github.com/openedx/wg-frontend/issues/179
2024-06-17 12:03:01 -03:00
Adolfo R. Brandes
13433e969f Merge pull request #310 from raccoongang/lunyachek/fix/edit-grades-modal-rows-counter-olive
fix: Fix rows counter in the Edit Grade modal window
2024-05-30 14:13:07 -03:00
Farhaan Bukhsh
c21a81eb55 Merge pull request #393 from openedx/farhaan/add-catalog-file
chore: Adds catalog-info.yml for the project
2024-05-23 13:27:14 +05:30
Farhaan Bukhsh
9675a6e9a9 chore: Adds catalog-info.yml for the project
Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
2024-05-23 12:19:51 +05:30
Adolfo R. Brandes
d9a0a11936 Merge pull request #391 from brian-smith-tcril/footer-slot
feat: use frontend-plugin-framework to provide a FooterSlot
2024-05-17 13:10:25 -03:00
Brian Smith
13a19a274c feat: use frontend-plugin-framework to provide a FooterSlot 2024-05-17 10:49:02 -04:00
Feanil Patel
f4edf956bb Merge pull request #392 from salman2013/salman/Add-renovate-json-file
Add renovate configuration file
2024-05-15 12:05:41 -04:00
salman2013
c3c328fddb chore: add renovate configuration file 2024-05-15 15:23:02 +05:00
Adolfo R. Brandes
75725c16f4 Merge pull request #390 from DmytroAlipov/fix-original-grade-value
fix: Grade value is displayed in the modal
2024-05-01 12:37:00 -03:00
Dima Alipov
d59a4bf54d fix: grade value is displayed in the modal 2024-04-21 13:38:35 +03:00
Stanislav Lunyachek
18cede45a6 fix: Fix rows counter in the Edit Grade modal window 2024-04-18 23:34:33 +03:00
Adolfo R. Brandes
c3823c39b0 Merge pull request #387 from DmytroAlipov/fix-second-opening-grade
fix: "An unexpected error occurred" after second opening any grade
2024-04-08 14:48:52 -03:00
Adolfo R. Brandes
ef8e20f2b3 Merge pull request #363 from raccoongang/fix/fix-responsive-issues-master
fix: fix sidebar scrolling and adaptation for mobile
2024-04-03 15:27:29 -03:00
Samir Sabri
d683e874b7 feat!: remove Transifex calls for OEP-58 2024-03-18 14:38:42 -04:00
Dima Alipov
4e9270ab8e fix: "An unexpected error occurred" after second opening any grade 2024-03-16 18:57:48 +02:00
Adolfo R. Brandes
6450d8648b Merge pull request #383 from brian-smith-tcril/update-build-paragon
chore(deps): update paragon and frontend-build to openedx scope
2024-02-27 11:46:22 -03:00
Brian Smith
9c7c848df5 chore(deps): update paragon and frontend-build to openedx scope 2024-02-23 14:27:48 -05:00
Omar Al-Ithawi
9ad10108ec feat: tutor-mfe compatiblilty for atlas pull (#379)
- install atlas
 - remove `--filter` to pull all languages by default
 - use ATLAS_OPTIONS to allow custom `--filter`
 - include frontend-platform in `atlas pull`

Refs: FC-0012 OEP-58
2024-02-02 11:15:59 -05:00
Syed Ali Abbas Zaidi
338498543a feat: migrate enzyme to react-unit-test-utils (#378)
* feat: migrate enzyme to react-unit-test-utils

* refactor: remove unnecessary usage of shallow wrapper
2024-01-30 12:31:48 +05:00
Adolfo R. Brandes
788476193c Merge pull request #375 from arbrandes/fix-route
fix: Route with PUBLIC_PATH
2023-12-15 13:23:34 -03:00
Adolfo R. Brandes
cb658776c6 fix: Route with PUBLIC_PATH
Prior to this change, the MFE would fail to render with any PUBLIC_PATH
set.  Because that is now handled entirely by `frontend-platform`, we
can avoid referring to PUBLIC_PATH entirely, here.
2023-12-08 16:32:16 -03:00
Mashal Malik
a4eff6991f refactor: updated README file to reflect template changes (#369) 2023-10-26 08:47:40 -04:00
ihor-romaniuk
f1c9140c8e fix: fix sidebar scrolling and adaptation for mobile
- fix text overload in heading
- fix appearance for sidebar with a short main content
- fix transformation of search and filter button on mobile
- fix extra empty space below main content table
- fix adaptation modal content to mobile view
2023-10-23 15:53:35 +03:00
Feanil Patel
ba9bd466a3 chore: Update to the new version of brand-openedx in the new scope. (#359)
Part of https://github.com/openedx/axim-engineering/issues/23

This updates the `@edx/brand` alias to point to the `brand-openedx` package at
the `openedx` scope. This does not impact imports because this package is used
via an alias.
2023-10-20 17:23:36 -04:00
Mashal Malik
e44f5dde44 refactor: update lock file version (#347) 2023-10-19 10:18:44 +05:00
Syed Ali Abbas Zaidi
d46ce000bb chore: bump frontend-platform (#356) 2023-10-16 13:11:40 +05:00
Muhammad Abdullah Waheed
bf3b37caa4 feat: babel-plugin-react-intl to babel-plugin-formatjs migration (#354)
* feat: babel-plugin-react-intl to babel-plugin-formatjs migration

* fix: upgraded frontend-build to fix security issue
2023-10-11 13:31:57 -04:00
0x29a
295048b4e9 fix: add limited_staff to allowedRoles 2023-10-10 10:29:21 -04:00
Diana Catalina Olarte
1c70458590 fix: replace Field.Email reference for Field.Text 2023-09-25 11:10:13 -03:00
Syed Ali Abbas Zaidi
247e9f3668 feat: upgrade react router to v6 (#319)
* feat: upgrade react router to v6

* refactor: removed remaining router v5 code

* refactor: improve code coverage
2023-09-18 11:27:40 -04:00
150 changed files with 7229 additions and 15613 deletions

View File

@@ -7,7 +7,6 @@ LOGOUT_URL='http://localhost:18000/login'
LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
LOGO_POWERED_BY_OPEN_EDX_URL_SVG=https://edx-cdn.org/v3/stage/open-edx-tag.svg
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'

View File

@@ -1,5 +1,5 @@
// eslint-disable-next-line import/no-extraneous-dependencies
const { createConfig } = require('@edx/frontend-build');
const { createConfig } = require('@openedx/frontend-build');
const config = createConfig('eslint', {
rules: {

7
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
version: 2
updates:
# Adding new check for github-actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

33
.github/renovate.json vendored Normal file
View File

@@ -0,0 +1,33 @@
{
"extends": [
"config:base",
"schedule:weekly",
":automergeLinters",
":automergeMinor",
":automergeTesters",
":enableVulnerabilityAlerts",
":rebaseStalePrs",
":semanticCommits",
":updateNotScheduled"
],
"packageRules": [
{
"matchDepTypes": [
"devDependencies"
],
"matchUpdateTypes": [
"lockFileMaintenance",
"minor",
"patch",
"pin"
],
"automerge": true
},
{
"matchPackagePatterns": ["@edx", "@openedx"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
}
],
"timezone": "America/New_York"
}

View File

@@ -10,19 +10,16 @@ on:
jobs:
test:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Nodejs Env
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
uses: actions/checkout@v4
- name: Setup Nodejs
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VER }}
node-version-file: '.nvmrc'
- name: Install dependencies
run: npm ci
@@ -43,11 +40,14 @@ jobs:
run: npm run build
- name: Run Coverage
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
- name: Send failure notification
if: ${{ failure() }}
uses: dawidd6/action-send-mail@v3
uses: dawidd6/action-send-mail@v4
with:
server_address: email-smtp.us-east-1.amazonaws.com
server_port: 465

View File

@@ -1,36 +0,0 @@
name: Release CI
on:
push:
tags:
- '*'
jobs:
release:
name: Release
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup Nodejs Env
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: ${{ env.NODE_VER }}
- name: Install dependencies
run: npm ci
- name: Create Build
run: npm run build
- name: Release Package
env:
GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.SEMANTIC_RELEASE_NPM_TOKEN }}
run: npm semantic-release

2
.gitignore vendored
View File

@@ -23,3 +23,5 @@ temp/babel-plugin-react-intl
### transifex ###
src/i18n/transifex_input.json
temp
src/i18n/messages/

2
.nvmrc
View File

@@ -1 +1 @@
18.15
20

View File

@@ -1,27 +0,0 @@
{
"branch": "master",
"tagFormat": "v${version}",
"verifyConditions": [
"@semantic-release/npm",
{
"path": "@semantic-release/github",
"assets": {
"path": "dist/*"
}
}
],
"analyzeCommits": "@semantic-release/commit-analyzer",
"generateNotes": "@semantic-release/release-notes-generator",
"prepare": "@semantic-release/npm",
"publish": [
"@semantic-release/npm",
{
"path": "@semantic-release/github",
"assets": {
"path": "dist/*"
}
}
],
"success": [],
"fail": []
}

View File

@@ -1,9 +0,0 @@
[main]
host = https://www.transifex.com
[o:open-edx:p:edx-platform:r:frontend-app-gradebook]
file_filter = src/i18n/messages/<lang>.json
source_file = src/i18n/transifex_input.json
source_lang = en
type = KEYVALUEJSON

View File

@@ -1,18 +1,14 @@
npm-install-%: ## install specified % npm package
npm install $* --save-dev
npm ci $* --save-dev
git add package.json
export TRANSIFEX_RESOURCE = frontend-app-gradebook
transifex_langs = "ar,de,es_419,fa_IR,fr,fr_CA,hi,it,pt,ru,uk,zh_CN"
intl_imports = ./node_modules/.bin/intl-imports.js
transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json
tx_url1 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/translation/en/strings/
tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/source/
# This directory must match .babelrc .
transifex_temp = ./temp/babel-plugin-react-intl
transifex_temp = ./temp/babel-plugin-formatjs
NPM_TESTS=build i18n_extract lint test is-es5
@@ -39,40 +35,20 @@ i18n.concat:
extract_translations: | requirements i18n.extract i18n.concat
# Despite the name, we actually need this target to detect changes in the incoming translated message files as well.
detect_changed_source_translations:
# Checking for changed translations...
git diff --exit-code $(i18n)
# Pushes translations to Transifex. You must run make extract_translations first.
push_translations:
# Pushing strings to Transifex...
tx push -s
# Fetching hashes from Transifex...
./node_modules/@edx/reactifex/bash_scripts/get_hashed_strings_v3.sh
# Writing out comments to file...
$(transifex_utils) $(transifex_temp) --comments --v3-scripts-path
# Pushing comments to Transifex...
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
ifeq ($(OPENEDX_ATLAS_PULL),)
# Pulls translations from Transifex.
pull_translations:
tx pull -t -f --mode reviewed --languages=$(transifex_langs)
else
# Experimental: OEP-58 Pulls translations using atlas
pull_translations:
rm -rf src/i18n/messages
mkdir src/i18n/messages
cd src/i18n/messages \
&& atlas pull --filter=$(transifex_langs) \
&& atlas pull $(ATLAS_OPTIONS) \
translations/frontend-component-footer/src/i18n/messages:frontend-component-footer \
translations/frontend-component-header/src/i18n/messages:frontend-component-header \
translations/frontend-platform/src/i18n/messages:frontend-platform \
translations/paragon/src/i18n/messages:paragon \
translations/frontend-app-gradebook/src/i18n/messages:frontend-app-gradebook
$(intl_imports) paragon frontend-component-header frontend-component-footer frontend-app-gradebook
endif
$(intl_imports) frontend-platform paragon frontend-component-header frontend-component-footer frontend-app-gradebook
# This target is used by CI.
validate-no-uncommitted-package-lock-changes:

View File

@@ -1,3 +1,5 @@
# frontend-app-gradebook
[![Build Status](https://api.travis-ci.com/edx/frontend-app-gradebook.svg?branch=master)](https://travis-ci.com/edx/frontend-app-gradebook)
[![Codecov](https://img.shields.io/codecov/c/gh/openedx/frontend-app-gradebook)](https://app.codecov.io/gh/openedx/frontend-app-gradebook)
[![npm_version](https://img.shields.io/npm/v/@edx/frontend-app-gradebook.svg)](@edx/frontend-app-gradebook)
@@ -5,7 +7,7 @@
[![license](https://img.shields.io/npm/l/@edx/frontend-app-gradebook.svg)](@edx/frontend-app-gradebook)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
# Gradebook
# Purpose
Gradebook allows course staff to view, filter, and override subsection grades for a course. Additionally for Masters courses, Gradebook enables bulk management of subsection grades.
@@ -47,7 +49,8 @@ depending on their needs. Instructors that expect to review grades infrequently
to the problem in question will have a worse UX than the legacy gradebook provides. Instructors that rely on the graphs
generated by the current gradebook might find the lack of autogenerated graphs to be frustrating.
## Quickstart
## Getting Started
### Installation
@@ -55,6 +58,20 @@ To install gradebook into your project:
```
npm i --save @edx/frontend-app-gradebook
```
Cloning and Startup
===================
1. Clone your new repo:
``git clone https://github.com/openedx/frontend-app-gradebook.git``
2. Install npm dependencies:
``cd frontend-app-gradebook && npm install``
3. Start the dev server:
``npm start``
## Running the UI Standalone
@@ -91,6 +108,11 @@ check the ``enabled`` and ``enabled for all courses`` boxes.
numbers for grades. If your gradebook isn't accepting your changes, or the changes aren't resulting in sane,
recalculated grade values, verify you've set all flags correctly.
## Plugins
This MFE can be customized using [Frontend Plugin Framework](https://github.com/openedx/frontend-plugin-framework).
The parts of this MFE that can be customized in that manner are documented [here](/src/plugin-slots).
## Running tests
1. Assuming that you're operating in the context of the edX devstack,
@@ -119,3 +141,57 @@ running gradebook container.
## Authentication with backend API services
See the [`@edx/frontend-auth`](https://github.com/edx-unsupported/frontend-auth) repo for information about securing routes in your application that require user authentication.
License
=======
The code in this repository is licensed under the AGPLv3 unless otherwise
noted.
Contributing
============
Contributions are very welcome. Please read `How To Contribute`_ for details.
.. _How To Contribute: https://openedx.org/r/how-to-contribute
This project is currently accepting all types of contributions, bug fixes,
security fixes, maintenance work, or new features. However, please make sure
to have a discussion about your new feature idea with the maintainers prior to
beginning development to maximize the chances of your change being accepted.
You can start a conversation by creating a new issue on this repo summarizing
your idea.
Getting Help
===========
If you're having trouble, we have discussion forums at
https://discuss.openedx.org where you can connect with others in the community.
Our real-time conversations are on Slack. You can request a `Slack
invitation`_, then join our `community Slack workspace`_. Because this is a
frontend repository, the best place to discuss it would be in the `#wg-frontend
channel`_.
For anything non-trivial, the best path is to open an issue in this repository
with as many details about the issue you are facing as you can provide.
https://github.com/openedx/frontend-app-gradebook/issues
For more information about these options, see the `Getting Help`_ page.
.. _Slack invitation: https://openedx.org/slack
.. _community Slack workspace: https://openedx.slack.com/
.. _#wg-frontend channel: https://openedx.slack.com/archives/C04BM6YC7A6
.. _Getting Help: https://openedx.org/community/connect
The Open edX Code of Conduct
============================
All community members are expected to follow the `Open edX Code of Conduct`_.
.. _Open edX Code of Conduct: https://openedx.org/code-of-conduct/
Reporting Security Issues
=========================
Please do not report security issues in public. Please email security@openedx.org.

View File

@@ -1,3 +1,3 @@
const { createConfig } = require('@edx/frontend-build');
const { createConfig } = require('@openedx/frontend-build');
module.exports = createConfig('babel');

13
catalog-info.yaml Normal file
View File

@@ -0,0 +1,13 @@
# This file records information about this repo. Its use is described in OEP-55:
# https://open-edx-proposals.readthedocs.io/en/latest/processes/oep-0055-proc-project-maintainers.html
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: "frontend-app-gradebook"
description: "The frontend (MFE) for Open edX Gradebook"
annotations:
openedx.org/arch-interest-groups: ""
spec:
owner: user:farhaanbukhsh
type: 'website'
lifecycle: 'experimental'

View File

@@ -1,13 +1,10 @@
const { createConfig } = require('@edx/frontend-build');
const { createConfig } = require('@openedx/frontend-build');
module.exports = createConfig('jest', {
setupFilesAfterEnv: [
'<rootDir>/src/setupTest.js',
],
modulePaths: ['<rootDir>/src/'],
snapshotSerializers: [
'enzyme-to-json/serializer',
],
coveragePathIgnorePatterns: [
'src/segment.js',
'src/postcss.config.js',

20291
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@
"scripts": {
"build": "fedx-scripts webpack",
"is-es5": "es-check es5 ./dist/*.js",
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"i18n_extract": "fedx-scripts formatjs extract",
"lint": "fedx-scripts eslint --ext .jsx,.js src/",
"lint-fix": "fedx-scripts eslint --fix --ext .jsx,.js src/",
"prepush": "npm run lint",
@@ -28,24 +28,24 @@
"extends @edx/browserslist-config"
],
"dependencies": {
"@edx/brand": "npm:@edx/brand-openedx@^1.2.0",
"@edx/frontend-component-footer": "12.1.0",
"@edx/frontend-component-header": "4.3.0",
"@edx/frontend-platform": "4.6.0",
"@edx/paragon": "20.45.0",
"@edx/react-unit-test-utils": "1.7.0",
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/frontend-component-header": "^5.6.0",
"@edx/frontend-platform": "8.0.0",
"@edx/openedx-atlas": "^0.6.0",
"@edx/react-unit-test-utils": "^3.0.0",
"@edx/reactifex": "^2.1.1",
"@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-brands-svg-icons": "^5.11.2",
"@fortawesome/free-solid-svg-icons": "^5.11.2",
"@fortawesome/react-fontawesome": "^0.1.5",
"@openedx/frontend-plugin-framework": "^1.2.1",
"@openedx/frontend-slot-footer": "^1.0.2",
"@openedx/paragon": "^22.1.1",
"@redux-beacon/segment": "^1.0.0",
"@reduxjs/toolkit": "^1.5.1",
"classnames": "^2.2.6",
"core-js": "3.6.5",
"email-prop-type": "^1.1.7",
"enzyme": "^3.10.0",
"enzyme-to-json": "^3.6.2",
"font-awesome": "4.7.0",
"history": "4.10.1",
"prop-types": "15.8.1",
@@ -54,8 +54,8 @@
"react-dom": "17.0.2",
"react-helmet": "^6.1.0",
"react-redux": "^7.2.9",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",
"react-router": "6.15.0",
"react-router-dom": "6.15.0",
"react-router-redux": "^5.0.0-alpha.9",
"redux": "4.0.5",
"redux-beacon": "^2.1.0",
@@ -68,20 +68,16 @@
},
"devDependencies": {
"@edx/browserslist-config": "^1.1.1",
"@edx/frontend-build": "12.9.3",
"@openedx/frontend-build": "14.0.3",
"@testing-library/react": "12.1.5",
"@wojtekmaj/enzyme-adapter-react-17": "0.8.0",
"axios": "0.21.2",
"axios-mock-adapter": "^1.17.0",
"es-check": "^2.3.0",
"fetch-mock": "^6.5.2",
"fetch-mock": "^12.2.0",
"husky": "2.7.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^26.6.3",
"jest": "^29.7.0",
"react-dev-utils": "^12.0.1",
"react-test-renderer": "17.0.2",
"reactifex": "1.1.1",
"redux-mock-store": "^1.5.3",
"semantic-release": "^19.0.3"
"redux-mock-store": "^1.5.3"
}
}

View File

@@ -1,12 +1,11 @@
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Route, Routes } from 'react-router-dom';
import { AppProvider } from '@edx/frontend-platform/react';
import Footer from '@edx/frontend-component-footer';
import FooterSlot from '@openedx/frontend-slot-footer';
import Header from '@edx/frontend-component-header';
import { routePath } from 'data/constants/app';
import store from 'data/store';
import GradebookPage from 'containers/GradebookPage';
import './App.scss';
@@ -15,21 +14,18 @@ import Head from './head/Head';
const App = () => (
<AppProvider store={store}>
<Head />
<Router>
<div>
<Header />
<main>
<Switch>
<Route
exact
path={routePath}
component={GradebookPage}
/>
</Switch>
</main>
<Footer logo={process.env.LOGO_POWERED_BY_OPEN_EDX_URL_SVG} />
</div>
</Router>
<div>
<Header />
<main>
<Routes>
<Route
path="/:courseId"
element={<GradebookPage />}
/>
</Routes>
</main>
<FooterSlot />
</div>
</AppProvider>
);

View File

@@ -1,7 +1,7 @@
// frontend-app-*/src/index.scss
@import "~@edx/brand/paragon/fonts";
@import "~@edx/brand/paragon/variables";
@import "~@edx/paragon/scss/core/core";
@import "~@openedx/paragon/scss/core/core";
@import "~@edx/brand/paragon/overrides";
$fa-font-path: "~font-awesome/fonts";

View File

@@ -1,80 +1,63 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { AppProvider } from '@edx/frontend-platform/react';
import { Route } from 'react-router-dom';
import Footer from '@edx/frontend-component-footer';
import Header from '@edx/frontend-component-header';
import { routePath } from 'data/constants/app';
import store from 'data/store';
import GradebookPage from 'containers/GradebookPage';
import App from './App';
import Head from './head/Head';
jest.mock('react-router-dom', () => ({
BrowserRouter: () => 'BrowserRouter',
Route: () => 'Route',
Switch: () => 'Switch',
Routes: () => 'Routes',
}));
jest.mock('@edx/frontend-platform/react', () => ({
AppProvider: () => 'AppProvider',
}));
jest.mock('data/constants/app', () => ({
routePath: '/:courseId',
}));
jest.mock('@edx/frontend-component-footer', () => 'Footer');
jest.mock('@edx/frontend-component-footer', () => ({ FooterSlot: 'Footer' }));
jest.mock('data/store', () => 'testStore');
jest.mock('containers/GradebookPage', () => 'GradebookPage');
jest.mock('@edx/frontend-component-header', () => 'Header');
jest.mock('./head/Head', () => 'Head');
const logo = 'fakeLogo.png';
let el;
let router;
let secondChild;
describe('App router component', () => {
test('snapshot', () => {
expect(shallow(<App />)).toMatchSnapshot();
expect(shallow(<App />).snapshot).toMatchSnapshot();
});
describe('component', () => {
beforeEach(() => {
process.env.LOGO_POWERED_BY_OPEN_EDX_URL_SVG = logo;
el = shallow(<App />);
router = el.childAt(1);
secondChild = el.instance.children;
});
describe('AppProvider', () => {
test('AppProvider is the parent component, passed the redux store props', () => {
expect(el.type()).toBe(AppProvider);
expect(el.props().store).toEqual(store);
expect(el.instance.type).toBe('AppProvider');
expect(el.instance.props.store).toEqual(store);
});
});
describe('Head', () => {
test('first child of AppProvider', () => {
expect(el.childAt(0).type()).toBe(Head);
expect(el.instance.children[0].type).toBe('Head');
});
});
describe('Router', () => {
test('second child of AppProvider', () => {
expect(router.type()).toBe(Router);
expect(secondChild[1].type).toBe('div');
});
test('Header is above/outside-of the routing', () => {
expect(router.childAt(0).childAt(0).type()).toBe(Header);
expect(router.childAt(0).childAt(1).type()).toBe('main');
expect(secondChild[1].children[0].type).toBe('Header');
expect(secondChild[1].children[1].type).toBe('main');
});
test('Routing - GradebookPage is only route', () => {
expect(router.find('main')).toEqual(shallow(
<main>
<Switch>
<Route exact path={routePath} component={GradebookPage} />
</Switch>
</main>,
));
expect(secondChild[1].findByType(Route)).toHaveLength(1);
expect(secondChild[1].findByType(Route)[0].props.path).toEqual('/:courseId');
expect(secondChild[1].findByType(Route)[0].props.element.type).toEqual(GradebookPage);
});
});
test('Footer logo drawn from env variable', () => {
expect(router.find(Footer).props().logo).toEqual(logo);
});
});
});

View File

@@ -5,20 +5,17 @@ exports[`App router component snapshot 1`] = `
store="testStore"
>
<Head />
<BrowserRouter>
<div>
<Header />
<main>
<Switch>
<Route
component="GradebookPage"
exact={true}
path="/:courseId"
/>
</Switch>
</main>
<Footer />
</div>
</BrowserRouter>
<div>
<Header />
<main>
<Routes>
<Route
element={<GradebookPage />}
path="/:courseId"
/>
</Routes>
</main>
<FooterSlot />
</div>
</AppProvider>
`;

View File

@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { connect } from 'react-redux';
import { Alert } from '@edx/paragon';
import { Alert } from '@openedx/paragon';
import selectors from 'data/selectors';
import messages from './messages';

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Alert } from '@edx/paragon';
import { shallow } from '@edx/react-unit-test-utils';
import { Alert } from '@openedx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import selectors from 'data/selectors';
@@ -12,7 +12,7 @@ jest.mock('@edx/frontend-platform/i18n', () => ({
defineMessages: m => m,
FormattedMessage: () => 'FormattedMessage',
}));
jest.mock('@edx/paragon', () => ({
jest.mock('@openedx/paragon', () => ({
Alert: () => 'Alert',
}));
jest.mock('data/selectors', () => ({
@@ -35,17 +35,17 @@ describe('BulkManagementAlerts', () => {
el = shallow(<BulkManagementAlerts />);
});
test('snapshot - bulkImportError closed, success closed', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('closed danger alert', () => {
expect(el.childAt(0).is(Alert)).toEqual(true);
expect(el.childAt(0).props().show).toEqual(false);
expect(el.childAt(0).props().variant).toEqual('danger');
expect(el.instance.children[0].type).toBe('Alert');
expect(el.instance.findByType(Alert)[0].props.show).toEqual(false);
expect(el.instance.findByType(Alert)[0].props.variant).toEqual('danger');
});
test('closed success alert', () => {
expect(el.childAt(1).is(Alert)).toEqual(true);
expect(el.childAt(1).props().show).toEqual(false);
expect(el.childAt(1).props().variant).toEqual('success');
expect(el.instance.children[1].type).toBe('Alert');
expect(el.instance.findByType(Alert)[1].props.show).toEqual(false);
expect(el.instance.findByType(Alert)[1].props.variant).toEqual('success');
});
});
describe('no errer, no upload success', () => {
@@ -57,19 +57,19 @@ describe('BulkManagementAlerts', () => {
'success alert open with messages.successDialog',
];
test(`snapshot - ${assertions.join(', ')}`, () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('open danger alert with bulkImportError content', () => {
expect(el.childAt(0).is(Alert)).toEqual(true);
expect(el.childAt(0).children().text()).toEqual(errorMessage);
expect(el.childAt(0).props().show).toEqual(true);
expect(el.instance.children[0].type).toBe('Alert');
expect(el.instance.findByType(Alert)[0].children[0].el).toEqual(errorMessage);
expect(el.instance.findByType(Alert)[0].props.show).toEqual(true);
});
test('open success alert with messages.successDialog content', () => {
expect(el.childAt(1).is(Alert)).toEqual(true);
expect(el.childAt(1).children().getElement()).toEqual(
expect(el.instance.children[1].type).toBe('Alert');
expect(el.shallowWrapper.props.children[1].props.children).toEqual(
<FormattedMessage {...messages.successDialog} />,
);
expect(el.childAt(1).props().show).toEqual(true);
expect(el.instance.children[1].props.show).toEqual(true);
});
});
});

View File

@@ -3,7 +3,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { DataTable } from '@edx/paragon';
import { DataTable } from '@openedx/paragon';
import { bulkManagementColumns } from 'data/constants/app';
import selectors from 'data/selectors';

View File

@@ -1,7 +1,7 @@
/* eslint-disable import/no-named-as-default */
import React from 'react';
import { shallow } from 'enzyme';
import { DataTable } from '@edx/paragon';
import { shallow } from '@edx/react-unit-test-utils';
import { DataTable } from '@openedx/paragon';
import selectors from 'data/selectors';
import { bulkManagementColumns } from 'data/constants/app';
@@ -9,7 +9,7 @@ import { bulkManagementColumns } from 'data/constants/app';
import ResultsSummary from './ResultsSummary';
import { HistoryTable, mapStateToProps } from './HistoryTable';
jest.mock('@edx/paragon', () => ({ DataTable: () => 'DataTable' }));
jest.mock('@openedx/paragon', () => ({ DataTable: () => 'DataTable' }));
jest.mock('@edx/frontend-platform/i18n', () => ({
defineMessages: m => m,
@@ -56,12 +56,12 @@ describe('HistoryTable', () => {
el = shallow(<HistoryTable {...props} />);
});
test('snapshot - loads formatted table', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
describe('history table', () => {
let table;
beforeEach(() => {
table = el.find(DataTable);
table = el.instance.findByType(DataTable);
});
describe('data (from bulkManagementHistory.map(this.formatHistoryRow)', () => {
const fieldAssertions = [
@@ -70,10 +70,10 @@ describe('HistoryTable', () => {
'forwards the rest',
];
test(`snapshot: ${fieldAssertions.join(', ')}`, () => {
expect(table.props().data).toMatchSnapshot();
expect(table[0].props.data).toMatchSnapshot();
});
test(fieldAssertions.join(', '), () => {
const rows = table.props().data;
const rows = table[0].props.data;
expect(rows[0].resultsSummary).toEqual(<ResultsSummary {...entry1.resultsSummary} />);
expect(rows[0].user).toEqual(<span className="wrap-text-in-cell">{entry1.user}</span>);
expect(
@@ -87,7 +87,7 @@ describe('HistoryTable', () => {
});
});
test('columns from bulkManagementColumns', () => {
expect(table.props().columns).toEqual(bulkManagementColumns);
expect(table[0].props.columns).toEqual(bulkManagementColumns);
});
});
});

View File

@@ -2,8 +2,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Hyperlink, Icon } from '@edx/paragon';
import { Download } from '@edx/paragon/icons';
import { Hyperlink, Icon } from '@openedx/paragon';
import { Download } from '@openedx/paragon/icons';
import lms from 'data/services/lms';

View File

@@ -1,17 +1,16 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { Icon } from '@edx/paragon';
import { Download } from '@edx/paragon/icons';
import { Download } from '@openedx/paragon/icons';
import lms from 'data/services/lms';
import ResultsSummary from './ResultsSummary';
jest.mock('@edx/paragon', () => ({
jest.mock('@openedx/paragon', () => ({
Hyperlink: () => 'Hyperlink',
Icon: () => 'Icon',
}));
jest.mock('@edx/paragon/icons', () => ({
jest.mock('@openedx/paragon/icons', () => ({
Download: 'DownloadIcon',
}));
jest.mock('data/services/lms', () => ({
@@ -35,19 +34,19 @@ describe('ResultsSummary component', () => {
el = shallow(<ResultsSummary {...props} />);
});
test(`snapshot - ${assertions.join(', ')}`, () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('Hyperlink has target="_blank" and rel="noopener noreferrer"', () => {
expect(el.props().target).toEqual('_blank');
expect(el.props().rel).toEqual('noopener noreferrer');
expect(el.instance.props.target).toEqual('_blank');
expect(el.instance.props.rel).toEqual('noopener noreferrer');
});
test('Hyperlink has href to bulkGradesUrl', () => {
expect(el.props().href).toEqual(lms.urls.bulkGradesUrlByRow(props.rowId));
expect(el.instance.props.href).toEqual(lms.urls.bulkGradesUrlByRow(props.rowId));
});
test('displays Download Icon and text', () => {
const icon = el.childAt(0);
expect(icon.is(Icon)).toEqual(true);
expect(icon.props().src).toEqual(Download);
expect(el.childAt(1).text()).toEqual(props.text);
const icon = el.instance.children[0];
expect(icon.type).toEqual('Icon');
expect(icon.props.src).toEqual(Download);
expect(el.instance.children[1].el).toEqual(props.text);
});
});

View File

@@ -1,8 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`HistoryTable component snapshot history table data (from bulkManagementHistory.map(this.formatHistoryRow) snapshot: maps resultsSummay to ResultsSummary, wraps filename and user, forwards the rest 1`] = `
Array [
Object {
[
{
"filename": <span
className="wrap-text-in-cell"
>
@@ -20,7 +20,7 @@ Array [
Eifel
</span>,
},
Object {
{
"filename": <span
className="wrap-text-in-cell"
>
@@ -45,26 +45,26 @@ exports[`HistoryTable component snapshot snapshot - loads formatted table 1`] =
<DataTable
className="table-striped"
columns={
Array [
Object {
[
{
"Header": "Gradebook",
"accessor": "filename",
"columnSortable": false,
"width": "col-5",
},
Object {
{
"Header": "Download Summary",
"accessor": "resultsSummary",
"columnSortable": false,
"width": "col",
},
Object {
{
"Header": "Who",
"accessor": "user",
"columnSortable": false,
"width": "col-1",
},
Object {
{
"Header": "When",
"accessor": "timeUploaded",
"columnSortable": false,
@@ -73,8 +73,8 @@ exports[`HistoryTable component snapshot snapshot - loads formatted table 1`] =
]
}
data={
Array [
Object {
[
{
"filename": <span
className="wrap-text-in-cell"
>
@@ -92,7 +92,7 @@ exports[`HistoryTable component snapshot snapshot - loads formatted table 1`] =
Eifel
</span>,
},
Object {
{
"filename": <span
className="wrap-text-in-cell"
>

View File

@@ -4,8 +4,8 @@ exports[`ResultsSummary component snapshot - safe hyperlink with bulkGradesUrl w
<Hyperlink
destination="www.edx.org"
href={
Object {
"url": Object {
{
"url": {
"rowId": 42,
},
}

View File

@@ -1,6 +1,6 @@
/* eslint-disable import/no-named-as-default */
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { BulkManagementHistoryView } from '.';
@@ -24,20 +24,23 @@ describe('BulkManagementHistoryView', () => {
'<HistoryTable />',
];
test(`snapshot - loads ${snapshotSegments.join(', ')}`, () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('heading - h4 loaded from messages', () => {
const heading = el.find('h4');
expect(heading.getElement()).toEqual((
const heading = el.instance.findByType('h4')[0];
const expectedHeading = shallow(
<h4>
<FormattedMessage {...messages.heading} />
</h4>
));
</h4>,
);
expect(heading.el.type).toEqual(expectedHeading.type);
expect(heading.el.props).toEqual(expectedHeading.props);
});
test('heading, then alerts, then upload form, then table', () => {
expect(el.childAt(0).is('h4')).toEqual(true);
expect(el.childAt(2).is(BulkManagementAlerts)).toEqual(true);
expect(el.childAt(3).is(HistoryTable)).toEqual(true);
expect(el.instance.children[0].type).toEqual('h4');
expect(el.instance.children[2].type).toEqual(BulkManagementAlerts);
expect(el.instance.children[3].type).toEqual(HistoryTable);
});
});
});

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Hyperlink } from '@edx/paragon';
import { Hyperlink } from '@openedx/paragon';
import { getConfig } from '@edx/frontend-platform';
/**

View File

@@ -1,11 +1,11 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { getConfig } from '@edx/frontend-platform';
import Header from '.';
jest.mock('@edx/paragon', () => ({
jest.mock('@openedx/paragon', () => ({
Hyperlink: () => 'Hyperlink',
}));
jest.mock('@edx/frontend-platform', () => ({
@@ -16,6 +16,6 @@ describe('Header', () => {
test('snapshot - has edx link with logo url', () => {
const url = 'www.ourLogo.url';
getConfig.mockReturnValue({ LOGO_URL: url });
expect(shallow(<Header />)).toMatchSnapshot();
expect(shallow(<Header />).snapshot).toMatchSnapshot();
});
});

View File

@@ -10,7 +10,7 @@ exports[`AssignmentFilter component render snapshot 1`] = `
label="Assignment"
onChange={[MockFunction]}
options={
Array [
[
<option
value=""
>

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
import SelectGroup from '../SelectGroup';
@@ -38,10 +38,10 @@ describe('AssignmentFilter component', () => {
});
describe('render', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('filter options', () => {
const { options } = el.find(SelectGroup).props();
const { options } = el.instance.findByType(SelectGroup)[0].props;
expect(options.length).toEqual(5);
const testOption = assignmentFilterOptions[0];
const optionProps = options[1].props;

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Button } from '@edx/paragon';
import { Button } from '@openedx/paragon';
import useAssignmentGradeFilterData from './hooks';
import messages from '../messages';

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Button } from '@edx/paragon';
import { Button } from '@openedx/paragon';
import PercentGroup from '../PercentGroup';
import useAssignmentGradeFilterData from './hooks';
@@ -37,20 +37,20 @@ describe('AssignmentFilter component', () => {
describe('render', () => {
describe('with selected assignment', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
it('renders a PercentGroup for both Max and Min filters', () => {
let props = el.find(PercentGroup).at(0).props();
let { props } = el.instance.findByType(PercentGroup)[0];
expect(props.value).toEqual(hookData.assignmentGradeMin);
expect(props.disabled).toEqual(false);
expect(props.onChange).toEqual(hookData.handleSetMin);
props = el.find(PercentGroup).at(1).props();
props = el.instance.findByType(PercentGroup)[1].props;
expect(props.value).toEqual(hookData.assignmentGradeMax);
expect(props.disabled).toEqual(false);
expect(props.onChange).toEqual(hookData.handleSetMax);
});
it('renders a submit button', () => {
const props = el.find(Button).props();
const { props } = el.instance.findByType(Button)[0];
expect(props.disabled).toEqual(false);
expect(props.onClick).toEqual(hookData.handleSubmit);
});
@@ -64,12 +64,12 @@ describe('AssignmentFilter component', () => {
el = shallow(<AssignmentFilter updateQueryParams={updateQueryParams} />);
});
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
it('disables controls', () => {
let props = el.find(PercentGroup).at(0).props();
let { props } = el.instance.findByType(PercentGroup)[0];
expect(props.disabled).toEqual(true);
props = el.find(PercentGroup).at(1).props();
props = el.instance.findByType(PercentGroup)[1].props;
expect(props.disabled).toEqual(true);
});
});

View File

@@ -10,7 +10,7 @@ exports[`AssignmentFilterType component render snapshot 1`] = `
label="Assignment Types"
onChange={[MockFunction]}
options={
Array [
[
<option
value=""
>

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
import SelectGroup from '../SelectGroup';
@@ -34,10 +34,10 @@ describe('AssignmentFilterType component', () => {
});
describe('render', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('filter options', () => {
const { options } = el.find(SelectGroup).props();
const { options } = el.instance.findByType(SelectGroup)[0].props;
expect(options.length).toEqual(5);
const optionProps = options[1].props;
expect(optionProps.value).toEqual(assignmentTypes[0]);

View File

@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import { Button } from '@edx/paragon';
import { Button } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from '../messages';

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Button } from '@edx/paragon';
import { Button } from '@openedx/paragon';
import PercentGroup from '../PercentGroup';
import useCourseGradeFilterData from './hooks';
@@ -42,18 +42,18 @@ describe('CourseFilter component', () => {
describe('render', () => {
describe('with selected assignment', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
it('renders a PercentGroup for both Max and Min filters', () => {
let props = el.find(PercentGroup).at(0).props();
let { props } = el.instance.findByType(PercentGroup)[0];
expect(props.value).toEqual(hookData.min.value);
expect(props.onChange).toEqual(hookData.min.onChange);
props = el.find(PercentGroup).at(1).props();
props = el.instance.findByType(PercentGroup)[1].props;
expect(props.value).toEqual(hookData.max.value);
expect(props.onChange).toEqual(hookData.max.onChange);
});
it('renders a submit button', () => {
const props = el.find(Button).props();
const { props } = el.instance.findByType(Button)[0];
expect(props.disabled).toEqual(false);
expect(props.onClick).toEqual(hookData.handleApplyClick);
});
@@ -64,10 +64,10 @@ describe('CourseFilter component', () => {
el = shallow(<CourseFilter updateQueryParams={updateQueryParams} />);
});
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
it('disables submit', () => {
const props = el.find(Button).props();
const { props } = el.instance.findByType(Button)[0];
expect(props.disabled).toEqual(true);
});
});

View File

@@ -2,7 +2,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Form } from '@edx/paragon';
import { Form } from '@openedx/paragon';
const PercentGroup = ({
id,

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import PercentGroup from './PercentGroup';
@@ -22,11 +22,11 @@ describe('PercentGroup', () => {
describe('snapshots', () => {
test('basic snapshot', () => {
const el = shallow(<PercentGroup {...props} />);
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('disabled', () => {
const el = shallow(<PercentGroup {...props} disabled />);
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
});
});

View File

@@ -2,7 +2,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Form } from '@edx/paragon';
import { Form } from '@openedx/paragon';
const SelectGroup = ({
id,

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import SelectGroup from './SelectGroup';
@@ -27,11 +27,11 @@ describe('SelectGroup', () => {
describe('snapshots', () => {
test('basic snapshot', () => {
const el = shallow(<SelectGroup {...props} />);
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('disabled', () => {
const el = shallow(<SelectGroup {...props} disabled />);
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
});
});

View File

@@ -7,7 +7,7 @@ exports[`StudentGroupsFilter component render snapshot 1`] = `
label="Tracks"
onChange={[MockFunction]}
options={
Array [
[
<option
value="Track-All"
>
@@ -43,7 +43,7 @@ exports[`StudentGroupsFilter component render snapshot 1`] = `
label="Cohorts"
onChange={[MockFunction]}
options={
Array [
[
<option
value="Cohort-All"
>

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
import SelectGroup from '../SelectGroup';
@@ -48,14 +48,14 @@ describe('StudentGroupsFilter component', () => {
});
describe('render', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('track options', () => {
const {
options,
onChange,
value,
} = el.find(SelectGroup).at(0).props();
} = el.instance.findByType(SelectGroup)[0].props;
expect(value).toEqual(props.tracks.value);
expect(onChange).toEqual(props.tracks.handleChange);
expect(options.length).toEqual(5);
@@ -70,7 +70,7 @@ describe('StudentGroupsFilter component', () => {
onChange,
disabled,
value,
} = el.find(SelectGroup).at(1).props();
} = el.instance.findByType(SelectGroup)[1].props;
expect(value).toEqual(props.cohorts.value);
expect(disabled).toEqual(false);
expect(onChange).toEqual(props.cohorts.handleChange);

View File

@@ -6,8 +6,8 @@ import {
Icon,
IconButton,
Form,
} from '@edx/paragon';
import { Close } from '@edx/paragon/icons';
} from '@openedx/paragon';
import { Close } from '@openedx/paragon/icons';
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from './messages';

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Collapsible } from '@edx/paragon';
import { Collapsible } from '@openedx/paragon';
import { formatMessage } from 'testUtils';
@@ -49,10 +49,10 @@ describe('GradebookFilters', () => {
});
describe('render', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('Assignment filters', () => {
expect(el.find(Collapsible).at(0).children()).toMatchObject(shallow(
expect(el.instance.findByType(Collapsible)[0].children[0]).toMatchObject(shallow(
<div>
<AssignmentTypeFilter updateQueryParams={updateQueryParams} />
<AssignmentFilter updateQueryParams={updateQueryParams} />
@@ -61,22 +61,22 @@ describe('GradebookFilters', () => {
));
});
test('CourseGrade filters', () => {
expect(el.find(Collapsible).at(1).children()).toMatchObject(shallow(
expect(el.instance.findByType(Collapsible)[1].children[0]).toMatchObject(shallow(
<CourseGradeFilter updateQueryParams={updateQueryParams} />,
));
});
test('StudentGroups filters', () => {
expect(el.find(Collapsible).at(2).children()).toMatchObject(shallow(
expect(el.instance.findByType(Collapsible)[2].children[0]).toMatchObject(shallow(
<StudentGroupsFilter updateQueryParams={updateQueryParams} />,
));
});
test('includeCourseTeamMembers', () => {
const checkbox = el.find(Collapsible).at(3).children();
expect(checkbox.props()).toEqual({
const checkbox = el.instance.findByType(Collapsible)[3].children[0];
expect(checkbox.props).toEqual({
checked: true,
onChange: hookProps.includeCourseTeamMembers.handleChange,
children: formatMessage(messages.includeCourseTeamMembers),
});
expect(checkbox.children[0].el).toEqual(formatMessage(messages.includeCourseTeamMembers));
});
});
});

View File

@@ -21,7 +21,9 @@ exports[`GradebookHeader component render default view shapshot 1`] = `
<div
className="subtitle-row d-flex justify-content-between align-items-center"
>
<h2>
<h2
className="text-break"
>
test-course-id
</h2>
</div>
@@ -49,7 +51,9 @@ exports[`GradebookHeader component render frozen grades snapshot: show frozen wa
<div
className="subtitle-row d-flex justify-content-between align-items-center"
>
<h2>
<h2
className="text-break"
>
test-course-id
</h2>
</div>
@@ -83,7 +87,9 @@ exports[`GradebookHeader component render show bulk management snapshot: show to
<div
className="subtitle-row d-flex justify-content-between align-items-center"
>
<h2>
<h2
className="text-break"
>
test-course-id
</h2>
<Button
@@ -117,7 +123,9 @@ exports[`GradebookHeader component render user cannot view gradebook snapshot: s
<div
className="subtitle-row d-flex justify-content-between align-items-center"
>
<h2>
<h2
className="text-break"
>
test-course-id
</h2>
</div>

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Button } from '@edx/paragon';
import { Button } from '@openedx/paragon';
import { instructorDashboardUrl } from 'data/services/lms/urls';
import useGradebookHeaderData from './hooks';
@@ -26,7 +26,7 @@ export const GradebookHeader = () => {
</a>
<h1>{formatMessage(messages.gradebook)}</h1>
<div className="subtitle-row d-flex justify-content-between align-items-center">
<h2>{courseId}</h2>
<h2 className="text-break">{courseId}</h2>
{showBulkManagement && (
<Button variant="tertiary" onClick={handleToggleViewClick}>
{formatMessage(toggleViewMessage)}

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Button } from '@edx/paragon';
import { Button } from '@openedx/paragon';
import { formatMessage } from 'testUtils';
import { instructorDashboardUrl } from 'data/services/lms/urls';
@@ -40,7 +40,7 @@ describe('GradebookHeader component', () => {
describe('render', () => {
describe('default view', () => {
test('shapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
});
describe('show bulk management', () => {
@@ -49,10 +49,10 @@ describe('GradebookHeader component', () => {
el = shallow(<GradebookHeader />);
});
test('snapshot: show toggle view message button with handleToggleViewClick method', () => {
expect(el).toMatchSnapshot();
const { onClick, children } = el.find(Button).props();
expect(el.snapshot).toMatchSnapshot();
const { onClick } = el.instance.findByType(Button)[0].props;
expect(onClick).toEqual(hookProps.handleToggleViewClick);
expect(children).toEqual(formatMessage(hookProps.toggleViewMessage));
expect(el.instance.findByType(Button)[0].children[0].el).toEqual(formatMessage(hookProps.toggleViewMessage));
});
});
describe('frozen grades', () => {
@@ -61,7 +61,7 @@ describe('GradebookHeader component', () => {
el = shallow(<GradebookHeader />);
});
test('snapshot: show frozen warning', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
});
describe('user cannot view gradebook', () => {
@@ -70,7 +70,7 @@ describe('GradebookHeader component', () => {
el = shallow(<GradebookHeader />);
});
test('snapshot: show unauthorized warning', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
});
});

View File

@@ -6,7 +6,7 @@ exports[`BulkManagementControls render snapshot - show - network and import butt
>
<NetworkButton
label={
Object {
{
"defaultMessage": "Download Grades",
"description": "A labeled button that allows an admin user to download course grades all at once (in bulk).",
"id": "gradebook.GradesView.BulkManagementControls.bulkManagementLabel",

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import useBulkManagementControlsData from './hooks';
import BulkManagementControls from '.';
@@ -22,7 +22,7 @@ describe('BulkManagementControls', () => {
});
describe('render', () => {
test('snapshot - show - network and import buttons', () => {
expect(shallow(<BulkManagementControls />)).toMatchSnapshot();
expect(shallow(<BulkManagementControls />).snapshot).toMatchSnapshot();
});
test('snapshot - empty if show is not truthy', () => {
useBulkManagementControlsData.mockReturnValueOnce({ ...hookProps, show: false });

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import HistoryHeader from './HistoryHeader';
@@ -11,7 +11,7 @@ describe('HistoryHeader', () => {
};
describe('Component', () => {
test('snapshot', () => {
expect(shallow(<HistoryHeader {...props} />)).toMatchSnapshot();
expect(shallow(<HistoryHeader {...props} />).snapshot).toMatchSnapshot();
});
});
});

View File

@@ -22,7 +22,7 @@ export const HistoryKeys = StrictDict({
*/
export const ModalHeaders = () => {
const { assignmentName, updateUserName } = selectors.app.useModalData();
const { currentGrade, originalGrade } = selectors.grades.useGradeData();
const { gradeOverrideCurrentEarnedGradedOverride, gradeOriginalEarnedGraded } = selectors.grades.useGradeData();
const { formatMessage } = useIntl();
return (
<div>
@@ -39,12 +39,12 @@ export const ModalHeaders = () => {
<HistoryHeader
id={HistoryKeys.originalGrade}
label={formatMessage(messages.originalGradeHeader)}
value={originalGrade}
value={gradeOriginalEarnedGraded}
/>
<HistoryHeader
id={HistoryKeys.currentGrade}
label={formatMessage(messages.currentGradeHeader)}
value={currentGrade}
value={gradeOverrideCurrentEarnedGradedOverride}
/>
</div>
);

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
import { selectors } from 'data/redux/hooks';
@@ -25,8 +25,8 @@ const modalData = {
};
selectors.app.useModalData.mockReturnValue(modalData);
const gradeData = {
currentGrade: 'test-current-grade',
originalGrade: 'test-original-grade',
gradeOverrideCurrentEarnedGradedOverride: 'test-current-grade',
gradeOriginalEarnedGraded: 'test-original-grade',
};
selectors.grades.useGradeData.mockReturnValue(gradeData);
@@ -47,10 +47,10 @@ describe('ModalHeaders', () => {
});
describe('render', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('assignment header', () => {
const headerProps = el.find(HistoryHeader).at(0).props();
const headerProps = el.instance.findByType(HistoryHeader)[0].props;
expect(headerProps).toMatchObject({
id: HistoryKeys.assignment,
label: formatMessage(messages.assignmentHeader),
@@ -58,7 +58,7 @@ describe('ModalHeaders', () => {
});
});
test('student header', () => {
const headerProps = el.find(HistoryHeader).at(1).props();
const headerProps = el.instance.findByType(HistoryHeader)[1].props;
expect(headerProps).toMatchObject({
id: HistoryKeys.student,
label: formatMessage(messages.studentHeader),
@@ -66,19 +66,19 @@ describe('ModalHeaders', () => {
});
});
test('originalGrade header', () => {
const headerProps = el.find(HistoryHeader).at(2).props();
const headerProps = el.instance.findByType(HistoryHeader)[2].props;
expect(headerProps).toMatchObject({
id: HistoryKeys.originalGrade,
label: formatMessage(messages.originalGradeHeader),
value: gradeData.originalGrade,
value: gradeData.gradeOriginalEarnedGraded,
});
});
test('currentGrade header', () => {
const headerProps = el.find(HistoryHeader).at(3).props();
const headerProps = el.instance.findByType(HistoryHeader)[3].props;
expect(headerProps).toMatchObject({
id: HistoryKeys.currentGrade,
label: formatMessage(messages.currentGradeHeader),
value: gradeData.currentGrade,
value: gradeData.gradeOverrideCurrentEarnedGradedOverride,
});
});
});

View File

@@ -5,7 +5,7 @@ exports[`AdjustedGradeInput component render snapshot 1`] = `
<Form.Control
name="adjustedGradeValue"
onChange={[MockFunction hook.onChange]}
type="text"
type="number"
value="test-value"
/>
some-hint-text

View File

@@ -15,6 +15,7 @@ const useAdjustedGradeInputData = () => {
value,
onChange,
hintText,
possibleGrade,
};
};

View File

@@ -1,8 +1,10 @@
import React from 'react';
import { Form } from '@edx/paragon';
import { Form } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import useAdjustedGradeInputData from './hooks';
import messages from '../messages';
/**
* <AdjustedGradeInput />
@@ -14,16 +16,18 @@ export const AdjustedGradeInput = () => {
value,
onChange,
hintText,
possibleGrade,
} = useAdjustedGradeInputData();
const { formatMessage } = useIntl();
return (
<span>
<Form.Control
type="text"
type="number"
name="adjustedGradeValue"
value={value}
onChange={onChange}
/>
{hintText}
{value > possibleGrade ? <div style={{ color: 'red' }}>{ formatMessage(messages.adjustedGradeError, { possibleGrade })}</div> : hintText}
</span>
);
};

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { Form } from '@edx/paragon';
import { Form } from '@openedx/paragon';
import useAdjustedGradeInputData from './hooks';
import AdjustedGradeInput from '.';
@@ -28,11 +28,11 @@ describe('AdjustedGradeInput component', () => {
});
describe('render', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
const control = el.find(Form.Control);
expect(control.props().value).toEqual(hookProps.value);
expect(control.props().onChange).toEqual(hookProps.onChange);
expect(el.contains(hookProps.hintText)).toEqual(true);
expect(el.snapshot).toMatchSnapshot();
const control = el.instance.findByType(Form.Control)[0];
expect(control.props.value).toEqual(hookProps.value);
expect(control.props.onChange).toEqual(hookProps.onChange);
expect(el.instance.children[1].el).toContain(hookProps.hintText);
});
});
});

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { Form } from '@edx/paragon';
import { Form } from '@openedx/paragon';
import useReasonInputData from './hooks';

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { Form } from '@edx/paragon';
import { Form } from '@openedx/paragon';
import useReasonInputData from './hooks';
import ReasonInput from '.';
@@ -28,10 +28,10 @@ describe('ReasonInput component', () => {
});
describe('render', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
const control = el.find(Form.Control);
expect(control.props().value).toEqual(hookProps.value);
expect(control.props().onChange).toEqual(hookProps.onChange);
expect(el.snapshot).toMatchSnapshot();
const control = el.instance.findByType(Form.Control)[0];
expect(control.props.value).toEqual(hookProps.value);
expect(control.props.onChange).toEqual(hookProps.onChange);
});
});
});

View File

@@ -5,7 +5,7 @@ import useReasonInputData from './hooks';
import ReasonInput, { controlTestId } from '.';
jest.unmock('react');
jest.unmock('@edx/paragon');
jest.unmock('@openedx/paragon');
jest.mock('./hooks', () => ({ __esModule: true, default: jest.fn() }));
const focus = jest.fn();

View File

@@ -4,22 +4,22 @@ exports[`OverrideTable component render snapshot 1`] = `
<DataTable
columns="test-columns"
data={
Array [
Object {
[
{
"test": "data",
},
Object {
{
"andOther": "test-data",
},
Object {
{
"adjustedGrade": <AdjustedGradeInput />,
"date": Object {
"date": {
"formatted": 2000-01-01T00:00:00.000Z,
},
"reason": <ReasonInput />,
},
]
}
itemCount={2}
itemCount={3}
/>
`;

View File

@@ -9,7 +9,7 @@ const useOverrideTableData = () => {
const { formatMessage } = useIntl();
const hide = selectors.grades.useHasOverrideErrors();
const gradeOverrides = selectors.grades.useGradeData().gradeOverrideHistoryResults;
const gradeOverrides = selectors.grades.useGradeData().gradeOverrideHistoryResults || [];
const tableProps = {};
if (!hide) {
tableProps.columns = [

View File

@@ -1,7 +1,7 @@
/* eslint-disable react/sort-comp, react/button-has-type, import/no-named-as-default */
import React from 'react';
import { DataTable } from '@edx/paragon';
import { DataTable } from '@openedx/paragon';
import { formatDateForDisplay } from 'utils';
@@ -20,18 +20,20 @@ export const OverrideTable = () => {
if (hide) { return null; }
const tableData = [
...data,
{
adjustedGrade: <AdjustedGradeInput />,
date: formatDateForDisplay(new Date()),
reason: <ReasonInput />,
},
];
return (
<DataTable
columns={columns}
data={[
...data,
{
adjustedGrade: <AdjustedGradeInput />,
date: formatDateForDisplay(new Date()),
reason: <ReasonInput />,
},
]}
itemCount={data.length}
data={tableData}
itemCount={tableData.length}
/>
);
};

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { DataTable } from '@edx/paragon';
import { DataTable } from '@openedx/paragon';
import { formatDateForDisplay } from 'utils';
@@ -48,10 +48,10 @@ describe('OverrideTable component', () => {
expect(el.isEmptyRender()).toEqual(true);
});
test('snapshot', () => {
expect(el).toMatchSnapshot();
const table = el.find(DataTable);
expect(table.props().columns).toEqual(hookProps.columns);
const data = [...table.props().data];
expect(el.snapshot).toMatchSnapshot();
const table = el.instance.findByType(DataTable)[0];
expect(table.props.columns).toEqual(hookProps.columns);
const data = [...table.props.data];
const inputRow = data.pop();
const formattedDate = formatDateForDisplay(new Date());
expect(data).toEqual(hookProps.data);

View File

@@ -21,6 +21,11 @@ const messages = defineMessages({
defaultMessage: 'Reason',
description: 'Edit Modal Override Table Reason column header',
},
adjustedGradeError: {
id: 'gradebook.GradesView.EditModal.Overrides.adjustedGradeError',
defaultMessage: 'The value exceeds the maximum grade: {possibleGrade}',
description: 'Edit Modal Override Adjusted Grade Error',
},
});
export default messages;

View File

@@ -36,6 +36,7 @@ exports[`EditModal component render with error snapshot 1`] = `
Cancel
</ModalDialog.CloseButton>
<Button
disabled={false}
onClick={[MockFunction hooks.handleAdjustedGradeClick]}
variant="primary"
>
@@ -80,6 +81,7 @@ exports[`EditModal component render without error snapshot 1`] = `
Cancel
</ModalDialog.CloseButton>
<Button
disabled={false}
onClick={[MockFunction hooks.handleAdjustedGradeClick]}
variant="primary"
>

View File

@@ -5,13 +5,14 @@ import {
Alert,
ModalDialog,
ActionRow,
} from '@edx/paragon';
} from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import OverrideTable from './OverrideTable';
import ModalHeaders from './ModalHeaders';
import useEditModalData from './hooks';
import messages from './messages';
import useAdjustedGradeInputData from './OverrideTable/AdjustedGradeInput/hooks';
/**
* <EditModal />
@@ -30,6 +31,10 @@ export const EditModal = () => {
handleAdjustedGradeClick,
isOpen,
} = useEditModalData();
const {
value,
possibleGrade,
} = useAdjustedGradeInputData();
return (
<ModalDialog
@@ -57,7 +62,7 @@ export const EditModal = () => {
<ModalDialog.CloseButton variant="tertiary">
{formatMessage(messages.closeText)}
</ModalDialog.CloseButton>
<Button variant="primary" onClick={handleAdjustedGradeClick}>
<Button variant="primary" onClick={handleAdjustedGradeClick} disabled={value > possibleGrade}>
{formatMessage(messages.saveGrade)}
</Button>
</ActionRow>

View File

@@ -1,10 +1,10 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import {
ActionRow,
ModalDialog,
} from '@edx/paragon';
} from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import { formatMessage } from 'testUtils';
@@ -14,10 +14,12 @@ import OverrideTable from './OverrideTable';
import useEditModalData from './hooks';
import EditModal from '.';
import messages from './messages';
import useAdjustedGradeInputData from './OverrideTable/AdjustedGradeInput/hooks';
jest.mock('./hooks', () => jest.fn());
jest.mock('./ModalHeaders', () => 'ModalHeaders');
jest.mock('./OverrideTable', () => 'OverrideTable');
jest.mock('./OverrideTable/AdjustedGradeInput/hooks', () => jest.fn());
const hookProps = {
onClose: jest.fn().mockName('hooks.onClose'),
@@ -27,6 +29,12 @@ const hookProps = {
};
useEditModalData.mockReturnValue(hookProps);
const adjustedGradeProps = {
value: 50,
possibleGrade: 100,
};
useAdjustedGradeInputData.mockReturnValue(adjustedGradeProps);
let el;
describe('EditModal component', () => {
beforeEach(() => {
@@ -39,94 +47,103 @@ describe('EditModal component', () => {
});
it('initializes component hooks', () => {
expect(useEditModalData).toHaveBeenCalled();
expect(useAdjustedGradeInputData).toHaveBeenCalled();
});
});
describe('render', () => {
test('modal props', () => {
const modalProps = el.find(ModalDialog).props();
const modalProps = el.instance.findByType(ModalDialog)[0].props;
expect(modalProps.title).toEqual(formatMessage(messages.title));
expect(modalProps.isOpen).toEqual(hookProps.isOpen);
expect(modalProps.onClose).toEqual(hookProps.onClose);
});
const loadBody = () => {
const body = el.find(ModalDialog).children().at(0);
const children = body.find('div').children();
const body = el.instance.findByType(ModalDialog)[0].children[0];
const { children } = body.children[0];
return { body, children };
};
const testBody = () => {
test('type', () => {
const { body } = loadBody();
expect(body.type()).toEqual('ModalDialog.Body');
expect(body.type).toEqual('ModalDialog.Body');
});
test('headers row', () => {
const { children } = loadBody();
expect(children.at(0)).toMatchObject(shallow(<ModalHeaders />));
expect(children[0]).toMatchObject(shallow(<ModalHeaders />));
});
test('table row', () => {
const { children } = loadBody();
expect(children.at(2)).toMatchObject(shallow(<OverrideTable />));
expect(children[2]).toMatchObject(shallow(<OverrideTable />));
});
test('messages', () => {
const { children } = loadBody();
expect(
children.at(3).contains(formatMessage(messages.visibility)),
).toEqual(true);
expect(
children.at(4).contains(formatMessage(messages.saveVisibility)),
).toEqual(true);
expect(children[3].children[0].el).toEqual(formatMessage(messages.visibility));
expect(children[4].children[0].el).toEqual(formatMessage(messages.saveVisibility));
});
};
const testFooter = () => {
let footer;
beforeEach(() => {
footer = el.find(ModalDialog).children().at(1);
footer = el.instance.findByType(ModalDialog)[0].children;
});
test('type', () => {
expect(footer.type()).toEqual('ModalDialog.Footer');
expect(footer[1].type).toEqual('ModalDialog.Footer');
});
test('contains action row', () => {
expect(footer.children().at(0).type()).toEqual('ActionRow');
expect(footer[1].children[0].type).toEqual('ActionRow');
});
test('close button', () => {
const button = footer.find(ActionRow).children().at(0);
expect(button.contains(formatMessage(messages.closeText))).toEqual(true);
expect(button.type()).toEqual('ModalDialog.CloseButton');
const button = footer[1].findByType(ActionRow)[0].children[0];
expect(button.children[0].el).toEqual(formatMessage(messages.closeText));
expect(button.type).toEqual('ModalDialog.CloseButton');
});
test('adjusted grade button', () => {
const button = footer.find(ActionRow).children().at(1);
expect(button.contains(formatMessage(messages.saveGrade))).toEqual(true);
expect(button.type()).toEqual('Button');
expect(button.props().onClick).toEqual(hookProps.handleAdjustedGradeClick);
test('adjusted grade button enabled', () => {
const button = footer[1].findByType(ActionRow)[0].children[1];
expect(button.children[0].el).toEqual(formatMessage(messages.saveGrade));
expect(button.type).toEqual('Button');
expect(button.props.onClick).toEqual(hookProps.handleAdjustedGradeClick);
expect(button.props.disabled).toEqual(false);
});
};
describe('without error', () => {
beforeEach(() => {
useEditModalData.mockReturnValueOnce({ ...hookProps, error: undefined });
useAdjustedGradeInputData.mockReturnValueOnce({ value: 50, possibleGrade: 100 });
el = shallow(<EditModal />);
});
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
testBody();
testFooter();
test('alert row', () => {
const alert = loadBody().children.at(1);
expect(alert.type()).toEqual('Alert');
expect(alert.props().show).toEqual(false);
const alert = loadBody().children[1];
expect(alert.type).toEqual('Alert');
expect(alert.props.show).toEqual(false);
});
});
describe('with error', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
testBody();
test('alert row', () => {
const alert = loadBody().children.at(1);
expect(alert.type()).toEqual('Alert');
expect(alert.props().show).toEqual(true);
expect(alert.contains(hookProps.error)).toEqual(true);
const alert = loadBody().children[1];
expect(alert.type).toEqual('Alert');
expect(alert.props.show).toEqual(true);
expect(alert.children[0].el).toEqual(hookProps.error);
});
testFooter();
});
describe('when the adjusted grade button is disabled', () => {
beforeEach(() => {
useAdjustedGradeInputData.mockReturnValueOnce({ value: 101, possibleGrade: 100 });
el = shallow(<EditModal />);
});
test('adjusted grade button is disabled', () => {
const button = el.instance.findByType(ActionRow)[0].children[1];
expect(button.props.disabled).toEqual(true);
});
});
});
});

View File

@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from '@edx/paragon';
import { Button } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import { selectors } from 'data/redux/hooks';
@@ -32,8 +32,8 @@ export const FilterBadge = ({
return (
<div>
<span className="badge badge-info">
<span>{formatMessage(displayName)}</span>
<span>
<span data-testid="display-name">{formatMessage(displayName)}</span>
<span data-testid="filter-value">
{!hideValue ? `: ${value}` : ''}
</span>
<Button

View File

@@ -1,13 +1,13 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
import { formatMessage } from 'testUtils';
import { Button } from '@edx/paragon';
import { Button } from '@openedx/paragon';
import { selectors } from 'data/redux/hooks';
import FilterBadge from './FilterBadge';
jest.mock('@edx/paragon', () => ({
jest.mock('@openedx/paragon', () => ({
Button: () => 'Button',
}));
jest.mock('data/redux/hooks', () => ({
@@ -48,12 +48,12 @@ describe('FilterBadge', () => {
describe('render', () => {
const testDisplayName = () => {
test('formatted display name appears on badge', () => {
expect(el.contains(formatMessage(hookProps.displayName))).toEqual(true);
expect(el.instance.findByTestId('display-name')[0].children[0].el).toEqual(formatMessage(hookProps.displayName));
});
};
const testCloseButton = () => {
test('close button forwards close method', () => {
expect(el.find(Button).props().onClick).toEqual(handleClose(hookProps.connectedFilters));
expect(el.instance.findByType(Button)[0].props.onClick).toEqual(handleClose(hookProps.connectedFilters));
});
};
test('empty render if isDefault', () => {
@@ -75,20 +75,20 @@ describe('FilterBadge', () => {
testDisplayName();
testCloseButton();
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('value is note present in the badge', () => {
expect(el.contains(hookProps.value)).toEqual(false);
expect(el.instance.findByTestId('filter-value')[0].children).toHaveLength(0);
});
});
describe('do not hide value', () => {
testDisplayName();
testCloseButton();
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('value is note present in the badge', () => {
expect(el.text().includes(hookProps.value)).toEqual(true);
test('value is present in the badge', () => {
expect(el.instance.findByTestId('filter-value')[0].children[0].el).toBe(`: ${hookProps.value}`);
});
});
});

View File

@@ -5,18 +5,22 @@ exports[`FilterBadge render do not hide value snapshot 1`] = `
<span
className="badge badge-info"
>
<span>
<span
data-testid="display-name"
>
a common name
</span>
<span>
<span
data-testid="filter-value"
>
: a common value
</span>
<Button
aria-label="close"
className="btn-info"
onClick={
Object {
"handleClose": Array [
{
"handleClose": [
"some",
"filters",
],
@@ -39,16 +43,20 @@ exports[`FilterBadge render hide Value snapshot 1`] = `
<span
className="badge badge-info"
>
<span>
<span
data-testid="display-name"
>
a common name
</span>
<span />
<span
data-testid="filter-value"
/>
<Button
aria-label="close"
className="btn-info"
onClick={
Object {
"handleClose": Array [
{
"handleClose": [
"some",
"filters",
],

View File

@@ -1,6 +1,6 @@
/* eslint-disable import/no-named-as-default */
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import FilterBadges from '.';
import FilterBadge from './FilterBadge';
@@ -22,14 +22,14 @@ describe('FilterBadges', () => {
el = shallow(<FilterBadges handleClose={handleClose} />);
});
test('snapshot - has a filterbadge with handleClose for each filter in badgeOrder', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('has a filterbadge with handleClose for each filter in badgeOrder', () => {
const badgeProps = el.find(FilterBadge).map(badgeEl => badgeEl.props());
const badgeProps = el.instance.findByType(FilterBadge).map(badgeEl => badgeEl.props);
// key prop is not rendered by react
expect(badgeProps[0]).toEqual({ filterName: order[0], handleClose });
expect(badgeProps[1]).toEqual({ filterName: order[1], handleClose });
expect(badgeProps[2]).toEqual({ filterName: order[2], handleClose });
expect(badgeProps[0]).toMatchObject({ filterName: order[0], handleClose });
expect(badgeProps[1]).toMatchObject({ filterName: order[1], handleClose });
expect(badgeProps[2]).toMatchObject({ filterName: order[2], handleClose });
expect(badgeProps.length).toEqual(3);
});
});

View File

@@ -7,6 +7,7 @@ exports[`FilterMenuToggle component render snapshot 1`] = `
onClick={[MockFunction hooks.toggleFilterMenu]}
>
<Icon
className="mr-1"
src="FilterAlt"
/>

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { Button, Icon } from '@edx/paragon';
import { Button, Icon } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import { FilterAlt } from '@edx/paragon/icons';
import { FilterAlt } from '@openedx/paragon/icons';
import { thunkActions } from 'data/redux/hooks';
@@ -21,7 +21,7 @@ export const FilterMenuToggle = () => {
className="btn-primary align-self-start"
onClick={toggleFilterMenu}
>
<Icon src={FilterAlt} /> {formatMessage(messages.editFilters)}
<Icon src={FilterAlt} className="mr-1" /> {formatMessage(messages.editFilters)}
</Button>
);
};

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
@@ -38,10 +38,10 @@ describe('FilterMenuToggle component', () => {
});
describe('render', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.type()).toEqual('Button');
expect(el.props().onClick).toEqual(toggleFilterMenu);
expect(el.text().includes(formatMessage(messages.editFilters)));
expect(el.snapshot).toMatchSnapshot();
expect(el.instance.type).toEqual('Button');
expect(el.instance.props.onClick).toEqual(toggleFilterMenu);
expect(el.instance.children[2].el).toContain(formatMessage(messages.editFilters));
});
});
});

View File

@@ -3,14 +3,14 @@
exports[`FilteredUsersLabel component render snapshot 1`] = `
<format-message-function
message={
Object {
{
"defaultMessage": "Showing {filteredUsers} of {totalUsers} total learners",
"description": "Users visibility label",
"id": "gradebook.GradesTab.usersVisibilityLabel",
}
}
values={
Object {
{
"filteredUsers": <BoldText
text={100}
/>,

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
@@ -46,8 +46,8 @@ describe('FilteredUsersLabel component', () => {
expect(shallow(<FilteredUsersLabel />).isEmptyRender()).toEqual(true);
});
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el).toMatchObject(shallow(formatMessage(messages.visibilityLabel, {
expect(el.snapshot).toMatchSnapshot();
expect(el.instance).toMatchObject(shallow(formatMessage(messages.visibilityLabel, {
filteredUsers: <BoldText text={userCounts.filteredUsersCount} />,
totalUsers: <BoldText text={userCounts.totalUsersCount} />,
})));

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import Fields from './Fields';
@@ -16,14 +16,13 @@ describe('Gradebook Table Fields', () => {
el = shallow(<Fields.Username {...props} />);
});
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('wraps external user key and username', () => {
expect(el.find('span').childAt(0)).toMatchSnapshot();
expect(el.find('span').childAt(0)).toMatchSnapshot();
const content = el.find('span').childAt(0);
expect(content.childAt(0).text()).toEqual(username);
expect(content.childAt(1).text()).toEqual(props.userKey);
expect(el.instance.findByType('span')[0].el).toMatchSnapshot();
const content = el.instance.findByType('span')[0].children[0];
expect(content.children[0].children[0].el).toEqual(username);
expect(content.children[1].children[0].el).toEqual(props.userKey);
});
});
describe('without external_user_key', () => {
@@ -31,12 +30,12 @@ describe('Gradebook Table Fields', () => {
el = shallow(<Fields.Username username={username} />);
});
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('wraps username only', () => {
const content = el.find('span').childAt(0);
expect(content.childAt(0).text()).toEqual(username);
expect(content.children()).toHaveLength(1);
const content = el.instance.findByType('span')[0].children[0];
expect(content.children[0].children[0].el).toEqual(username);
expect(content.children).toHaveLength(1);
});
});
});
@@ -44,10 +43,10 @@ describe('Gradebook Table Fields', () => {
describe('Text', () => {
const value = 'myTag@place.com';
test('snapshot', () => {
expect(shallow(<Fields.Text value={value} />)).toMatchSnapshot();
expect(shallow(<Fields.Text value={value} />).snapshot).toMatchSnapshot();
});
test('wraps entry value', () => {
expect(shallow(<Fields.Text value={value} />).text()).toEqual(value);
expect(shallow(<Fields.Text value={value} />).instance.children[0].el).toEqual(value);
});
});
});

View File

@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from '@edx/paragon';
import { Button } from '@openedx/paragon';
import { selectors, thunkActions } from 'data/redux/hooks';
import transforms from 'data/redux/transforms';
@@ -11,7 +11,7 @@ export const useGradeButtonData = ({ entry, subsection }) => {
const areGradesFrozen = selectors.assignmentTypes.useAreGradesFrozen();
const { gradeFormat } = selectors.grades.useGradeData();
const setModalState = thunkActions.app.useSetModalStateFromTable();
const label = transforms.grades.subsectionGrade({ gradeFormat, subsection });
const label = transforms.grades.subsectionGrade({ gradeFormat, subsection })();
const onClick = () => {
setModalState({

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { selectors, thunkActions } from 'data/redux/hooks';
import transforms from 'data/redux/transforms';
@@ -41,7 +41,7 @@ const props = {
};
const gradeFormat = 'percent';
const setModalState = jest.fn();
const subsectionGrade = 'test-subsection-grade';
const subsectionGrade = () => 'test-subsection-grade';
selectors.assignmentTypes.useAreGradesFrozen.mockReturnValue(false);
selectors.grades.useGradeData.mockReturnValue({ gradeFormat });
thunkActions.app.useSetModalStateFromTable.mockReturnValue(setModalState);
@@ -73,7 +73,7 @@ describe('GradeButton', () => {
expect(out.areGradesFrozen).toEqual(false);
});
test('label passed from subsection grade redux hook', () => {
expect(out.label).toEqual(subsectionGrade);
expect(out.label).toEqual(subsectionGrade());
});
test('onClick sets modal state with user entry and subsection', () => {
out.onClick();
@@ -101,8 +101,8 @@ describe('GradeButton', () => {
el = shallow(<GradeButton {...props} />);
});
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.text()).toEqual(hookProps.label);
expect(el.snapshot).toMatchSnapshot();
expect(el.instance.el).toEqual(hookProps.label);
});
});
describe('not frozen grades', () => {
@@ -111,10 +111,10 @@ describe('GradeButton', () => {
el = shallow(<GradeButton {...props} />);
});
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.type()).toEqual('Button');
expect(el.props().onClick).toEqual(hookProps.onClick);
expect(el.contains(hookProps.label)).toEqual(true);
expect(el.snapshot).toMatchSnapshot();
expect(el.instance.type).toEqual('Button');
expect(el.instance.props.onClick).toEqual(hookProps.onClick);
expect(el.instance.children[0].el).toEqual(hookProps.label);
});
});
});

View File

@@ -5,7 +5,7 @@ import {
Icon,
OverlayTrigger,
Tooltip,
} from '@edx/paragon';
} from '@openedx/paragon';
import { StrictDict } from 'utils';

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { getLocale } from '@edx/frontend-platform/i18n';
import { OverlayTrigger } from '@edx/paragon';
import { OverlayTrigger } from '@openedx/paragon';
import LabelReplacements from './LabelReplacements';
@@ -12,7 +12,7 @@ const {
MastersOnlyLabelReplacement,
} = LabelReplacements;
jest.mock('@edx/paragon', () => ({
jest.mock('@openedx/paragon', () => ({
Icon: () => 'Icon',
OverlayTrigger: () => 'OverlayTrigger',
Tooltip: () => 'Tooltip',
@@ -25,15 +25,15 @@ describe('LabelReplacements', () => {
el = shallow(<TotalGradeLabelReplacement />);
});
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('displays overlay tooltip', () => {
expect(el.find(OverlayTrigger).props().overlay).toMatchSnapshot();
expect(el.instance.findByType(OverlayTrigger)[0].props.overlay).toMatchSnapshot();
});
});
describe('UsernameLabelReplacement', () => {
test('snapshot', () => {
expect(shallow(<UsernameLabelReplacement />)).toMatchSnapshot();
expect(shallow(<UsernameLabelReplacement />).snapshot).toMatchSnapshot();
});
});
describe('MastersOnlyLabelReplacement', () => {
@@ -43,7 +43,7 @@ describe('LabelReplacements', () => {
defaultMessage: 'defaultMessAge',
description: 'desCripTion',
};
expect(shallow(<MastersOnlyLabelReplacement {...message} />)).toMatchSnapshot();
expect(shallow(<MastersOnlyLabelReplacement {...message} />).snapshot).toMatchSnapshot();
});
});
});
@@ -53,11 +53,11 @@ describe('snapshot', () => {
test('right to left overlay placement', () => {
getLocale.mockImplementation(() => 'en');
el = shallow(<TotalGradeLabelReplacement />);
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('left to right overlay placement', () => {
getLocale.mockImplementation(() => 'ar');
el = shallow(<TotalGradeLabelReplacement />);
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
});

View File

@@ -28,29 +28,36 @@ exports[`Gradebook Table Fields Username with external_user_key snapshot 1`] = `
`;
exports[`Gradebook Table Fields Username with external_user_key wraps external user key and username 1`] = `
<div>
<div>
MyNameFromHere
</div>
<div
className="student-key"
>
My name from another land
</div>
</div>
`;
exports[`Gradebook Table Fields Username with external_user_key wraps external user key and username 2`] = `
<div>
<div>
MyNameFromHere
</div>
<div
className="student-key"
>
My name from another land
</div>
</div>
{
"children": [
{
"children": [
{
"children": [
"MyNameFromHere",
],
"props": {},
"type": "div",
},
{
"children": [
"My name from another land",
],
"props": {
"className": "student-key",
},
"type": "div",
},
],
"props": {},
"type": "div",
},
],
"props": {
"className": "wrap-text-in-cell",
},
"type": "span",
}
`;
exports[`Gradebook Table Fields Username without external_user_key snapshot 1`] = `

View File

@@ -32,7 +32,7 @@ exports[`LabelReplacements TotalGradeLabelReplacement snapshot 1`] = `
}
placement="left"
trigger={
Array [
[
"hover",
"focus",
]
@@ -84,7 +84,7 @@ exports[`snapshot left to right overlay placement 1`] = `
}
placement="right"
trigger={
Array [
[
"hover",
"focus",
]
@@ -118,7 +118,7 @@ exports[`snapshot right to left overlay placement 1`] = `
}
placement="left"
trigger={
Array [
[
"hover",
"focus",
]

View File

@@ -7,13 +7,13 @@ exports[`GradebookTable snapshot 1`] = `
<DataTable
RowStatusComponent={[MockFunction hooks.nullMethod]}
columns={
Array [
[
"some",
"columns",
]
}
data={
Array [
[
"some",
"data",
]

View File

@@ -39,7 +39,7 @@ export const useGradebookTableData = () => {
[Headings.username]: (
<Fields.Username username={entry.username} userKey={entry.external_user_key} />
),
[Headings.email]: (<Fields.Email email={entry.email} />),
[Headings.email]: (<Fields.Text value={entry.email} />),
[Headings.totalGrade]: `${roundGrade(entry.percent * 100)}${getLocalizedPercentSign()}`,
...entry.section_breakdown.reduce((acc, subsection) => ({
...acc,

View File

@@ -1,4 +1,4 @@
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
@@ -22,7 +22,7 @@ jest.mock('i18n/utils', () => ({
jest.mock('./GradeButton', () => 'GradeButton');
jest.mock('./Fields', () => jest.requireActual('testUtils').mockNestedComponents({
Username: 'Fields.Username',
Email: 'Fields.Email',
Text: 'Fields.Text',
}));
jest.mock('./LabelReplacements', () => jest.requireActual('testUtils').mockNestedComponents({
TotalGradeLabelReplacement: 'LabelReplacements.TotalGradeLabelReplacement',
@@ -158,7 +158,7 @@ describe('useGradebookTableData', () => {
test('email field', () => {
allGrades.forEach((entry, index) => {
expect(out.data[index][Headings.email]).toMatchObject(
<Fields.Email email={entry.email} />,
<Fields.Text value={entry.email} />,
);
});
});

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { DataTable } from '@edx/paragon';
import { DataTable } from '@openedx/paragon';
import useGradebookTableData from './hooks';

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import { DataTable } from '@edx/paragon';
import { DataTable } from '@openedx/paragon';
import useGradebookTableData from './hooks';
import GradebookTable from '.';
@@ -24,16 +24,16 @@ describe('GradebookTable', () => {
el = shallow(<GradebookTable />);
});
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('Datatable props', () => {
const datatable = el.find(DataTable);
const props = datatable.props();
const datatable = el.instance.findByType(DataTable)[0];
const { props } = datatable;
expect(props.columns).toEqual(hookProps.columns);
expect(props.data).toEqual(hookProps.data);
expect(props.itemCount).toEqual(hookProps.grades.length);
expect(props.RowStatusComponent).toEqual(hookProps.nullMethod);
expect(datatable.children().at(2).type()).toEqual('DataTable.EmptyTable');
expect(datatable.children().at(2).props().content).toEqual(hookProps.emptyContent);
expect(datatable.children[2].type).toEqual('DataTable.EmptyTable');
expect(datatable.children[2].props.content).toEqual(hookProps.emptyContent);
});
});

View File

@@ -46,6 +46,7 @@
}
.grade-history-header{
float: left;
min-width: 170px;
}
.grade-history-assignment{
@@ -65,7 +66,7 @@
.gradebook-container {
width: 100%;
overflow-x: auto;
height: 600px;
max-height: 600px;
overflow-y: auto;
position: relative;
}
@@ -122,3 +123,34 @@ select#ScoreView.form-control {
border-right-color: $black;
}
}
#edit-filters-btn {
@include media-breakpoint-down(xs) {
width: 100%;
margin-bottom: 1rem;
}
}
.search-container {
@include media-breakpoint-down(xs) {
width: 100%;
}
}
.pgn__modal-body-content .pgn__data-table-layout-wrapper {
@include media-breakpoint-down(sm) {
clear: both;
padding: 1rem 0;
}
}
.page-gradebook {
position: relative;
.sidebar-container {
position: relative;
}
aside.sidebar {
overflow: auto;
}
}

View File

@@ -41,7 +41,7 @@ exports[`ImportGradesButton component render snapshot 1`] = `
className="import-grades-btn"
import={true}
label={
Object {
{
"defaultMessage": "Import Grades",
"description": "A labeled button to import grades in the BulkManagement Tab File Upload Form",
"id": "gradebook.GradesView.importGradesBtnText",

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Form } from '@edx/paragon';
import { Form } from '@openedx/paragon';
import NetworkButton from 'components/NetworkButton';
import messages from './messages';

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Form } from '@edx/paragon';
import { Form } from '@openedx/paragon';
import NetworkButton from 'components/NetworkButton';
import useImportGradesButtonData from './hooks';

View File

@@ -5,7 +5,7 @@ import useImportGradesButtonData from './hooks';
import ImportGradesButton from '.';
jest.unmock('react');
jest.unmock('@edx/paragon');
jest.unmock('@openedx/paragon');
jest.mock('components/NetworkButton', () => 'network-button');
jest.mock('./hooks', () => ({ __esModule: true, default: jest.fn() }));

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { Toast } from '@edx/paragon';
import { Toast } from '@openedx/paragon';
import useImportSuccessToastData from './hooks';

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { shallow } from 'enzyme';
import { shallow } from '@edx/react-unit-test-utils';
import useImportSuccessToastData from './hooks';
import ImportSuccessToast from '.';
@@ -26,14 +26,14 @@ describe('ImportSuccessToast component', () => {
});
describe('render', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
expect(el.snapshot).toMatchSnapshot();
});
test('Toast', () => {
expect(el.type()).toEqual('Toast');
expect(el.props().action).toEqual(hookProps.action);
expect(el.props().onClose).toEqual(hookProps.onClose);
expect(el.props().show).toEqual(hookProps.show);
expect(el.text()).toEqual(hookProps.description);
expect(el.instance.type).toEqual('Toast');
expect(el.instance.props.action).toEqual(hookProps.action);
expect(el.instance.props.onClose).toEqual(hookProps.onClose);
expect(el.instance.props.show).toEqual(hookProps.show);
expect(el.instance.children[0].el).toEqual(hookProps.description);
});
});
});

View File

@@ -17,7 +17,7 @@ exports[`InterventionsReport component output snapshot 1`] = `
</div>
<NetworkButton
label={
Object {
{
"defaultMessage": "Download Interventions",
"description": "The labeled button to download the Intervention report from the Grades View",
"id": "gradebook.GradesView.InterventionsReport.downloadBtn",

Some files were not shown because too many files have changed in this diff Show More