Compare commits

...

115 Commits

Author SHA1 Message Date
Marcos
1a5ae3939b feat: Updated Courseware Search results for SR 2025-03-24 17:28:47 -03:00
renovate[bot]
7ea0bd175b fix(deps): update dependency @openedx/frontend-build to v14.3.3 (#1648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 19:56:42 +00:00
Kristin Aoki
dae1d63e23 feat: update unit title plugin (#1643)
This PR updates the unit title plugin to include all the elements that are part of the unit title, which includes the unit title, bookmark button, and navigation buttons (if left sidebar navigation is enabled).
2025-03-24 11:46:02 -04:00
edX requirements bot
6ab0deb7b7 chore: update browserslist DB (#1646)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-03-24 00:39:25 +00:00
Michael Roytman
e5f04d92b9 fix: useExamAccess hook does not make call to fetchExamAccessToken after isExam changes (#1644)
This commit fixes a bug that prevents the fetchExamAccessToken hook in the useExamAccess hook from running. This occurs when the value of isExam returned by the useIsExam hook changes from false to true. Because the dependency array of the useEffect hook within the useExamAccess hook was ['id'], the useEffect hook would not rerun on changes to isExam. The fix is to add isExam to the dependency array.
2025-03-20 15:01:40 -04:00
Kristin Aoki
f39a50e7dc feat: add section outline plugin (#1632)
This PR adds a plugin slot for the section list in the outline tab. This plugin can be used to add custom content before the list or add extra content to the titles for sections and subsections. To accomplish this, some of the smaller components inside Section and SequenceLink have been extrapolated into their own components so that they can be easily imported for use in plugins.
2025-03-18 16:51:16 -04:00
Kristin Aoki
72724bcafb fix: use sentence casing (#1640)
* fix: use sentence casing

* fix: failing tests
2025-03-18 15:28:59 -04:00
Kristin Aoki
964abbe0c3 fix: right sidebar icon behavior (#1636)
When using the right new-sidebar with the left sidebar navigation, the icon for the right sidebar changed whenever the left sidebar was open. The icon change is supposed to indicate to users that the right sidebar is open. It is confusing to users when the left sidebar navigation is open and the right sidebar icon is filled instead of outlined.
2025-03-17 09:31:23 -04:00
edX requirements bot
96d20e20e6 chore: update browserslist DB (#1638)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-03-17 00:39:37 +00:00
Kristin Aoki
a56fd7d0e1 feat: change blocked icon to lock icon (#1619)
* feat: replace blocked icon with locked

* refactor: replace injectIntl with useIntl

* revert: temporary test change

* fix: failing test

* fix: wording of message description

* fix: lingering lint error

* fix: missing message variable
2025-03-12 15:43:27 -04:00
Rodrigo Martin
679caa61f3 feat: Update link styling (#1631) 2025-03-11 12:06:28 -03:00
edX requirements bot
420060967b chore: update browserslist DB (#1630)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-03-10 00:32:49 +00:00
Michael Roytman
91d3762513 feat: update version of frontend-lib-learning-assistant to 2.19.2 (#1629)
This commit installs version 2.19.2 of @edx/frontend-lib-learning-assistant.

This release commit fixes a bug where the days remaining banner appears after an audit trial learner sends their first message. In this case, the days remaining is not displayed until the call to the chat summary endpoint completes. This commit adds a loading spinner to the banner that appears while that call is in progress.

See https://github.com/edx/frontend-lib-learning-assistant/releases/tag/v2.19.2.
2025-03-07 11:01:21 -05:00
Marcos Rigoli
2e3ed087d1 fix: Updated a11y on Courseware Search results. (#1620) 2025-03-06 15:24:35 -03:00
Rodrigo Martin
d76d4db097 feat: remove align-items-center from unit title main header (#1628) 2025-03-06 12:52:12 -03:00
renovate[bot]
04b314d157 fix(deps): update dependency @openedx/paragon to v22.16.0 (#1626)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 05:43:15 +00:00
renovate[bot]
1db4848d1a fix(deps): update dependency @openedx/frontend-slot-footer to v1.1.0 (#1625)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 02:18:14 +00:00
renovate[bot]
8f294781d2 fix(deps): update dependency @openedx/frontend-plugin-framework to v1.5.0 (#1624)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 22:06:50 +00:00
renovate[bot]
d6908abb13 chore(deps): update dependency @testing-library/user-event to v14.6.1 2025-03-05 14:00:48 -08:00
Braden MacDonald
96d3d0da7e chore: remove husky 2025-03-05 16:39:59 -03:00
renovate[bot]
14cc32fcf6 fix(deps): update dependency @openedx/frontend-build to v14.3.2 (#1616)
* fix(deps): update dependency @openedx/frontend-build to v14.3.2

* fix: 'unitId' PropType is defined but prop is never used

* fix: bump maximum allowed bundle size for now :/

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Braden MacDonald <braden@opencraft.com>
2025-03-05 18:04:37 +00:00
Jansen Kantor
0ac127e4c9 feat: add plugin slot for course end course recommendations (#1618) 2025-03-04 15:12:53 -05:00
Rodrigo Martin
06e5fb5a44 feat: Update previous and next unit navigation buttons design (#1617)
* feat: Update previous and next unit navigation buttons design

* feat: add unit test

* feat: move unit navigation to be inline with unit title
2025-03-04 11:05:39 -03:00
Agrendalath
2235737490 feat: close sidebar on mobile after selecting a unit 2025-03-03 13:28:59 -08:00
edX requirements bot
fca32ae872 chore: update browserslist DB (#1567)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-03-03 10:16:40 -08:00
Braden MacDonald
ae04e5b366 fix: remove husky internal files that should never have been committed 2025-02-28 10:18:31 -08:00
Marcos Rigoli
db3f1b9cb0 feat: Search a11y updated behavior. (#1614)
* feat: Updated accesibility on courseware search modal

* chore: Updated i18n to use useIntl() hook and dialog behavior updates

* feat: Added close when clicking on the modal backdrop

* chore: Swapped remove listeners with an AbortController

* fix: Fixed tests

* chore: Rolled back unintended husky script change
2025-02-28 10:12:39 -05:00
renovate[bot]
ec360bc545 fix(deps): update dependency @edx/frontend-platform to v8.2.1 (#1615)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 14:44:43 +00:00
renovate[bot]
6c5220b4d7 fix(deps): update dependency @edx/frontend-lib-special-exams to v3.3.0 (#1613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 10:38:07 +00:00
renovate[bot]
f433118a8d fix(deps): update dependency @edx/browserslist-config to v1.5.0 (#1612)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 06:41:56 +00:00
renovate[bot]
e798331855 fix(deps): update dependency @edx/frontend-component-header to v5.8.3 (#1609)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 00:54:24 +00:00
renovate[bot]
adb5796ff6 fix(deps): update dependency sass-loader to v16.0.5 (#1610)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 00:53:56 +00:00
renovate[bot]
9958638a86 fix(deps): update dependency @reduxjs/toolkit to v1.9.7 2025-02-27 16:47:57 -08:00
renovate[bot]
b8e844eba6 fix(deps): update dependency sass to v1.85.1 2025-02-27 16:43:20 -08:00
Varsha Menon
da633ffbd9 feat: upgrade learning assistant frontend to 2.19.1 optimizely bug fix (#1607) 2025-02-27 10:15:04 -05:00
Rodrigo Martin
46889c2aba feat(AU-2375): Update breadcrumbs and outline navigation bar UI (#1582)
* feat: extend CourseOutlineSidebarTriggerSlot props

* feat: Remove courseId from CourseOutlineSidebarTriggerPlugin props

* feat: update useContextId to use courseware data along with coursehome

* feat: extend useCourseOutlineSidebar values with sequenceStatus
2025-02-27 11:24:05 -03:00
Kristin Aoki
3cbbb0272b fix: update outline sidebar hooks for plugins (#1586)
* fix: update outline sidebar hooks for plugins

* docs: explain UnitLinkWrapper
2025-02-21 14:49:52 -05:00
Michael Roytman
911c7658f5 feat: update version of frontend-lib-special-exams library from 3.2.0 to 3.2.1 (#1591)
This commit updates the version of the frontend-lib-special-exams library from 3.2.0 to 3.2.1. The full changelog is available here: openedx/frontend-lib-special-exams@3.2.0...3.2.1: https://github.com/openedx/frontend-lib-special-exams/compare/v3.2.0...v3.2.1.

The changes in this new version are summarized below.
* Update exam message for submitted proctored exams with more accurate language.
2025-02-21 11:28:07 -05:00
Varsha Menon
b54d1e467e Revert "chore: upgrade frontend lib learning assistant version (#1590)" (#1592)
This reverts commit e34d18d727.
2025-02-21 10:38:28 -05:00
Varsha Menon
e34d18d727 chore: upgrade frontend lib learning assistant version (#1590) 2025-02-19 12:18:51 -05:00
Kyle McCormick
6949e5708f chore: make sure links point to MFE not legacy (#1589)
now that the legacy profile and account pages have been removed, we need to make sure that all of the links point to the MFE URLs; we were relying on the legacy applications to do redirection before.

FIXES: APER-3884
FIXES: openedx/public-engineering#71

Co-authored-by: Deborah Kaplan <deborahgu@users.noreply.github.com>
2025-02-18 08:59:15 -08:00
Stanislav
eef6b1efe2 fix: Ugly appearance of the Start/Resume Course button at the top of the course outline (#1585) 2025-02-14 18:17:21 -08:00
Kristin Aoki
9dc45e192d fix: accessibility issues on outline and unit pages (#1580)
This PR fixes the following accessibility issues:

1. Header used for screenreader only text
2. Element focus when expanding and dismissing welcome message
3. Bookmark button using wrong ARIA attributing while processing bookmark status
2025-01-31 14:18:28 -05:00
Marcos Rigoli
bd9c97c269 fix: Unify Xpert audit trial eligibility between backend and frontend (#1581)
* fix: Refactored Chat to be easier to read

* chore: Fixed comment typo
2025-01-30 16:35:39 -03:00
Alison Langston
c70fb138f0 feat: update proctoring info panel api call (#1579) 2025-01-29 15:00:13 -05:00
Kristin Aoki
8823cfaa0a feat: update next unit button plugin for left sidebar navigation usage (#1578)
* feat: move unit next button slot to plugins folder

* feat: update unit navigation at top to use next unit plugin

* fix: remove 2u plugin specific code
2025-01-28 13:01:18 -05:00
salmannawaz
7865fadec2 chore: update catalog-info file and remove openedx.yaml (#1575) 2025-01-24 09:39:36 -05:00
Marcos Rigoli
5be1620f1d fix: Updated learning assistant to v2.14.2 (#1577) 2025-01-21 17:17:38 -03:00
Kshitij Sobti
d5a6a59d07 feat: Add plugin slots for progress page components (#1496) 2025-01-16 12:56:07 -05:00
Braden MacDonald
826f1382dd * fix(deps): update @openedx/paragon to v22.13.0, fix minor TypeScript warning (#1572)
* fix(deps): update dependency @openedx/paragon to v22.13.0

* fix: update use of useWindowSize() to reflect accurate data types

* chore: allow slightly larger bundle size for new paragon :/

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 10:09:17 -08:00
Awais Ansari
5e5fdeba44 fix: updated notifiations preferences url (#1573) 2025-01-10 17:13:15 +05:00
dependabot[bot]
01369eb00d chore(deps): bump cross-spawn from 7.0.3 to 7.0.6
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-08 17:25:18 -08:00
renovate[bot]
4bb4bb7a88 fix(deps): update dependency @edx/browserslist-config to v1.4.0 (#1570)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 00:40:32 +00:00
renovate[bot]
1d154f46c1 fix(deps): update dependency @edx/frontend-platform to v8.1.5 (#1568)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 22:58:18 +00:00
renovate[bot]
420db8133f chore(deps): update dependency jest-when to v3.7.0 2025-01-06 14:53:06 -08:00
Kristin Aoki
1ffc93dc6d feat: update grade summary to show floating point grades (#1558)
This PR resolves the bug that shows assignment's weighted grades that do not sum to the correct total grade. When a learner's weighted grades round down to the nearest whole number, but the summation of the weighted grades will round to a higher percent than the pre-rounded summation. To clarify this for users, the assignment's weighted grade will now show 2 decimal points, matching the legacy display found in the grade graph. To further clarify the difference, a tooltip was added to show the learner the raw weighted grade and the rounded weighted grade.
2025-01-06 15:12:40 -05:00
Marcos Rigoli
346e15abd4 feat: Updated frontend-lib-learning-assistant to v2.14.1 (#1559) 2025-01-03 13:00:10 -03:00
renovate[bot]
4726c23bc3 fix(deps): update dependency sass-loader to v16.0.4 (#1565)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-03 02:01:20 +00:00
renovate[bot]
cbbb417894 fix(deps): update dependency @openedx/frontend-slot-footer to v1.0.7 (#1564)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 22:50:50 +00:00
renovate[bot]
c57f28ad40 fix(deps): update dependency @openedx/frontend-build to v14.2.2 (#1563)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 18:25:52 +00:00
renovate[bot]
310fb84517 fix(deps): update dependency @edx/frontend-platform to v8.1.4 (#1562)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 16:06:10 +00:00
renovate[bot]
623f6946e5 fix(deps): update dependency @edx/frontend-component-header to v5.8.2 (#1561)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 15:54:37 +00:00
renovate[bot]
cf124877e8 chore(deps): update dependency eslint-import-resolver-webpack to v0.13.10 (#1560)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 14:40:47 +00:00
renovate[bot]
0456ad9318 fix(deps): update dependency husky to v9 (#1545)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 11:34:18 -03:00
edX requirements bot
7be87b0f83 chore: update browserslist DB (#1555)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-01-02 11:33:07 -03:00
Rodrigo Martin
cbe5b28762 fix(AU-2174): Fix left sidebar throwing 404 (#1556) 2024-12-20 13:09:10 -03:00
Abdur Rahman Asad
4a80532b8d fix: update iframe feature policy
This is needed to fix Xblock video play button not working in Chrome for youtube videos due to iframe security policy.
2024-12-18 09:38:00 -08:00
Marcos Rigoli
e505f78cfb feat: Updated frontend-lib-learning-assistant to v2.13.0 (#1557) 2024-12-18 12:50:19 -03:00
dependabot[bot]
3811f5f9d5 chore(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>
2024-12-16 11:17:29 -08:00
Varsha Menon
8a20b908c7 feat: upgrade learning assistant (#1553) 2024-12-16 10:09:34 -05:00
Kristin Aoki
8a6fa937ea feat: add progress certificate status plugin slot (#1551) 2024-12-11 13:24:33 +01:00
Alison Langston
dafdcad2b4 feat: update gating for chat component (#1550)
* feat: update gating for chat component

* fix: add gating for access expiration

* chore: upgrade learning assistant version
2024-12-09 16:21:40 -05:00
dependabot[bot]
cd56ffaf9d chore(deps): bump path-to-regexp and express
Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `path-to-regexp` from 0.1.10 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/v0.1.10...v0.1.12)

Updates `express` from 4.21.1 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.1...4.21.2)

---
updated-dependencies:
- dependency-name: path-to-regexp
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-09 12:06:31 -08:00
edX requirements bot
c11cb85d78 chore: update browserslist DB (#1548)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-12-09 10:04:40 -08:00
Farhaan Bukhsh
b09bcbd3ae Revert PR #1519 "fix: only one in-course experience sidebar can be open..."
(and "fix: add test in CourseOutlineTray")
This reverts commit 020e7fb42 and 038b05ba
2024-12-04 11:06:35 -08:00
Braden MacDonald
4a925f9c11 refactor: convert masquerade UI widgets to Function Components + TypeScript (#1513)
* refactor: convert masquerade UI widgets to TypeScript

* test: improve test coverage

* chore: upgrade @testing-library/user-event to v14

* test: improve test coverage

* test: improve test coverage
2024-12-04 22:33:06 +05:30
Brian Smith
f5b6243c61 feat: wrap existing sidebars in frontend-plugin-framework PluginSlots (#1543) 2024-12-04 10:24:03 -05:00
dependabot[bot]
98c670afe7 chore(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>
2024-12-02 09:34:26 -08:00
jciasenza
038b05ba6c fix: add test in CourseOutlineTray 2024-11-30 20:57:59 +05:30
jciasenza
020e7fb42c fix: only one in-course experience sidebar can be open at a time failing 2024-11-30 20:57:59 +05:30
Demid
ead98538b9 feat: hide studio button for limited staff (#1436) 2024-11-29 00:36:05 +05:30
renovate[bot]
90ef6ace5c fix(deps): update dependency @openedx/frontend-plugin-framework to v1.4.1 (#1544)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-26 22:50:10 +00:00
renovate[bot]
e0196f2a2a fix(deps): update dependency copy-webpack-plugin to v12 2024-11-26 14:41:49 -08:00
edX requirements bot
b7befcff7e chore: update browserslist DB (#1541)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-11-25 10:38:56 -08:00
Varsha Menon
642031bf87 feat: upgrade learning assistant (#1542) 2024-11-25 12:02:51 -05:00
Brian Smith
f778f27647 feat: update to header with new FPF pluginProps (#1524) 2024-11-22 15:20:58 -05:00
renovate[bot]
b3bce8713c chore(deps): update dependency @pact-foundation/pact to v13.2.0 2024-11-21 13:59:06 -08:00
renovate[bot]
dacb30c73e chore(deps): update actions/checkout action to v4 2024-11-21 09:37:10 -08:00
renovate[bot]
81a4deeec0 fix(deps): update dependency @openedx/paragon to v22.10.0 (#1536)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-21 05:14:05 +00:00
renovate[bot]
9a1b05a1a4 fix(deps): update dependency @openedx/frontend-plugin-framework to v1.4.0 (#1535)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-21 00:55:38 +00:00
renovate[bot]
b9e1fb0d2b fix(deps): update dependency @edx/frontend-lib-special-exams to v3.2.0 (#1534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 22:04:03 +00:00
renovate[bot]
ebd0f8816c fix(deps): update dependency @edx/frontend-lib-learning-assistant to v2.5.0 (#1533)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 20:47:48 +00:00
renovate[bot]
d749429361 fix(deps): update dependency @edx/frontend-component-header to v5.8.0 (#1532)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 17:12:33 +00:00
renovate[bot]
19b8df35ae chore(deps): update dependency axios-mock-adapter to v2.1.0 2024-11-20 09:06:54 -08:00
renovate[bot]
e468d2087b fix(deps): update dependency sass-loader to v16.0.3 (#1530)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 11:23:37 +00:00
renovate[bot]
42e0ac86d7 fix(deps): update dependency @openedx/frontend-slot-footer to v1.0.6 (#1529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 08:36:02 +00:00
renovate[bot]
ea5cf37fd8 fix(deps): update dependency @edx/frontend-platform to v8.1.2 (#1528)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 03:33:02 +00:00
renovate[bot]
e4cdec7389 chore(deps): update dependency @pact-foundation/pact to v13.1.5 (#1527)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 00:14:12 +00:00
renovate[bot]
8aafc6b8bd fix(deps): update dependency redux to v4.2.1 2024-11-19 16:08:56 -08:00
dependabot[bot]
913c8e4086 chore(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>
2024-11-18 11:27:30 -08:00
Nathan Sprenkle
c221770213 chore: bump frontend-build to 14.2.0 (#1522) 2024-11-18 14:02:51 -05:00
Nathan Sprenkle
4fe40c264f feat: add configurable robots meta tag (#1520) 2024-11-18 12:47:52 -05:00
Marcos Rigoli
c20c7677a3 chore: Updated @edx/frontend-lib-learning-assistant to v2.4.1 (#1518) 2024-11-14 15:22:23 -03:00
Kristin Aoki
2ff8c3949e fix: update skip nav destination (#1516) 2024-11-12 16:18:09 -05:00
edX requirements bot
4a5c43d365 chore: update browserslist DB (#1515)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-11-12 12:32:57 -08:00
Kristin Aoki
4da37f369b feat: add page not found route (#1514)
* feat: add page not found route

* feat: add logging

* feat: add tests
2024-11-12 09:05:24 -05:00
edX requirements bot
0effb32318 chore: update browserslist DB (#1511)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-11-04 12:26:21 -08:00
Roman Edirisinghe
6813872dd3 fix: corrects typo in IFRAME_FEATURE_POLICY 2024-11-04 09:47:37 -08:00
Kristin Aoki
e337a367d1 fix: xblock error mfe unit preview (#1508)
* feat: add functionality to see unit draft preview

* fix: course redirect unit to sequnce unit redirect

* fix: not showing preview when masquerading

* feat: in preview fetch draft branch of sequence metadata
2024-11-01 13:22:24 -04:00
Bilal Qamar
65343470e1 test: Remove support for Node 18 (#1454)
* test: Remove support for Node 18

* chore: update code coverage artifact naming
2024-10-31 15:35:17 -04:00
Navin Karkera
e69114a839 feat: option to show/hide ungraded assignment in progress page (#1380)
* feat: option to show/hide ungraded assignment in progress page

test: add tests for show ungraded toggle

feat: update score label in progress page based on grading

refactor: update score label text and add tooltip

refactor: move label tooltip near header as normal text

refactor: update problem score label Graded scores

* refactor: move config check to utils
2024-10-31 15:48:24 +05:30
Alison Langston
2d63a14c2e feat: update courseware search api name (#1507) 2024-10-30 08:52:54 -04:00
Juan Carlos Iasenza (Aulasneo)
2d1f893a40 fix: untranslatable strings in Instructor Toolbar #1193 (#1505) 2024-10-29 14:34:08 -07:00
edX requirements bot
64f92deeb1 chore: update browserslist DB (#1506)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-10-28 11:28:11 -07:00
Kristin Aoki
d47433ee83 feat: add functionality to see unit draft preview (#1501)
* feat: add functionality to see unit draft preview

* feat: add tests for course link redirects

* fix: course redirect unit to sequnce unit redirect

* fix: test coverage
2024-10-28 10:31:17 -04:00
209 changed files with 7657 additions and 3991 deletions

1
.env
View File

@@ -48,3 +48,4 @@ TWITTER_HASHTAG=''
TWITTER_URL='' TWITTER_URL=''
USER_INFO_COOKIE_NAME='' USER_INFO_COOKIE_NAME=''
OPTIMIZELY_FULL_STACK_SDK_KEY='' OPTIMIZELY_FULL_STACK_SDK_KEY=''
SHOW_UNGRADED_ASSIGNMENT_PROGRESS=''

View File

@@ -50,3 +50,4 @@ SESSION_COOKIE_DOMAIN='localhost'
CHAT_RESPONSE_URL='http://localhost:18000/api/learning_assistant/v1/course_id' CHAT_RESPONSE_URL='http://localhost:18000/api/learning_assistant/v1/course_id'
PRIVACY_POLICY_URL='http://localhost:18000/privacy' PRIVACY_POLICY_URL='http://localhost:18000/privacy'
OPTIMIZELY_FULL_STACK_SDK_KEY='' OPTIMIZELY_FULL_STACK_SDK_KEY=''
SHOW_UNGRADED_ASSIGNMENT_PROGRESS=''

View File

@@ -47,3 +47,4 @@ TWITTER_HASHTAG='myedxjourney'
TWITTER_URL='https://twitter.com/edXOnline' TWITTER_URL='https://twitter.com/edXOnline'
USER_INFO_COOKIE_NAME='edx-user-info' USER_INFO_COOKIE_NAME='edx-user-info'
PRIVACY_POLICY_URL='http://localhost:18000/privacy' PRIVACY_POLICY_URL='http://localhost:18000/privacy'
SHOW_UNGRADED_ASSIGNMENT_PROGRESS=''

View File

@@ -9,35 +9,28 @@ on:
jobs: jobs:
tests: tests:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
node: [18, 20]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: ${{ matrix.node }} node-version-file: '.nvmrc'
- run: make validate.ci - run: make validate.ci
- name: Archive code coverage results - name: Archive code coverage results
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: code-coverage-report-${{ matrix.node }} name: code-coverage-report
# When we're only using Node 20, replace the line above with the following:
# name: code-coverage-report
path: coverage/*.* path: coverage/*.*
coverage: coverage:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: tests needs: tests
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Download code coverage results - name: Download code coverage results
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: code-coverage-report-20 name: code-coverage-report
# When we're only using Node 20, replace the line above with the following:
# name: code-coverage-report
- name: Upload coverage - name: Upload coverage
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v5
with: with:
fail_ci_if_error: true fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}

1
.husky/_/.gitignore vendored
View File

@@ -1 +0,0 @@
*

View File

@@ -1,31 +0,0 @@
#!/bin/sh
if [ -z "$husky_skip_init" ]; then
debug () {
if [ "$HUSKY_DEBUG" = "1" ]; then
echo "husky (debug) - $1"
fi
}
readonly hook_name="$(basename "$0")"
debug "starting $hook_name..."
if [ "$HUSKY" = "0" ]; then
debug "HUSKY env variable is set to 0, skipping hook"
exit 0
fi
if [ -f ~/.huskyrc ]; then
debug "sourcing ~/.huskyrc"
. ~/.huskyrc
fi
export readonly husky_skip_init=1
sh -e "$0" "$@"
exitCode="$?"
if [ $exitCode != 0 ]; then
echo "husky - $hook_name hook exited with code $exitCode (error)"
fi
exit $exitCode
fi

View File

@@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run lint

View File

@@ -12,6 +12,7 @@ metadata:
icon: "Web" icon: "Web"
annotations: annotations:
openedx.org/arch-interest-groups: "" openedx.org/arch-interest-groups: ""
openedx.org/release: "master"
spec: spec:
owner: group:committers-frontend-app-learning owner: group:committers-frontend-app-learning
type: 'website' type: 'website'

View File

@@ -1,10 +0,0 @@
# This file describes this Open edX repo, as described in OEP-2:
# https://open-edx-proposals.readthedocs.io/en/latest/oep-0002-bp-repo-metadata.html#specification
oeps: {}
owner: edx/platform-core-tnl
openedx-release:
# The openedx-release key is described in OEP-10:
# https://open-edx-proposals.readthedocs.io/en/latest/oep-0010-proc-openedx-releases.html
# The FAQ might also be helpful: https://openedx.atlassian.net/wiki/spaces/COMM/pages/1331268879/Open+edX+Release+FAQ
ref: master

3458
package-lock.json generated
View File

@@ -11,9 +11,9 @@
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2", "@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/browserslist-config": "1.2.0", "@edx/browserslist-config": "1.5.0",
"@edx/frontend-component-header": "^5.6.0", "@edx/frontend-component-header": "^5.8.0",
"@edx/frontend-lib-learning-assistant": "^2.2.4", "@edx/frontend-lib-learning-assistant": "^2.19.2",
"@edx/frontend-lib-special-exams": "^3.1.3", "@edx/frontend-lib-special-exams": "^3.1.3",
"@edx/frontend-platform": "^8.0.0", "@edx/frontend-platform": "^8.0.0",
"@edx/openedx-atlas": "^0.6.0", "@edx/openedx-atlas": "^0.6.0",
@@ -22,16 +22,15 @@
"@fortawesome/free-regular-svg-icons": "5.15.4", "@fortawesome/free-regular-svg-icons": "5.15.4",
"@fortawesome/free-solid-svg-icons": "5.15.4", "@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/react-fontawesome": "^0.1.4", "@fortawesome/react-fontawesome": "^0.1.4",
"@openedx/frontend-build": "14.1.2", "@openedx/frontend-build": "^14.2.0",
"@openedx/frontend-plugin-framework": "^1.2.1", "@openedx/frontend-plugin-framework": "^1.2.1",
"@openedx/frontend-slot-footer": "^1.0.2", "@openedx/frontend-slot-footer": "^1.0.2",
"@openedx/paragon": "^22.3.0", "@openedx/paragon": "^22.3.0",
"@popperjs/core": "2.11.8", "@popperjs/core": "2.11.8",
"@reduxjs/toolkit": "1.8.1", "@reduxjs/toolkit": "1.9.7",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"classnames": "2.5.1", "classnames": "2.5.1",
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^12.0.0",
"husky": "7.0.4",
"joi": "^17.11.0", "joi": "^17.11.0",
"js-cookie": "3.0.5", "js-cookie": "3.0.5",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@@ -47,7 +46,7 @@
"react-router": "6.15.0", "react-router": "6.15.0",
"react-router-dom": "6.15.0", "react-router-dom": "6.15.0",
"react-share": "4.4.1", "react-share": "4.4.1",
"redux": "4.1.2", "redux": "4.2.1",
"reselect": "4.1.8", "reselect": "4.1.8",
"sass": "^1.79.3", "sass": "^1.79.3",
"sass-loader": "^16.0.2", "sass-loader": "^16.0.2",
@@ -60,8 +59,8 @@
"@testing-library/jest-dom": "5.17.0", "@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "12.1.5", "@testing-library/react": "12.1.5",
"@testing-library/react-hooks": "^8.0.1", "@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "13.5.0", "@testing-library/user-event": "14.6.1",
"axios-mock-adapter": "2.0.0", "axios-mock-adapter": "2.1.0",
"bundlewatch": "^0.4.0", "bundlewatch": "^0.4.0",
"eslint-import-resolver-webpack": "^0.13.9", "eslint-import-resolver-webpack": "^0.13.9",
"jest": "^29.7.0", "jest": "^29.7.0",
@@ -131,12 +130,13 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.24.7", "version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
"integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/highlight": "^7.24.7", "@babel/helper-validator-identifier": "^7.25.9",
"js-tokens": "^4.0.0",
"picocolors": "^1.0.0" "picocolors": "^1.0.0"
}, },
"engines": { "engines": {
@@ -206,27 +206,28 @@
} }
}, },
"node_modules/@babel/generator": { "node_modules/@babel/generator": {
"version": "7.25.0", "version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz",
"integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/types": "^7.25.0", "@babel/parser": "^7.26.9",
"@babel/types": "^7.26.9",
"@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25", "@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^2.5.1" "jsesc": "^3.0.2"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-annotate-as-pure": { "node_modules/@babel/helper-annotate-as-pure": {
"version": "7.24.7", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz",
"integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/types": "^7.24.7" "@babel/types": "^7.25.9"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -328,13 +329,13 @@
} }
}, },
"node_modules/@babel/helper-module-imports": { "node_modules/@babel/helper-module-imports": {
"version": "7.24.7", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
"integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/traverse": "^7.24.7", "@babel/traverse": "^7.25.9",
"@babel/types": "^7.24.7" "@babel/types": "^7.25.9"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -371,9 +372,9 @@
} }
}, },
"node_modules/@babel/helper-plugin-utils": { "node_modules/@babel/helper-plugin-utils": {
"version": "7.24.8", "version": "7.26.5",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
"integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -440,27 +441,27 @@
} }
}, },
"node_modules/@babel/helper-string-parser": { "node_modules/@babel/helper-string-parser": {
"version": "7.24.8", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
"integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-validator-identifier": { "node_modules/@babel/helper-validator-identifier": {
"version": "7.24.7", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
"integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-validator-option": { "node_modules/@babel/helper-validator-option": {
"version": "7.24.8", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
"integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -493,99 +494,13 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/highlight": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
"integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.24.7",
"chalk": "^2.4.2",
"js-tokens": "^4.0.0",
"picocolors": "^1.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight/node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"license": "MIT",
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/highlight/node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/highlight/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"license": "MIT",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/@babel/highlight/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"license": "MIT"
},
"node_modules/@babel/highlight/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"license": "MIT",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@babel/highlight/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/highlight/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"license": "MIT",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.25.3", "version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz",
"integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/types": "^7.25.2" "@babel/types": "^7.26.9"
}, },
"bin": { "bin": {
"parser": "bin/babel-parser.js" "parser": "bin/babel-parser.js"
@@ -828,12 +743,12 @@
} }
}, },
"node_modules/@babel/plugin-syntax-jsx": { "node_modules/@babel/plugin-syntax-jsx": {
"version": "7.24.7", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz",
"integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-plugin-utils": "^7.24.7" "@babel/helper-plugin-utils": "^7.25.9"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -1580,12 +1495,12 @@
} }
}, },
"node_modules/@babel/plugin-transform-react-display-name": { "node_modules/@babel/plugin-transform-react-display-name": {
"version": "7.24.7", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz",
"integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==", "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-plugin-utils": "^7.24.7" "@babel/helper-plugin-utils": "^7.25.9"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -1595,16 +1510,16 @@
} }
}, },
"node_modules/@babel/plugin-transform-react-jsx": { "node_modules/@babel/plugin-transform-react-jsx": {
"version": "7.25.2", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.2.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz",
"integrity": "sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==", "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-annotate-as-pure": "^7.24.7", "@babel/helper-annotate-as-pure": "^7.25.9",
"@babel/helper-module-imports": "^7.24.7", "@babel/helper-module-imports": "^7.25.9",
"@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-plugin-utils": "^7.25.9",
"@babel/plugin-syntax-jsx": "^7.24.7", "@babel/plugin-syntax-jsx": "^7.25.9",
"@babel/types": "^7.25.2" "@babel/types": "^7.25.9"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -1614,12 +1529,12 @@
} }
}, },
"node_modules/@babel/plugin-transform-react-jsx-development": { "node_modules/@babel/plugin-transform-react-jsx-development": {
"version": "7.24.7", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz",
"integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==", "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/plugin-transform-react-jsx": "^7.24.7" "@babel/plugin-transform-react-jsx": "^7.25.9"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -1629,13 +1544,13 @@
} }
}, },
"node_modules/@babel/plugin-transform-react-pure-annotations": { "node_modules/@babel/plugin-transform-react-pure-annotations": {
"version": "7.24.7", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz",
"integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==", "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-annotate-as-pure": "^7.24.7", "@babel/helper-annotate-as-pure": "^7.25.9",
"@babel/helper-plugin-utils": "^7.24.7" "@babel/helper-plugin-utils": "^7.25.9"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -1941,17 +1856,17 @@
} }
}, },
"node_modules/@babel/preset-react": { "node_modules/@babel/preset-react": {
"version": "7.24.7", "version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz", "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz",
"integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==", "integrity": "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-plugin-utils": "^7.24.7", "@babel/helper-plugin-utils": "^7.25.9",
"@babel/helper-validator-option": "^7.24.7", "@babel/helper-validator-option": "^7.25.9",
"@babel/plugin-transform-react-display-name": "^7.24.7", "@babel/plugin-transform-react-display-name": "^7.25.9",
"@babel/plugin-transform-react-jsx": "^7.24.7", "@babel/plugin-transform-react-jsx": "^7.25.9",
"@babel/plugin-transform-react-jsx-development": "^7.24.7", "@babel/plugin-transform-react-jsx-development": "^7.25.9",
"@babel/plugin-transform-react-pure-annotations": "^7.24.7" "@babel/plugin-transform-react-pure-annotations": "^7.25.9"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -2006,41 +1921,31 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/runtime-corejs3/node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/@babel/runtime/node_modules/regenerator-runtime": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
"integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
},
"node_modules/@babel/template": { "node_modules/@babel/template": {
"version": "7.25.0", "version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
"integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.24.7", "@babel/code-frame": "^7.26.2",
"@babel/parser": "^7.25.0", "@babel/parser": "^7.26.9",
"@babel/types": "^7.25.0" "@babel/types": "^7.26.9"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/traverse": { "node_modules/@babel/traverse": {
"version": "7.25.3", "version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz",
"integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.24.7", "@babel/code-frame": "^7.26.2",
"@babel/generator": "^7.25.0", "@babel/generator": "^7.26.9",
"@babel/parser": "^7.25.3", "@babel/parser": "^7.26.9",
"@babel/template": "^7.25.0", "@babel/template": "^7.26.9",
"@babel/types": "^7.25.2", "@babel/types": "^7.26.9",
"debug": "^4.3.1", "debug": "^4.3.1",
"globals": "^11.1.0" "globals": "^11.1.0"
}, },
@@ -2049,14 +1954,13 @@
} }
}, },
"node_modules/@babel/types": { "node_modules/@babel/types": {
"version": "7.25.2", "version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
"integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-string-parser": "^7.24.8", "@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.24.7", "@babel/helper-validator-identifier": "^7.25.9"
"to-fast-properties": "^2.0.0"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -2177,14 +2081,16 @@
"integrity": "sha512-Dn9CtpC8fovh++Xi4NF5NJoeR9yU2yXZnV9IujxIyGd/dn0Phq5t6dzJVfupwq09mpDnzJv7egA8Znz/3ljO+w==" "integrity": "sha512-Dn9CtpC8fovh++Xi4NF5NJoeR9yU2yXZnV9IujxIyGd/dn0Phq5t6dzJVfupwq09mpDnzJv7egA8Znz/3ljO+w=="
}, },
"node_modules/@edx/browserslist-config": { "node_modules/@edx/browserslist-config": {
"version": "1.2.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/@edx/browserslist-config/-/browserslist-config-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@edx/browserslist-config/-/browserslist-config-1.5.0.tgz",
"integrity": "sha512-T1+6P52Yx7SMkmoIr4O0Q3m/DyRdrLTJbv1xVijdRLFEq1hqdafEs+Ln1423U5LSkTePb9AOkEtL1G0RZLFl1w==" "integrity": "sha512-d2ggwi5j4DOBJOwhWZxBWQSDR0DhT4ke/1PbzRauICdFkuOyax+PsFjK8GUh443K2OaQpy9PGfiCzZ1Yg37AUA==",
"license": "AGPL-3.0"
}, },
"node_modules/@edx/eslint-config": { "node_modules/@edx/eslint-config": {
"version": "4.2.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/@edx/eslint-config/-/eslint-config-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@edx/eslint-config/-/eslint-config-4.3.0.tgz",
"integrity": "sha512-2wuIw49uyj6gRwS74qJ8WhBU+X2FOP4uot40sthIC4YU9qCM7WJOcOuAhkRPP1FvZKd3UQH3gZM7eJ85xzDBqA==", "integrity": "sha512-4W9wFG4ALr3xocakCsncgJbK67RHfSmDwHDXKHReFtjxl/FRkxhS6qayz189oChqfANieeV3zRCLaq44bLf+/A==",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/parser": "^5.62.0",
@@ -2295,37 +2201,11 @@
"react": ">=16.3" "react": ">=16.3"
} }
}, },
"node_modules/@edx/frontend-component-footer/node_modules/jest-environment-jsdom": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz",
"integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==",
"peer": true,
"dependencies": {
"@jest/environment": "^29.7.0",
"@jest/fake-timers": "^29.7.0",
"@jest/types": "^29.6.3",
"@types/jsdom": "^20.0.0",
"@types/node": "*",
"jest-mock": "^29.7.0",
"jest-util": "^29.7.0",
"jsdom": "^20.0.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
},
"peerDependencies": {
"canvas": "^2.5.0"
},
"peerDependenciesMeta": {
"canvas": {
"optional": true
}
}
},
"node_modules/@edx/frontend-component-header": { "node_modules/@edx/frontend-component-header": {
"version": "5.6.0", "version": "5.8.3",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-5.6.0.tgz", "resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-5.8.3.tgz",
"integrity": "sha512-ITLLrej6BbWVc/0baMkKg/ACTvUGSR188Rn/BC2Y82Tdu8gRsZB6+0GUsDX/6FJjeIazLXdUusKlfwVU90sXLA==", "integrity": "sha512-cPenGEirOE7sQwyjakK9Vy/oyvBy4gzhCkf5pxiYYuOhTe0uK/iLnnThJh7R0XdkGdxMMOvUBJVmV4TGC1BHJQ==",
"license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "6.6.0", "@fortawesome/fontawesome-svg-core": "6.6.0",
"@fortawesome/free-brands-svg-icons": "6.6.0", "@fortawesome/free-brands-svg-icons": "6.6.0",
@@ -2345,7 +2225,8 @@
"@openedx/paragon": ">= 21.5.7 < 23.0.0", "@openedx/paragon": ">= 21.5.7 < 23.0.0",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"react": "^16.9.0 || ^17.0.0", "react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0" "react-dom": "^16.9.0 || ^17.0.0",
"react-router-dom": "^6.14.2"
} }
}, },
"node_modules/@edx/frontend-component-header/node_modules/@fortawesome/free-brands-svg-icons": { "node_modules/@edx/frontend-component-header/node_modules/@fortawesome/free-brands-svg-icons": {
@@ -2410,106 +2291,37 @@
"axios": ">= 0.17.0" "axios": ">= 0.17.0"
} }
}, },
"node_modules/@edx/frontend-component-header/node_modules/jest-environment-jsdom": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz",
"integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==",
"license": "MIT",
"dependencies": {
"@jest/environment": "^29.7.0",
"@jest/fake-timers": "^29.7.0",
"@jest/types": "^29.6.3",
"@types/jsdom": "^20.0.0",
"@types/node": "*",
"jest-mock": "^29.7.0",
"jest-util": "^29.7.0",
"jsdom": "^20.0.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
},
"peerDependencies": {
"canvas": "^2.5.0"
},
"peerDependenciesMeta": {
"canvas": {
"optional": true
}
}
},
"node_modules/@edx/frontend-lib-learning-assistant": { "node_modules/@edx/frontend-lib-learning-assistant": {
"version": "2.2.4", "version": "2.19.2",
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-learning-assistant/-/frontend-lib-learning-assistant-2.2.4.tgz", "resolved": "https://registry.npmjs.org/@edx/frontend-lib-learning-assistant/-/frontend-lib-learning-assistant-2.19.2.tgz",
"integrity": "sha512-EvN22XhDzzdIYrJY1TKEF/5zXuozC4tlKRo96+dSe9I7UAMg4zsLKC1zqY15oUuyB8rHo1ERYhQBG18uMepXrw==", "integrity": "sha512-EF2JPEe0llJfw8GwDgFsBr+5VKuqwVGqCBAP0jhWuWnLnJ2KUJJ+oxm8t0Dlni8M59LiO8QlO52OilJ6+/5IJw==",
"license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@edx/brand": "npm:@edx/brand-openedx@1.2.0", "@edx/brand": "npm:@openedx/brand-openedx@^1.2.3",
"@fortawesome/fontawesome-svg-core": "1.2.36",
"@fortawesome/free-brands-svg-icons": "5.15.4",
"@fortawesome/free-regular-svg-icons": "5.15.4",
"@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/react-fontawesome": "0.2.0",
"@optimizely/react-sdk": "^2.9.2", "@optimizely/react-sdk": "^2.9.2",
"core-js": "^3.36.1",
"prop-types": "15.8.1",
"react-markdown": "^8.0.5", "react-markdown": "^8.0.5",
"uuid": "9.0.0" "uuid": "9.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@edx/frontend-platform": "^7.0.0 || ^8.0.0", "@edx/browserslist-config": "*",
"@openedx/paragon": "^22.0.0", "@edx/frontend-platform": "^8",
"@reduxjs/toolkit": "1.8.1", "@openedx/frontend-build": "^14",
"react": "16.14.0 || ^17.0.0", "@openedx/paragon": "^22",
"react-dom": "16.14.0 || ^17.0.0", "@reduxjs/toolkit": "^1.8",
"react-redux": "7.2.9", "prop-types": "^15",
"react-router": "5.2.1 || ^6.0.0", "react": "^17",
"react-router-dom": "5.3.0 || ^6.0.0", "react-dom": "^17",
"redux": "4.1.2", "react-redux": "^7",
"regenerator-runtime": "0.13.11" "react-router": "^6",
} "react-router-dom": "^6",
}, "redux": "^4"
"node_modules/@edx/frontend-lib-learning-assistant/node_modules/@edx/brand": {
"name": "@edx/brand-openedx",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@edx/brand-openedx/-/brand-openedx-1.2.0.tgz",
"integrity": "sha512-r4PDN3rCgDsLovW44ayxoNNHgG5I4Rvss6MG5CrQEX4oW8YhQVEod+jJtwR5vi0mFLN2GIaMlDpd7iIy03VqXg=="
},
"node_modules/@edx/frontend-lib-learning-assistant/node_modules/@fortawesome/fontawesome-common-types": {
"version": "0.2.36",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
"integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
"hasInstallScript": true,
"engines": {
"node": ">=6"
}
},
"node_modules/@edx/frontend-lib-learning-assistant/node_modules/@fortawesome/fontawesome-svg-core": {
"version": "1.2.36",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz",
"integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==",
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.36"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@edx/frontend-lib-learning-assistant/node_modules/@fortawesome/react-fontawesome": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz",
"integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==",
"dependencies": {
"prop-types": "^15.8.1"
},
"peerDependencies": {
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
"react": ">=16.3"
} }
}, },
"node_modules/@edx/frontend-lib-special-exams": { "node_modules/@edx/frontend-lib-special-exams": {
"version": "3.1.3", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-3.1.3.tgz", "resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-3.3.0.tgz",
"integrity": "sha512-qkcnjPybt/eEE5txl8srUGTAVRiT92SFkjvPJEx9xuoUWuKTt0AmE0z3CVM2KypmcklBLlUZHVqZQVmgHlbtdA==", "integrity": "sha512-znlwIDqODP34C1QV+qLh8v9uRubGhOYrOf84lxulfiSGIVcxWtzQkjM8NHL2BE8OPefsyDbdy7QjC6GQ/Qo2hA==",
"license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "1.2.34", "@fortawesome/fontawesome-svg-core": "1.2.34",
"@fortawesome/free-brands-svg-icons": "5.11.2", "@fortawesome/free-brands-svg-icons": "5.11.2",
@@ -2537,6 +2349,7 @@
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
"integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==", "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@@ -2546,6 +2359,7 @@
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.34.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.34.tgz",
"integrity": "sha512-0KNN0nc5eIzaJxlv43QcDmTkDY1CqeN6J7OCGSs+fwGPdtv0yOQqRjieopBCmw+yd7uD3N2HeNL3Zm5isDleLg==", "integrity": "sha512-0KNN0nc5eIzaJxlv43QcDmTkDY1CqeN6J7OCGSs+fwGPdtv0yOQqRjieopBCmw+yd7uD3N2HeNL3Zm5isDleLg==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.34" "@fortawesome/fontawesome-common-types": "^0.2.34"
}, },
@@ -2557,6 +2371,7 @@
"version": "5.11.2", "version": "5.11.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.11.2.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.11.2.tgz",
"integrity": "sha512-wKK5znpHiZ2S0VgOvbeAnYuzkk3H86rxWajD9PVpfBj3s/kySEWTFKh/uLPyxiTOx8Tsd0OGN4En/s9XudVHLQ==", "integrity": "sha512-wKK5znpHiZ2S0VgOvbeAnYuzkk3H86rxWajD9PVpfBj3s/kySEWTFKh/uLPyxiTOx8Tsd0OGN4En/s9XudVHLQ==",
"license": "(CC-BY-4.0 AND MIT)",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.25" "@fortawesome/fontawesome-common-types": "^0.2.25"
}, },
@@ -2568,6 +2383,7 @@
"version": "5.11.2", "version": "5.11.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.11.2.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.11.2.tgz",
"integrity": "sha512-k0vbThRv9AvnXYBWi1gn1rFW4X7co/aFkbm0ZNmAR5PoWb9vY9EDDDobg8Ay4ISaXtCPypvJ0W1FWkSpLQwZ6w==", "integrity": "sha512-k0vbThRv9AvnXYBWi1gn1rFW4X7co/aFkbm0ZNmAR5PoWb9vY9EDDDobg8Ay4ISaXtCPypvJ0W1FWkSpLQwZ6w==",
"license": "(CC-BY-4.0 AND MIT)",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.25" "@fortawesome/fontawesome-common-types": "^0.2.25"
}, },
@@ -2579,6 +2395,7 @@
"version": "5.11.2", "version": "5.11.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.11.2.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.11.2.tgz",
"integrity": "sha512-zBue4i0PAZJUXOmLBBvM7L0O7wmsDC8dFv9IhpW5QL4kT9xhhVUsYg/LX1+5KaukWq4/cbDcKT+RT1aRe543sg==", "integrity": "sha512-zBue4i0PAZJUXOmLBBvM7L0O7wmsDC8dFv9IhpW5QL4kT9xhhVUsYg/LX1+5KaukWq4/cbDcKT+RT1aRe543sg==",
"license": "(CC-BY-4.0 AND MIT)",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.25" "@fortawesome/fontawesome-common-types": "^0.2.25"
}, },
@@ -2590,6 +2407,7 @@
"version": "0.1.14", "version": "0.1.14",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz",
"integrity": "sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==", "integrity": "sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==",
"license": "MIT",
"dependencies": { "dependencies": {
"prop-types": "^15.7.2" "prop-types": "^15.7.2"
}, },
@@ -2599,16 +2417,16 @@
} }
}, },
"node_modules/@edx/frontend-platform": { "node_modules/@edx/frontend-platform": {
"version": "8.1.1", "version": "8.2.1",
"resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-8.1.1.tgz", "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-8.2.1.tgz",
"integrity": "sha512-fJunRCg1P+mK7DhIIk+GsLNHjmqaZEnAD8W5eJ7Kb69/62oQg3/xJlx3twfYlLmdXtIOeyuZVtGs/X9AfQUeFg==", "integrity": "sha512-Pid7feVSr81Kl+Oeuh5sZR2caDyCvOz191dmtSYFKKR5lAyOBetc14GMZ9s/TYneeiR0bCbzUIWSl17eRuZrJw==",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@cospired/i18n-iso-languages": "4.2.0", "@cospired/i18n-iso-languages": "4.2.0",
"@formatjs/intl-pluralrules": "4.3.3", "@formatjs/intl-pluralrules": "4.3.3",
"@formatjs/intl-relativetimeformat": "10.0.1", "@formatjs/intl-relativetimeformat": "10.0.1",
"axios": "1.6.7", "axios": "1.7.9",
"axios-cache-interceptor": "1.3.2", "axios-cache-interceptor": "1.6.2",
"form-urlencoded": "4.1.4", "form-urlencoded": "4.1.4",
"glob": "7.2.3", "glob": "7.2.3",
"history": "4.10.1", "history": "4.10.1",
@@ -2620,8 +2438,8 @@
"lodash.memoize": "4.1.2", "lodash.memoize": "4.1.2",
"lodash.merge": "4.6.2", "lodash.merge": "4.6.2",
"lodash.snakecase": "4.1.1", "lodash.snakecase": "4.1.1",
"pubsub-js": "1.9.4", "pubsub-js": "1.9.5",
"react-intl": "6.6.8", "react-intl": "6.8.9",
"universal-cookie": "4.0.4" "universal-cookie": "4.0.4"
}, },
"bin": { "bin": {
@@ -2632,24 +2450,13 @@
"@openedx/frontend-build": ">= 14.0.0", "@openedx/frontend-build": ">= 14.0.0",
"@openedx/paragon": ">= 21.5.7 < 23.0.0", "@openedx/paragon": ">= 21.5.7 < 23.0.0",
"prop-types": ">=15.7.2 <16.0.0", "prop-types": ">=15.7.2 <16.0.0",
"react": "^16.9.0 || ^17.0.0", "react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0", "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-redux": "^7.1.1 || ^8.1.1", "react-redux": "^7.1.1 || ^8.1.1",
"react-router-dom": "^6.0.0", "react-router-dom": "^6.0.0",
"redux": "^4.0.4" "redux": "^4.0.4"
} }
}, },
"node_modules/@edx/frontend-platform/node_modules/axios": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/@edx/new-relic-source-map-webpack-plugin": { "node_modules/@edx/new-relic-source-map-webpack-plugin": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-2.1.0.tgz", "resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-2.1.0.tgz",
@@ -2912,12 +2719,12 @@
} }
}, },
"node_modules/@formatjs/fast-memoize": { "node_modules/@formatjs/fast-memoize": {
"version": "2.2.0", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.3.tgz",
"integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", "integrity": "sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tslib": "^2.4.0" "tslib": "2"
} }
}, },
"node_modules/@formatjs/icu-messageformat-parser": { "node_modules/@formatjs/icu-messageformat-parser": {
@@ -2980,18 +2787,18 @@
} }
}, },
"node_modules/@formatjs/intl": { "node_modules/@formatjs/intl": {
"version": "2.10.4", "version": "2.10.15",
"resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.4.tgz", "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.15.tgz",
"integrity": "sha512-56483O+HVcL0c7VucAS2tyH020mt9XTozZO67cwtGg0a7KWDukS/FzW3OnvaHmTHDuYsoPIzO+ZHVfU6fT/bJw==", "integrity": "sha512-i6+xVqT+6KCz7nBfk4ybMXmbKO36tKvbMKtgFz9KV+8idYFyFbfwKooYk8kGjyA5+T5f1kEPQM5IDLXucTAQ9g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@formatjs/ecma402-abstract": "2.0.0", "@formatjs/ecma402-abstract": "2.2.4",
"@formatjs/fast-memoize": "2.2.0", "@formatjs/fast-memoize": "2.2.3",
"@formatjs/icu-messageformat-parser": "2.7.8", "@formatjs/icu-messageformat-parser": "2.9.4",
"@formatjs/intl-displaynames": "6.6.8", "@formatjs/intl-displaynames": "6.8.5",
"@formatjs/intl-listformat": "7.5.7", "@formatjs/intl-listformat": "7.7.5",
"intl-messageformat": "10.5.14", "intl-messageformat": "10.7.7",
"tslib": "^2.4.0" "tslib": "2"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^4.7 || 5" "typescript": "^4.7 || 5"
@@ -3003,63 +2810,65 @@
} }
}, },
"node_modules/@formatjs/intl-displaynames": { "node_modules/@formatjs/intl-displaynames": {
"version": "6.6.8", "version": "6.8.5",
"resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.6.8.tgz", "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.8.5.tgz",
"integrity": "sha512-Lgx6n5KxN16B3Pb05z3NLEBQkGoXnGjkTBNCZI+Cn17YjHJ3fhCeEJJUqRlIZmJdmaXQhjcQVDp6WIiNeRYT5g==", "integrity": "sha512-85b+GdAKCsleS6cqVxf/Aw/uBd+20EM0wDpgaxzHo3RIR3bxF4xCJqH/Grbzx8CXurTgDDZHPdPdwJC+May41w==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@formatjs/ecma402-abstract": "2.0.0", "@formatjs/ecma402-abstract": "2.2.4",
"@formatjs/intl-localematcher": "0.5.4", "@formatjs/intl-localematcher": "0.5.8",
"tslib": "^2.4.0" "tslib": "2"
} }
}, },
"node_modules/@formatjs/intl-displaynames/node_modules/@formatjs/ecma402-abstract": { "node_modules/@formatjs/intl-displaynames/node_modules/@formatjs/ecma402-abstract": {
"version": "2.0.0", "version": "2.2.4",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz",
"integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==", "integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@formatjs/intl-localematcher": "0.5.4", "@formatjs/fast-memoize": "2.2.3",
"tslib": "^2.4.0" "@formatjs/intl-localematcher": "0.5.8",
"tslib": "2"
} }
}, },
"node_modules/@formatjs/intl-displaynames/node_modules/@formatjs/intl-localematcher": { "node_modules/@formatjs/intl-displaynames/node_modules/@formatjs/intl-localematcher": {
"version": "0.5.4", "version": "0.5.8",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz",
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", "integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tslib": "^2.4.0" "tslib": "2"
} }
}, },
"node_modules/@formatjs/intl-listformat": { "node_modules/@formatjs/intl-listformat": {
"version": "7.5.7", "version": "7.7.5",
"resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.7.tgz", "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.7.5.tgz",
"integrity": "sha512-MG2TSChQJQT9f7Rlv+eXwUFiG24mKSzmF144PLb8m8OixyXqn4+YWU+5wZracZGCgVTVmx8viCf7IH3QXoiB2g==", "integrity": "sha512-Wzes10SMNeYgnxYiKsda4rnHP3Q3II4XT2tZyOgnH5fWuHDtIkceuWlRQNsvrI3uiwP4hLqp2XdQTCsfkhXulg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@formatjs/ecma402-abstract": "2.0.0", "@formatjs/ecma402-abstract": "2.2.4",
"@formatjs/intl-localematcher": "0.5.4", "@formatjs/intl-localematcher": "0.5.8",
"tslib": "^2.4.0" "tslib": "2"
} }
}, },
"node_modules/@formatjs/intl-listformat/node_modules/@formatjs/ecma402-abstract": { "node_modules/@formatjs/intl-listformat/node_modules/@formatjs/ecma402-abstract": {
"version": "2.0.0", "version": "2.2.4",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz",
"integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==", "integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@formatjs/intl-localematcher": "0.5.4", "@formatjs/fast-memoize": "2.2.3",
"tslib": "^2.4.0" "@formatjs/intl-localematcher": "0.5.8",
"tslib": "2"
} }
}, },
"node_modules/@formatjs/intl-listformat/node_modules/@formatjs/intl-localematcher": { "node_modules/@formatjs/intl-listformat/node_modules/@formatjs/intl-localematcher": {
"version": "0.5.4", "version": "0.5.8",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz",
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", "integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tslib": "^2.4.0" "tslib": "2"
} }
}, },
"node_modules/@formatjs/intl-localematcher": { "node_modules/@formatjs/intl-localematcher": {
@@ -3091,22 +2900,44 @@
} }
}, },
"node_modules/@formatjs/intl/node_modules/@formatjs/ecma402-abstract": { "node_modules/@formatjs/intl/node_modules/@formatjs/ecma402-abstract": {
"version": "2.0.0", "version": "2.2.4",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz",
"integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==", "integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@formatjs/intl-localematcher": "0.5.4", "@formatjs/fast-memoize": "2.2.3",
"tslib": "^2.4.0" "@formatjs/intl-localematcher": "0.5.8",
"tslib": "2"
}
},
"node_modules/@formatjs/intl/node_modules/@formatjs/icu-messageformat-parser": {
"version": "2.9.4",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.4.tgz",
"integrity": "sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.2.4",
"@formatjs/icu-skeleton-parser": "1.8.8",
"tslib": "2"
}
},
"node_modules/@formatjs/intl/node_modules/@formatjs/icu-skeleton-parser": {
"version": "1.8.8",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.8.tgz",
"integrity": "sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.2.4",
"tslib": "2"
} }
}, },
"node_modules/@formatjs/intl/node_modules/@formatjs/intl-localematcher": { "node_modules/@formatjs/intl/node_modules/@formatjs/intl-localematcher": {
"version": "0.5.4", "version": "0.5.8",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz",
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", "integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tslib": "^2.4.0" "tslib": "2"
} }
}, },
"node_modules/@formatjs/ts-transformer": { "node_modules/@formatjs/ts-transformer": {
@@ -3929,9 +3760,10 @@
} }
}, },
"node_modules/@openedx/frontend-build": { "node_modules/@openedx/frontend-build": {
"version": "14.1.2", "version": "14.3.3",
"resolved": "https://registry.npmjs.org/@openedx/frontend-build/-/frontend-build-14.1.2.tgz", "resolved": "https://registry.npmjs.org/@openedx/frontend-build/-/frontend-build-14.3.3.tgz",
"integrity": "sha512-pLTcY/BLjCFn2qsBmUY0on2qB4PNShlg8scTYjrsz5fCASuSAJk000jOF5ys6bg6i8krSOIJlcZXPgrq0ywsUg==", "integrity": "sha512-I1xjpNV8m0ztbTFsh3YTeqcgc+9GqOtywdFnoXKzeCvjuQF/LVTmGg59/Cv0l9VisT82bI0cpi5DM4SwgHyQRA==",
"license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@babel/cli": "7.24.8", "@babel/cli": "7.24.8",
"@babel/core": "7.24.9", "@babel/core": "7.24.9",
@@ -3940,8 +3772,8 @@
"@babel/plugin-proposal-object-rest-spread": "7.20.7", "@babel/plugin-proposal-object-rest-spread": "7.20.7",
"@babel/plugin-syntax-dynamic-import": "7.8.3", "@babel/plugin-syntax-dynamic-import": "7.8.3",
"@babel/preset-env": "7.24.8", "@babel/preset-env": "7.24.8",
"@babel/preset-react": "7.24.7", "@babel/preset-react": "7.26.3",
"@edx/eslint-config": "4.2.0", "@edx/eslint-config": "^4.3.0",
"@edx/new-relic-source-map-webpack-plugin": "2.1.0", "@edx/new-relic-source-map-webpack-plugin": "2.1.0",
"@edx/typescript-config": "1.1.0", "@edx/typescript-config": "1.1.0",
"@formatjs/cli": "^6.0.3", "@formatjs/cli": "^6.0.3",
@@ -3951,9 +3783,9 @@
"@types/jest": "29.5.12", "@types/jest": "29.5.12",
"@typescript-eslint/eslint-plugin": "^5.58.0", "@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0", "@typescript-eslint/parser": "^5.58.0",
"autoprefixer": "10.4.19", "autoprefixer": "10.4.20",
"babel-jest": "29.6.1", "babel-jest": "29.7.0",
"babel-loader": "9.1.3", "babel-loader": "9.2.1",
"babel-plugin-formatjs": "^10.4.0", "babel-plugin-formatjs": "^10.4.0",
"babel-plugin-transform-imports": "2.0.0", "babel-plugin-transform-imports": "2.0.0",
"babel-polyfill": "6.26.0", "babel-polyfill": "6.26.0",
@@ -3967,27 +3799,27 @@
"eslint-config-airbnb": "19.0.4", "eslint-config-airbnb": "19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0", "eslint-config-airbnb-typescript": "^17.0.0",
"eslint-plugin-formatjs": "^4.12.2", "eslint-plugin-formatjs": "^4.12.2",
"eslint-plugin-import": "2.27.5", "eslint-plugin-import": "2.31.0",
"eslint-plugin-jsx-a11y": "6.7.1", "eslint-plugin-jsx-a11y": "6.7.1",
"eslint-plugin-react": "7.32.2", "eslint-plugin-react": "7.33.2",
"eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-hooks": "4.6.1",
"express": "^4.18.2", "express": "^4.18.2",
"file-loader": "6.2.0", "file-loader": "6.2.0",
"html-webpack-plugin": "5.6.0", "html-webpack-plugin": "5.6.3",
"identity-obj-proxy": "3.0.0", "identity-obj-proxy": "3.0.0",
"image-minimizer-webpack-plugin": "3.8.3", "image-minimizer-webpack-plugin": "3.8.3",
"jest": "29.6.1", "jest": "29.7.0",
"jest-environment-jsdom": "29.6.1", "jest-environment-jsdom": "29.7.0",
"mini-css-extract-plugin": "1.6.2", "mini-css-extract-plugin": "1.6.2",
"parse5": "7.1.2", "parse5": "7.1.2",
"postcss": "8.4.39", "postcss": "8.4.49",
"postcss-custom-media": "10.0.8", "postcss-custom-media": "10.0.8",
"postcss-loader": "7.3.4", "postcss-loader": "7.3.4",
"postcss-rtlcss": "5.1.2", "postcss-rtlcss": "5.1.2",
"react-dev-utils": "12.0.1", "react-dev-utils": "12.0.1",
"react-refresh": "0.14.2", "react-refresh": "0.16.0",
"resolve-url-loader": "5.0.0", "resolve-url-loader": "5.0.0",
"sass": "1.69.7", "sass": "1.85.1",
"sass-loader": "13.3.3", "sass-loader": "13.3.3",
"sharp": "0.32.6", "sharp": "0.32.6",
"source-map-loader": "4.0.2", "source-map-loader": "4.0.2",
@@ -3995,7 +3827,7 @@
"ts-jest": "29.1.4", "ts-jest": "29.1.4",
"typescript": "4.9.5", "typescript": "4.9.5",
"url-loader": "4.1.1", "url-loader": "4.1.1",
"webpack": "^5.89.0", "webpack": "^5.97.1",
"webpack-bundle-analyzer": "^4.10.1", "webpack-bundle-analyzer": "^4.10.1",
"webpack-cli": "^5.1.4", "webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1", "webpack-dev-server": "^4.15.1",
@@ -4006,7 +3838,7 @@
"fedx-scripts": "bin/fedx-scripts.js" "fedx-scripts": "bin/fedx-scripts.js"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.9.0 || ^17.0.0" "react": "^16.9.0 || ^17.0.0 || ^18.0.0"
} }
}, },
"node_modules/@openedx/frontend-build/node_modules/iconv-lite": { "node_modules/@openedx/frontend-build/node_modules/iconv-lite": {
@@ -4021,32 +3853,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/@openedx/frontend-build/node_modules/jest": {
"version": "29.6.1",
"resolved": "https://registry.npmjs.org/jest/-/jest-29.6.1.tgz",
"integrity": "sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw==",
"license": "MIT",
"dependencies": {
"@jest/core": "^29.6.1",
"@jest/types": "^29.6.1",
"import-local": "^3.0.2",
"jest-cli": "^29.6.1"
},
"bin": {
"jest": "bin/jest.js"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
},
"peerDependencies": {
"node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
},
"peerDependenciesMeta": {
"node-notifier": {
"optional": true
}
}
},
"node_modules/@openedx/frontend-build/node_modules/postcss-loader": { "node_modules/@openedx/frontend-build/node_modules/postcss-loader": {
"version": "7.3.4", "version": "7.3.4",
"resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz",
@@ -4069,23 +3875,6 @@
"webpack": "^5.0.0" "webpack": "^5.0.0"
} }
}, },
"node_modules/@openedx/frontend-build/node_modules/sass": {
"version": "1.69.7",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.7.tgz",
"integrity": "sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==",
"license": "MIT",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@openedx/frontend-build/node_modules/sass-loader": { "node_modules/@openedx/frontend-build/node_modules/sass-loader": {
"version": "13.3.3", "version": "13.3.3",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.3.3.tgz", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.3.3.tgz",
@@ -4124,9 +3913,9 @@
} }
}, },
"node_modules/@openedx/frontend-build/node_modules/semver": { "node_modules/@openedx/frontend-build/node_modules/semver": {
"version": "7.6.3", "version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@@ -4156,59 +3945,89 @@
} }
}, },
"node_modules/@openedx/frontend-plugin-framework": { "node_modules/@openedx/frontend-plugin-framework": {
"version": "1.3.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/@openedx/frontend-plugin-framework/-/frontend-plugin-framework-1.3.0.tgz", "resolved": "https://registry.npmjs.org/@openedx/frontend-plugin-framework/-/frontend-plugin-framework-1.5.0.tgz",
"integrity": "sha512-qLtX/4HIuWXiIhBdtBuL6mPVbV2un0rsFYx3I5+3tIUf7+T7WRq81a6JHU5QGyAmZy9dfiv7QwbqwiEQOVXVuQ==", "integrity": "sha512-+sZeyEA/AwoiRGQFvMUuUMD8i63p1LMgUo93sUg7OylMyMaaAnqLGR8QwsJ3CNRfPDBobPBcWPBY1C81VfDlXQ==",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2", "@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"classnames": "^2.3.2", "classnames": "^2.3.2",
"core-js": "3.37.1", "core-js": "3.37.1",
"react-redux": "7.2.9", "react-redux": "8.1.1",
"redux": "4.2.1", "redux": "4.2.1"
"regenerator-runtime": "0.14.1"
}, },
"peerDependencies": { "peerDependencies": {
"@edx/frontend-platform": "^7.0.0 || ^8.0.0", "@edx/frontend-platform": "^7.0.0 || ^8.0.0",
"@openedx/paragon": "^21.0.0 || ^22.0.0", "@openedx/paragon": "^21.0.0 || ^22.0.0",
"prop-types": "^15.8.0", "prop-types": "^15.8.0",
"react": "^17.0.0", "react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0", "react-dom": "^17.0.0 || ^18.0.0",
"react-error-boundary": "^4.0.11" "react-error-boundary": "^4.0.11"
} }
}, },
"node_modules/@openedx/frontend-plugin-framework/node_modules/redux": { "node_modules/@openedx/frontend-plugin-framework/node_modules/react-is": {
"version": "4.2.1", "version": "18.3.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.9.2"
}
},
"node_modules/@openedx/frontend-plugin-framework/node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@openedx/frontend-plugin-framework/node_modules/react-redux": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz",
"integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.1",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/use-sync-external-store": "^0.0.3",
"hoist-non-react-statics": "^3.3.2",
"react-is": "^18.0.0",
"use-sync-external-store": "^1.0.0"
},
"peerDependencies": {
"@types/react": "^16.8 || ^17.0 || ^18.0",
"@types/react-dom": "^16.8 || ^17.0 || ^18.0",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0",
"react-native": ">=0.59",
"redux": "^4 || ^5.0.0-beta.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
},
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/@openedx/frontend-slot-footer": { "node_modules/@openedx/frontend-slot-footer": {
"version": "1.0.5", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@openedx/frontend-slot-footer/-/frontend-slot-footer-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@openedx/frontend-slot-footer/-/frontend-slot-footer-1.1.0.tgz",
"integrity": "sha512-hlKj2p7zgAHfPTbmW4rAMUCCj35uCbm9VfRkZYPEIX2bdhkPjc4OIGTE5CKGkyWG5+zL+aap1ywwcKEjEgOMOQ==", "integrity": "sha512-EpWWRctxjucF7/5RMTWzdwiYHJDNgQ+b6a4PJvZX04u4u9ydURTSDeOlbmsu6ps/DsLU3NnQhG5VyRRwXGEhcg==",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@openedx/frontend-plugin-framework": "^1.1.2" "@openedx/frontend-plugin-framework": "^1.5.0"
}, },
"peerDependencies": { "peerDependencies": {
"@edx/frontend-component-footer": "*", "@edx/frontend-component-footer": "*",
"react": "^17.0.0" "react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
} }
}, },
"node_modules/@openedx/paragon": { "node_modules/@openedx/paragon": {
"version": "22.8.1", "version": "22.16.0",
"resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.8.1.tgz", "resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.16.0.tgz",
"integrity": "sha512-lm2x0tvNZrtJvp0L+cjvLLmkE9NoUbNIzt9L1FaOx9g92gf8rFVgq4aadq7IVAjN12HW19/QJMEJaQ0SVsvY2A==", "integrity": "sha512-6dhspna6rMJexeasq5acrkD+NMHOlVOt/RdraXg87Rqw8iR4XWxD8T0wY21DlYysPRCOZJVMXdc1jsjdI4MjXg==",
"license": "Apache-2.0", "license": "Apache-2.0",
"workspaces": [ "workspaces": [
"example", "example",
@@ -4252,8 +4071,8 @@
"paragon": "bin/paragon-scripts.js" "paragon": "bin/paragon-scripts.js"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.6 || ^17.0.0", "react": "^16.8.6 || ^17 || ^18",
"react-dom": "^16.8.6 || ^17.0.0", "react-dom": "^16.8.6 || ^17 || ^18",
"react-intl": "^5.25.1 || ^6.4.0" "react-intl": "^5.25.1 || ^6.4.0"
} }
}, },
@@ -4428,9 +4247,9 @@
} }
}, },
"node_modules/@pact-foundation/pact": { "node_modules/@pact-foundation/pact": {
"version": "13.1.3", "version": "13.2.0",
"resolved": "https://registry.npmjs.org/@pact-foundation/pact/-/pact-13.1.3.tgz", "resolved": "https://registry.npmjs.org/@pact-foundation/pact/-/pact-13.2.0.tgz",
"integrity": "sha512-AYSGAfA6b29wQR9RJqoVxasdAV9bx5+wpXvGc654O4+WmpxHs4BF6e8JVAuYVtfb3ego/HyPp+qQ08qKW1Tnxg==", "integrity": "sha512-WT9EGwFygbiMZXF5RyTNJgH2ZsHwOVL9guvvY4wpoyjejaXtA5QrnKuOzl8QRoBom+vVTenUJSyI0cMdwZg71w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -4438,8 +4257,8 @@
"@types/express": "^4.17.11", "@types/express": "^4.17.11",
"axios": "^1.7.4", "axios": "^1.7.4",
"body-parser": "^1.20.3", "body-parser": "^1.20.3",
"cli-color": "^2.0.1", "chalk": "4.1.2",
"express": "^4.21.0", "express": "^4.21.1",
"graphql": "^14.0.0", "graphql": "^14.0.0",
"graphql-tag": "^2.9.1", "graphql-tag": "^2.9.1",
"http-proxy": "^1.18.1", "http-proxy": "^1.18.1",
@@ -4512,6 +4331,315 @@
"node": ">= 14" "node": ">= 14"
} }
}, },
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher/node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"license": "Apache-2.0",
"optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/@pmmmwh/react-refresh-webpack-plugin": { "node_modules/@pmmmwh/react-refresh-webpack-plugin": {
"version": "0.5.15", "version": "0.5.15",
"resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz",
@@ -4627,18 +4755,19 @@
} }
}, },
"node_modules/@reduxjs/toolkit": { "node_modules/@reduxjs/toolkit": {
"version": "1.8.1", "version": "1.9.7",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.1.tgz", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz",
"integrity": "sha512-Q6mzbTpO9nOYRnkwpDlFOAbQnd3g7zj7CtHAZWz5SzE5lcV97Tf8f3SzOO8BoPOMYBFgfZaqTUZqgGu+a0+Fng==", "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"immer": "^9.0.7", "immer": "^9.0.21",
"redux": "^4.1.2", "redux": "^4.2.1",
"redux-thunk": "^2.4.1", "redux-thunk": "^2.4.2",
"reselect": "^4.1.5" "reselect": "^4.1.8"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18", "react": "^16.9.0 || ^17.0.0 || ^18",
"react-redux": "^7.2.1 || ^8.0.0-beta" "react-redux": "^7.2.1 || ^8.0.2"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"react": { "react": {
@@ -4676,6 +4805,12 @@
"react": ">=16.8.0" "react": ">=16.8.0"
} }
}, },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
"integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
"license": "MIT"
},
"node_modules/@sideway/address": { "node_modules/@sideway/address": {
"version": "4.1.5", "version": "4.1.5",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
@@ -4700,6 +4835,18 @@
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
"integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="
}, },
"node_modules/@sindresorhus/merge-streams": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
"integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@sinonjs/commons": { "node_modules/@sinonjs/commons": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
@@ -5074,15 +5221,13 @@
} }
}, },
"node_modules/@testing-library/user-event": { "node_modules/@testing-library/user-event": {
"version": "13.5.0", "version": "14.6.1",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
"integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
"dev": true, "dev": true,
"dependencies": { "license": "MIT",
"@babel/runtime": "^7.12.5"
},
"engines": { "engines": {
"node": ">=10", "node": ">=12",
"npm": ">=6" "npm": ">=6"
}, },
"peerDependencies": { "peerDependencies": {
@@ -5219,6 +5364,16 @@
"@types/json-schema": "*" "@types/json-schema": "*"
} }
}, },
"node_modules/@types/eslint-scope": {
"version": "3.7.7",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"license": "MIT",
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
}
},
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
@@ -5564,6 +5719,12 @@
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
"integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="
}, },
"node_modules/@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
"license": "MIT"
},
"node_modules/@types/warning": { "node_modules/@types/warning": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
@@ -5594,6 +5755,7 @@
"version": "5.62.0", "version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
"integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
"license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.4.0", "@eslint-community/regexpp": "^4.4.0",
"@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/scope-manager": "5.62.0",
@@ -5627,6 +5789,7 @@
"version": "7.6.3", "version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
}, },
@@ -5638,6 +5801,7 @@
"version": "5.62.0", "version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
"integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
"license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/scope-manager": "5.62.0",
"@typescript-eslint/types": "5.62.0", "@typescript-eslint/types": "5.62.0",
@@ -5664,6 +5828,7 @@
"version": "5.62.0", "version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz",
"integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==",
"license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "5.62.0", "@typescript-eslint/types": "5.62.0",
"@typescript-eslint/visitor-keys": "5.62.0" "@typescript-eslint/visitor-keys": "5.62.0"
@@ -5680,6 +5845,7 @@
"version": "5.62.0", "version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz",
"integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==",
"license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "5.62.0", "@typescript-eslint/typescript-estree": "5.62.0",
"@typescript-eslint/utils": "5.62.0", "@typescript-eslint/utils": "5.62.0",
@@ -5706,6 +5872,7 @@
"version": "5.62.0", "version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
"integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==",
"license": "MIT",
"engines": { "engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}, },
@@ -5718,6 +5885,7 @@
"version": "5.62.0", "version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz",
"integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
"license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "5.62.0", "@typescript-eslint/types": "5.62.0",
"@typescript-eslint/visitor-keys": "5.62.0", "@typescript-eslint/visitor-keys": "5.62.0",
@@ -5744,6 +5912,7 @@
"version": "11.1.0", "version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"license": "MIT",
"dependencies": { "dependencies": {
"array-union": "^2.1.0", "array-union": "^2.1.0",
"dir-glob": "^3.0.1", "dir-glob": "^3.0.1",
@@ -5763,6 +5932,7 @@
"version": "7.6.3", "version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
}, },
@@ -5774,6 +5944,7 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"license": "MIT",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -5782,6 +5953,7 @@
"version": "5.62.0", "version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz",
"integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@types/json-schema": "^7.0.9", "@types/json-schema": "^7.0.9",
@@ -5807,6 +5979,7 @@
"version": "7.6.3", "version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
}, },
@@ -5818,6 +5991,7 @@
"version": "5.62.0", "version": "5.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz",
"integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "5.62.0", "@typescript-eslint/types": "5.62.0",
"eslint-visitor-keys": "^3.3.0" "eslint-visitor-keys": "^3.3.0"
@@ -5834,6 +6008,7 @@
"version": "3.4.3", "version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"license": "Apache-2.0",
"engines": { "engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}, },
@@ -5842,133 +6017,148 @@
} }
}, },
"node_modules/@webassemblyjs/ast": { "node_modules/@webassemblyjs/ast": {
"version": "1.12.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
"integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-numbers": "1.13.2",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6" "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
} }
}, },
"node_modules/@webassemblyjs/floating-point-hex-parser": { "node_modules/@webassemblyjs/floating-point-hex-parser": {
"version": "1.11.6", "version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
"license": "MIT"
}, },
"node_modules/@webassemblyjs/helper-api-error": { "node_modules/@webassemblyjs/helper-api-error": {
"version": "1.11.6", "version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
"license": "MIT"
}, },
"node_modules/@webassemblyjs/helper-buffer": { "node_modules/@webassemblyjs/helper-buffer": {
"version": "1.12.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
"integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==" "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
"license": "MIT"
}, },
"node_modules/@webassemblyjs/helper-numbers": { "node_modules/@webassemblyjs/helper-numbers": {
"version": "1.11.6", "version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
"integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.11.6", "@webassemblyjs/floating-point-hex-parser": "1.13.2",
"@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-api-error": "1.13.2",
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
"node_modules/@webassemblyjs/helper-wasm-bytecode": { "node_modules/@webassemblyjs/helper-wasm-bytecode": {
"version": "1.11.6", "version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
"license": "MIT"
}, },
"node_modules/@webassemblyjs/helper-wasm-section": { "node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.12.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
"integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.12.1", "@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-buffer": "1.14.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/wasm-gen": "1.12.1" "@webassemblyjs/wasm-gen": "1.14.1"
} }
}, },
"node_modules/@webassemblyjs/ieee754": { "node_modules/@webassemblyjs/ieee754": {
"version": "1.11.6", "version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
"integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@xtuc/ieee754": "^1.2.0" "@xtuc/ieee754": "^1.2.0"
} }
}, },
"node_modules/@webassemblyjs/leb128": { "node_modules/@webassemblyjs/leb128": {
"version": "1.11.6", "version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
"integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
"license": "Apache-2.0",
"dependencies": { "dependencies": {
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
"node_modules/@webassemblyjs/utf8": { "node_modules/@webassemblyjs/utf8": {
"version": "1.11.6", "version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
"license": "MIT"
}, },
"node_modules/@webassemblyjs/wasm-edit": { "node_modules/@webassemblyjs/wasm-edit": {
"version": "1.12.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
"integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.12.1", "@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-buffer": "1.14.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/helper-wasm-section": "1.12.1", "@webassemblyjs/helper-wasm-section": "1.14.1",
"@webassemblyjs/wasm-gen": "1.12.1", "@webassemblyjs/wasm-gen": "1.14.1",
"@webassemblyjs/wasm-opt": "1.12.1", "@webassemblyjs/wasm-opt": "1.14.1",
"@webassemblyjs/wasm-parser": "1.12.1", "@webassemblyjs/wasm-parser": "1.14.1",
"@webassemblyjs/wast-printer": "1.12.1" "@webassemblyjs/wast-printer": "1.14.1"
} }
}, },
"node_modules/@webassemblyjs/wasm-gen": { "node_modules/@webassemblyjs/wasm-gen": {
"version": "1.12.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
"integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.12.1", "@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/ieee754": "1.13.2",
"@webassemblyjs/leb128": "1.11.6", "@webassemblyjs/leb128": "1.13.2",
"@webassemblyjs/utf8": "1.11.6" "@webassemblyjs/utf8": "1.13.2"
} }
}, },
"node_modules/@webassemblyjs/wasm-opt": { "node_modules/@webassemblyjs/wasm-opt": {
"version": "1.12.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
"integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.12.1", "@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-buffer": "1.14.1",
"@webassemblyjs/wasm-gen": "1.12.1", "@webassemblyjs/wasm-gen": "1.14.1",
"@webassemblyjs/wasm-parser": "1.12.1" "@webassemblyjs/wasm-parser": "1.14.1"
} }
}, },
"node_modules/@webassemblyjs/wasm-parser": { "node_modules/@webassemblyjs/wasm-parser": {
"version": "1.12.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
"integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.12.1", "@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-api-error": "1.13.2",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/ieee754": "1.13.2",
"@webassemblyjs/leb128": "1.11.6", "@webassemblyjs/leb128": "1.13.2",
"@webassemblyjs/utf8": "1.11.6" "@webassemblyjs/utf8": "1.13.2"
} }
}, },
"node_modules/@webassemblyjs/wast-printer": { "node_modules/@webassemblyjs/wast-printer": {
"version": "1.12.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
"integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.12.1", "@webassemblyjs/ast": "1.14.1",
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
@@ -6016,12 +6206,14 @@
"node_modules/@xtuc/ieee754": { "node_modules/@xtuc/ieee754": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
"license": "BSD-3-Clause"
}, },
"node_modules/@xtuc/long": { "node_modules/@xtuc/long": {
"version": "4.2.2", "version": "4.2.2",
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"license": "Apache-2.0"
}, },
"node_modules/@yarnpkg/lockfile": { "node_modules/@yarnpkg/lockfile": {
"version": "1.1.0", "version": "1.1.0",
@@ -6058,9 +6250,10 @@
} }
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.10.0", "version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"license": "MIT",
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -6077,14 +6270,6 @@
"acorn-walk": "^8.0.2" "acorn-walk": "^8.0.2"
} }
}, },
"node_modules/acorn-import-attributes": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
"integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
"peerDependencies": {
"acorn": "^8"
}
},
"node_modules/acorn-jsx": { "node_modules/acorn-jsx": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
@@ -6303,12 +6488,13 @@
} }
}, },
"node_modules/array-buffer-byte-length": { "node_modules/array-buffer-byte-length": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
"integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.5", "call-bound": "^1.0.3",
"is-array-buffer": "^3.0.4" "is-array-buffer": "^3.0.5"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -6357,6 +6543,26 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/array.prototype.findlastindex": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
"integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.7",
"define-properties": "^1.2.1",
"es-abstract": "^1.23.2",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.0.0",
"es-shim-unscopables": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/array.prototype.flat": { "node_modules/array.prototype.flat": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
@@ -6395,6 +6601,7 @@
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
"integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bind": "^1.0.7",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
@@ -6407,18 +6614,18 @@
} }
}, },
"node_modules/arraybuffer.prototype.slice": { "node_modules/arraybuffer.prototype.slice": {
"version": "1.0.3", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
"integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"array-buffer-byte-length": "^1.0.1", "array-buffer-byte-length": "^1.0.1",
"call-bind": "^1.0.5", "call-bind": "^1.0.8",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
"es-abstract": "^1.22.3", "es-abstract": "^1.23.5",
"es-errors": "^1.2.1", "es-errors": "^1.3.0",
"get-intrinsic": "^1.2.3", "get-intrinsic": "^1.2.6",
"is-array-buffer": "^3.0.4", "is-array-buffer": "^3.0.4"
"is-shared-array-buffer": "^1.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -6435,7 +6642,17 @@
"node_modules/ast-types-flow": { "node_modules/ast-types-flow": {
"version": "0.0.7", "version": "0.0.7",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
"integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
"license": "ISC"
},
"node_modules/async-function": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
"integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
}, },
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
@@ -6468,9 +6685,9 @@
} }
}, },
"node_modules/autoprefixer": { "node_modules/autoprefixer": {
"version": "10.4.19", "version": "10.4.20",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
"integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -6485,12 +6702,13 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"dependencies": { "dependencies": {
"browserslist": "^4.23.0", "browserslist": "^4.23.3",
"caniuse-lite": "^1.0.30001599", "caniuse-lite": "^1.0.30001646",
"fraction.js": "^4.3.7", "fraction.js": "^4.3.7",
"normalize-range": "^0.1.2", "normalize-range": "^0.1.2",
"picocolors": "^1.0.0", "picocolors": "^1.0.1",
"postcss-value-parser": "^4.2.0" "postcss-value-parser": "^4.2.0"
}, },
"bin": { "bin": {
@@ -6518,17 +6736,18 @@
} }
}, },
"node_modules/axe-core": { "node_modules/axe-core": {
"version": "4.10.0", "version": "4.10.2",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz",
"integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==", "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==",
"license": "MPL-2.0",
"engines": { "engines": {
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.7.7", "version": "1.7.9",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.6", "follow-redirects": "^1.15.6",
@@ -6537,14 +6756,14 @@
} }
}, },
"node_modules/axios-cache-interceptor": { "node_modules/axios-cache-interceptor": {
"version": "1.3.2", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.3.2.tgz", "resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.6.2.tgz",
"integrity": "sha512-FNy4/IKvFYVswpPS09j3H9OUzcXSuxQ93wYxCKnogHbjCRE9nDQ/lukgjyuJqMIk3Yao51qQI/zPbMRNQu4JJw==", "integrity": "sha512-YLbAODIHZZIcD4b3WYFVQOa5W2TY/WnJ6sBHqAg6Z+hx+RVj8/OcjQyRopO6awn7/kOkGL5X9TP16AucnlJ/lw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cache-parser": "^1.2.4", "cache-parser": "1.2.5",
"fast-defer": "^1.1.7", "fast-defer": "1.1.8",
"object-code": "^1.3.0" "object-code": "1.3.3"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@@ -6557,9 +6776,9 @@
} }
}, },
"node_modules/axios-mock-adapter": { "node_modules/axios-mock-adapter": {
"version": "2.0.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-2.0.0.tgz", "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-2.1.0.tgz",
"integrity": "sha512-D/K0J5Zm6KvaMTnsWrBQZWLzKN9GxUFZEa0mx2qeEHXDeTugCoplWehy8y36dj5vuSjhe1u/Dol8cZ8lzzmDew==", "integrity": "sha512-AZUe4OjECGCNNssH8SOdtneiQELsqTsat3SQQCWLPjN436/H+L9AjWfV7bF+Zg/YL9cgbhrz5671hoh+Tbn98w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -6574,6 +6793,7 @@
"version": "3.2.4", "version": "3.2.4",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.4.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.4.tgz",
"integrity": "sha512-aPTElBrbifBU1krmZxGZOlBkslORe7Ll7+BDnI50Wy4LgOt69luMgevkDfTq1O/ZgprooPCtWpjCwKSZw/iZ4A==", "integrity": "sha512-aPTElBrbifBU1krmZxGZOlBkslORe7Ll7+BDnI50Wy4LgOt69luMgevkDfTq1O/ZgprooPCtWpjCwKSZw/iZ4A==",
"license": "Apache-2.0",
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
} }
@@ -6584,14 +6804,15 @@
"integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg=="
}, },
"node_modules/babel-jest": { "node_modules/babel-jest": {
"version": "29.6.1", "version": "29.7.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.1.tgz", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
"integrity": "sha512-qu+3bdPEQC6KZSPz+4Fyjbga5OODNcp49j6GKzG1EKbkfyJBxEYGVUmVGpwCSeGouG52R4EgYMLb6p9YeEEQ4A==", "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@jest/transform": "^29.6.1", "@jest/transform": "^29.7.0",
"@types/babel__core": "^7.1.14", "@types/babel__core": "^7.1.14",
"babel-plugin-istanbul": "^6.1.1", "babel-plugin-istanbul": "^6.1.1",
"babel-preset-jest": "^29.5.0", "babel-preset-jest": "^29.6.3",
"chalk": "^4.0.0", "chalk": "^4.0.0",
"graceful-fs": "^4.2.9", "graceful-fs": "^4.2.9",
"slash": "^3.0.0" "slash": "^3.0.0"
@@ -6612,9 +6833,10 @@
} }
}, },
"node_modules/babel-loader": { "node_modules/babel-loader": {
"version": "9.1.3", "version": "9.2.1",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz",
"integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==",
"license": "MIT",
"dependencies": { "dependencies": {
"find-cache-dir": "^4.0.0", "find-cache-dir": "^4.0.0",
"schema-utils": "^4.0.0" "schema-utils": "^4.0.0"
@@ -7082,9 +7304,9 @@
} }
}, },
"node_modules/browserslist": { "node_modules/browserslist": {
"version": "4.23.3", "version": "4.24.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
"integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -7101,10 +7323,10 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"caniuse-lite": "^1.0.30001646", "caniuse-lite": "^1.0.30001688",
"electron-to-chromium": "^1.5.4", "electron-to-chromium": "^1.5.73",
"node-releases": "^2.0.18", "node-releases": "^2.0.19",
"update-browserslist-db": "^1.1.0" "update-browserslist-db": "^1.1.1"
}, },
"bin": { "bin": {
"browserslist": "cli.js" "browserslist": "cli.js"
@@ -7213,20 +7435,50 @@
} }
}, },
"node_modules/cache-parser": { "node_modules/cache-parser": {
"version": "1.2.4", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/cache-parser/-/cache-parser-1.2.4.tgz", "resolved": "https://registry.npmjs.org/cache-parser/-/cache-parser-1.2.5.tgz",
"integrity": "sha512-O0KwuHuJnbHUrghHi2kGp0SxnWSIBXTYt7M8WVhW0kbPRUNUKoE/Of6e1rRD6AAxmfxFunKnt90yEK09D+sc5g==" "integrity": "sha512-Md/4VhAHByQ9frQ15WD6LrMNiVw9AEl/J7vWIXw+sxT6fSOpbtt6LHTp76vy8+bOESPBO94117Hm2bIjlI7XjA==",
"license": "MIT"
}, },
"node_modules/call-bind": { "node_modules/call-bind": {
"version": "1.0.7", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind-apply-helpers": "^1.0.0",
"es-define-property": "^1.0.0", "es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4", "get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1" "set-function-length": "^1.2.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/call-bound": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
"integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"get-intrinsic": "^1.2.6"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -7275,9 +7527,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001669", "version": "1.0.30001707",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz",
"integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", "integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -7291,7 +7543,8 @@
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
] ],
"license": "CC-BY-4.0"
}, },
"node_modules/cast-array": { "node_modules/cast-array": {
"version": "1.0.1", "version": "1.0.1",
@@ -7572,22 +7825,6 @@
"webpack": ">=4.0.0 <6.0.0" "webpack": ">=4.0.0 <6.0.0"
} }
}, },
"node_modules/cli-color": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz",
"integrity": "sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==",
"dev": true,
"dependencies": {
"d": "^1.0.1",
"es5-ext": "^0.10.61",
"es6-iterator": "^2.0.3",
"memoizee": "^0.4.15",
"timers-ext": "^0.1.7"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/cli-cursor": { "node_modules/cli-cursor": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
@@ -7835,7 +8072,8 @@
"node_modules/confusing-browser-globals": { "node_modules/confusing-browser-globals": {
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
"integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
"license": "MIT"
}, },
"node_modules/connect-history-api-fallback": { "node_modules/connect-history-api-fallback": {
"version": "2.0.0", "version": "2.0.0",
@@ -7870,9 +8108,9 @@
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
}, },
"node_modules/cookie": { "node_modules/cookie": {
"version": "0.6.0", "version": "0.7.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
@@ -7889,19 +8127,20 @@
"integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw=="
}, },
"node_modules/copy-webpack-plugin": { "node_modules/copy-webpack-plugin": {
"version": "11.0.0", "version": "12.0.2",
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz",
"integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==",
"license": "MIT",
"dependencies": { "dependencies": {
"fast-glob": "^3.2.11", "fast-glob": "^3.3.2",
"glob-parent": "^6.0.1", "glob-parent": "^6.0.1",
"globby": "^13.1.1", "globby": "^14.0.0",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"schema-utils": "^4.0.0", "schema-utils": "^4.2.0",
"serialize-javascript": "^6.0.0" "serialize-javascript": "^6.0.2"
}, },
"engines": { "engines": {
"node": ">= 14.15.0" "node": ">= 18.12.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@@ -7912,14 +8151,15 @@
} }
}, },
"node_modules/copy-webpack-plugin/node_modules/ajv": { "node_modules/copy-webpack-plugin/node_modules/ajv": {
"version": "8.12.0", "version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0", "json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2", "require-from-string": "^2.0.2"
"uri-js": "^4.2.2"
}, },
"funding": { "funding": {
"type": "github", "type": "github",
@@ -7930,6 +8170,7 @@
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
"license": "MIT",
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3" "fast-deep-equal": "^3.1.3"
}, },
@@ -7941,6 +8182,7 @@
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"license": "ISC",
"dependencies": { "dependencies": {
"is-glob": "^4.0.3" "is-glob": "^4.0.3"
}, },
@@ -7951,12 +8193,14 @@
"node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
}, },
"node_modules/copy-webpack-plugin/node_modules/schema-utils": { "node_modules/copy-webpack-plugin/node_modules/schema-utils": {
"version": "4.1.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.1.0.tgz", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz",
"integrity": "sha512-Jw+GZVbP5IggB2WAn6UHI02LBwGmsIeYN/lNbSMZyDziQ7jmtAUrqKqDja+W89YHVs+KL/3IkIMltAklqB1vAw==", "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@types/json-schema": "^7.0.9", "@types/json-schema": "^7.0.9",
"ajv": "^8.9.0", "ajv": "^8.9.0",
@@ -8072,9 +8316,9 @@
} }
}, },
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.3", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dependencies": { "dependencies": {
"path-key": "^3.1.0", "path-key": "^3.1.0",
"shebang-command": "^2.0.0", "shebang-command": "^2.0.0",
@@ -8321,20 +8565,11 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
}, },
"node_modules/d": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
"dev": true,
"dependencies": {
"es5-ext": "^0.10.50",
"type": "^1.0.1"
}
},
"node_modules/damerau-levenshtein": { "node_modules/damerau-levenshtein": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
"license": "BSD-2-Clause"
}, },
"node_modules/data-urls": { "node_modules/data-urls": {
"version": "3.0.2", "version": "3.0.2",
@@ -8350,13 +8585,14 @@
} }
}, },
"node_modules/data-view-buffer": { "node_modules/data-view-buffer": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
"integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.6", "call-bound": "^1.0.3",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"is-data-view": "^1.0.1" "is-data-view": "^1.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -8366,27 +8602,29 @@
} }
}, },
"node_modules/data-view-byte-length": { "node_modules/data-view-byte-length": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
"integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bound": "^1.0.3",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"is-data-view": "^1.0.1" "is-data-view": "^1.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/inspect-js"
} }
}, },
"node_modules/data-view-byte-offset": { "node_modules/data-view-byte-offset": {
"version": "1.0.0", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
"integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.6", "call-bound": "^1.0.2",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"is-data-view": "^1.0.1" "is-data-view": "^1.0.1"
}, },
@@ -8936,6 +9174,20 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/duplexer": { "node_modules/duplexer": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@@ -8947,9 +9199,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.7", "version": "1.5.112",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.7.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.112.tgz",
"integrity": "sha512-6FTNWIWMxMy/ZY6799nBlPtF1DFDQ6VQJ7yyDP27SJNt5lwtQ5ufqVvHylb3fdQefvRcgA3fKcFMJi9OLwBRNw==", "integrity": "sha512-oen93kVyqSb3l+ziUgzIOlWt/oOuy4zRmpwestMn4rhFWAoFJeFuCVte9F2fASjeZZo7l/Cif9TiyrdW4CwEMA==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/email-prop-type": { "node_modules/email-prop-type": {
@@ -9068,56 +9320,62 @@
} }
}, },
"node_modules/es-abstract": { "node_modules/es-abstract": {
"version": "1.23.3", "version": "1.23.9",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz",
"integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==",
"license": "MIT",
"dependencies": { "dependencies": {
"array-buffer-byte-length": "^1.0.1", "array-buffer-byte-length": "^1.0.2",
"arraybuffer.prototype.slice": "^1.0.3", "arraybuffer.prototype.slice": "^1.0.4",
"available-typed-arrays": "^1.0.7", "available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.7", "call-bind": "^1.0.8",
"data-view-buffer": "^1.0.1", "call-bound": "^1.0.3",
"data-view-byte-length": "^1.0.1", "data-view-buffer": "^1.0.2",
"data-view-byte-offset": "^1.0.0", "data-view-byte-length": "^1.0.2",
"es-define-property": "^1.0.0", "data-view-byte-offset": "^1.0.1",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"es-object-atoms": "^1.0.0", "es-object-atoms": "^1.0.0",
"es-set-tostringtag": "^2.0.3", "es-set-tostringtag": "^2.1.0",
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.3.0",
"function.prototype.name": "^1.1.6", "function.prototype.name": "^1.1.8",
"get-intrinsic": "^1.2.4", "get-intrinsic": "^1.2.7",
"get-symbol-description": "^1.0.2", "get-proto": "^1.0.0",
"globalthis": "^1.0.3", "get-symbol-description": "^1.1.0",
"gopd": "^1.0.1", "globalthis": "^1.0.4",
"gopd": "^1.2.0",
"has-property-descriptors": "^1.0.2", "has-property-descriptors": "^1.0.2",
"has-proto": "^1.0.3", "has-proto": "^1.2.0",
"has-symbols": "^1.0.3", "has-symbols": "^1.1.0",
"hasown": "^2.0.2", "hasown": "^2.0.2",
"internal-slot": "^1.0.7", "internal-slot": "^1.1.0",
"is-array-buffer": "^3.0.4", "is-array-buffer": "^3.0.5",
"is-callable": "^1.2.7", "is-callable": "^1.2.7",
"is-data-view": "^1.0.1", "is-data-view": "^1.0.2",
"is-negative-zero": "^2.0.3", "is-regex": "^1.2.1",
"is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.4",
"is-shared-array-buffer": "^1.0.3", "is-string": "^1.1.1",
"is-string": "^1.0.7", "is-typed-array": "^1.1.15",
"is-typed-array": "^1.1.13", "is-weakref": "^1.1.0",
"is-weakref": "^1.0.2", "math-intrinsics": "^1.1.0",
"object-inspect": "^1.13.1", "object-inspect": "^1.13.3",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.5", "object.assign": "^4.1.7",
"regexp.prototype.flags": "^1.5.2", "own-keys": "^1.0.1",
"safe-array-concat": "^1.1.2", "regexp.prototype.flags": "^1.5.3",
"safe-regex-test": "^1.0.3", "safe-array-concat": "^1.1.3",
"string.prototype.trim": "^1.2.9", "safe-push-apply": "^1.0.0",
"string.prototype.trimend": "^1.0.8", "safe-regex-test": "^1.1.0",
"set-proto": "^1.0.0",
"string.prototype.trim": "^1.2.10",
"string.prototype.trimend": "^1.0.9",
"string.prototype.trimstart": "^1.0.8", "string.prototype.trimstart": "^1.0.8",
"typed-array-buffer": "^1.0.2", "typed-array-buffer": "^1.0.3",
"typed-array-byte-length": "^1.0.1", "typed-array-byte-length": "^1.0.3",
"typed-array-byte-offset": "^1.0.2", "typed-array-byte-offset": "^1.0.4",
"typed-array-length": "^1.0.6", "typed-array-length": "^1.0.7",
"unbox-primitive": "^1.0.2", "unbox-primitive": "^1.1.0",
"which-typed-array": "^1.1.15" "which-typed-array": "^1.1.18"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -9127,12 +9385,10 @@
} }
}, },
"node_modules/es-define-property": { "node_modules/es-define-property": {
"version": "1.0.0", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"dependencies": { "license": "MIT",
"get-intrinsic": "^1.2.4"
},
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
} }
@@ -9164,15 +9420,43 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/es-iterator-helpers": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
"integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.8",
"call-bound": "^1.0.3",
"define-properties": "^1.2.1",
"es-abstract": "^1.23.6",
"es-errors": "^1.3.0",
"es-set-tostringtag": "^2.0.3",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.6",
"globalthis": "^1.0.4",
"gopd": "^1.2.0",
"has-property-descriptors": "^1.0.2",
"has-proto": "^1.2.0",
"has-symbols": "^1.1.0",
"internal-slot": "^1.1.0",
"iterator.prototype": "^1.1.4",
"safe-array-concat": "^1.1.3"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-module-lexer": { "node_modules/es-module-lexer": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz",
"integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==" "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA=="
}, },
"node_modules/es-object-atoms": { "node_modules/es-object-atoms": {
"version": "1.0.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": { "dependencies": {
"es-errors": "^1.3.0" "es-errors": "^1.3.0"
}, },
@@ -9181,13 +9465,15 @@
} }
}, },
"node_modules/es-set-tostringtag": { "node_modules/es-set-tostringtag": {
"version": "2.0.3", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": { "dependencies": {
"get-intrinsic": "^1.2.4", "es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2", "has-tostringtag": "^1.0.2",
"hasown": "^2.0.1" "hasown": "^2.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -9202,13 +9488,14 @@
} }
}, },
"node_modules/es-to-primitive": { "node_modules/es-to-primitive": {
"version": "1.2.1", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
"license": "MIT",
"dependencies": { "dependencies": {
"is-callable": "^1.1.4", "is-callable": "^1.2.7",
"is-date-object": "^1.0.1", "is-date-object": "^1.0.5",
"is-symbol": "^1.0.2" "is-symbol": "^1.0.4"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -9217,58 +9504,10 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/es5-ext": {
"version": "0.10.62",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
"integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"es6-iterator": "^2.0.3",
"es6-symbol": "^3.1.3",
"next-tick": "^1.1.0"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/es6-iterator": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
"dev": true,
"dependencies": {
"d": "1",
"es5-ext": "^0.10.35",
"es6-symbol": "^3.1.1"
}
},
"node_modules/es6-symbol": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
"dev": true,
"dependencies": {
"d": "^1.0.1",
"ext": "^1.1.2"
}
},
"node_modules/es6-weak-map": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
"integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
"dev": true,
"dependencies": {
"d": "1",
"es5-ext": "^0.10.46",
"es6-iterator": "^2.0.3",
"es6-symbol": "^3.1.1"
}
},
"node_modules/escalade": { "node_modules/escalade": {
"version": "3.1.2", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@@ -9378,6 +9617,7 @@
"version": "19.0.4", "version": "19.0.4",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
"integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
"license": "MIT",
"dependencies": { "dependencies": {
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-base": "^15.0.0",
"object.assign": "^4.1.2", "object.assign": "^4.1.2",
@@ -9398,6 +9638,7 @@
"version": "15.0.0", "version": "15.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
"integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
"license": "MIT",
"dependencies": { "dependencies": {
"confusing-browser-globals": "^1.0.10", "confusing-browser-globals": "^1.0.10",
"object.assign": "^4.1.2", "object.assign": "^4.1.2",
@@ -9416,6 +9657,7 @@
"version": "17.1.0", "version": "17.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz", "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz",
"integrity": "sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig==", "integrity": "sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig==",
"license": "MIT",
"dependencies": { "dependencies": {
"eslint-config-airbnb-base": "^15.0.0" "eslint-config-airbnb-base": "^15.0.0"
}, },
@@ -9445,18 +9687,19 @@
} }
}, },
"node_modules/eslint-import-resolver-webpack": { "node_modules/eslint-import-resolver-webpack": {
"version": "0.13.9", "version": "0.13.10",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.9.tgz", "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.10.tgz",
"integrity": "sha512-yGngeefNiHXau2yzKKs2BNON4HLpxBabY40BGL/vUSKZtqzjlVsTTZm57jhHULhm+mJEwKsEIIN3NXup5AiiBQ==", "integrity": "sha512-ciVTEg7sA56wRMR772PyjcBRmyBMLS46xgzQZqt6cWBEKc7cK65ZSSLCTLVRu2gGtKyXUb5stwf4xxLBfERLFA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"debug": "^3.2.7", "debug": "^3.2.7",
"enhanced-resolve": "^0.9.1", "enhanced-resolve": "^0.9.1",
"find-root": "^1.1.0", "find-root": "^1.1.0",
"hasown": "^2.0.0", "hasown": "^2.0.2",
"interpret": "^1.4.0", "interpret": "^1.4.0",
"is-core-module": "^2.13.1", "is-core-module": "^2.15.1",
"is-regex": "^1.1.4", "is-regex": "^1.2.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"resolve": "^2.0.0-next.5", "resolve": "^2.0.0-next.5",
"semver": "^5.7.2" "semver": "^5.7.2"
@@ -9474,6 +9717,7 @@
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"ms": "^2.1.1" "ms": "^2.1.1"
} }
@@ -9497,6 +9741,7 @@
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">= 0.10" "node": ">= 0.10"
} }
@@ -9506,6 +9751,7 @@
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
"integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"is-core-module": "^2.13.0", "is-core-module": "^2.13.0",
"path-parse": "^1.0.7", "path-parse": "^1.0.7",
@@ -9523,6 +9769,7 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true, "dev": true,
"license": "ISC",
"bin": { "bin": {
"semver": "bin/semver" "semver": "bin/semver"
} }
@@ -9532,14 +9779,16 @@
"resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz",
"integrity": "sha512-jX8Et4hHg57mug1/079yitEKWGB3LCwoxByLsNim89LABq8NqgiX+6iYVOsq0vX8uJHkU+DZ5fnq95f800bEsQ==", "integrity": "sha512-jX8Et4hHg57mug1/079yitEKWGB3LCwoxByLsNim89LABq8NqgiX+6iYVOsq0vX8uJHkU+DZ5fnq95f800bEsQ==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=0.6" "node": ">=0.6"
} }
}, },
"node_modules/eslint-module-utils": { "node_modules/eslint-module-utils": {
"version": "2.11.0", "version": "2.12.0",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz",
"integrity": "sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==", "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==",
"license": "MIT",
"dependencies": { "dependencies": {
"debug": "^3.2.7" "debug": "^3.2.7"
}, },
@@ -9556,6 +9805,7 @@
"version": "3.2.7", "version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"ms": "^2.1.1" "ms": "^2.1.1"
} }
@@ -9760,31 +10010,36 @@
} }
}, },
"node_modules/eslint-plugin-import": { "node_modules/eslint-plugin-import": {
"version": "2.27.5", "version": "2.31.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz",
"integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
"license": "MIT",
"dependencies": { "dependencies": {
"array-includes": "^3.1.6", "@rtsao/scc": "^1.1.0",
"array.prototype.flat": "^1.3.1", "array-includes": "^3.1.8",
"array.prototype.flatmap": "^1.3.1", "array.prototype.findlastindex": "^1.2.5",
"array.prototype.flat": "^1.3.2",
"array.prototype.flatmap": "^1.3.2",
"debug": "^3.2.7", "debug": "^3.2.7",
"doctrine": "^2.1.0", "doctrine": "^2.1.0",
"eslint-import-resolver-node": "^0.3.7", "eslint-import-resolver-node": "^0.3.9",
"eslint-module-utils": "^2.7.4", "eslint-module-utils": "^2.12.0",
"has": "^1.0.3", "hasown": "^2.0.2",
"is-core-module": "^2.11.0", "is-core-module": "^2.15.1",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"minimatch": "^3.1.2", "minimatch": "^3.1.2",
"object.values": "^1.1.6", "object.fromentries": "^2.0.8",
"resolve": "^1.22.1", "object.groupby": "^1.0.3",
"semver": "^6.3.0", "object.values": "^1.2.0",
"tsconfig-paths": "^3.14.1" "semver": "^6.3.1",
"string.prototype.trimend": "^1.0.8",
"tsconfig-paths": "^3.15.0"
}, },
"engines": { "engines": {
"node": ">=4" "node": ">=4"
}, },
"peerDependencies": { "peerDependencies": {
"eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
} }
}, },
"node_modules/eslint-plugin-import/node_modules/debug": { "node_modules/eslint-plugin-import/node_modules/debug": {
@@ -9810,6 +10065,7 @@
"version": "6.7.1", "version": "6.7.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz",
"integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==", "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.7", "@babel/runtime": "^7.20.7",
"aria-query": "^5.1.3", "aria-query": "^5.1.3",
@@ -9838,17 +10094,20 @@
"node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": { "node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": {
"version": "9.2.2", "version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"license": "MIT"
}, },
"node_modules/eslint-plugin-react": { "node_modules/eslint-plugin-react": {
"version": "7.32.2", "version": "7.33.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz",
"integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==",
"license": "MIT",
"dependencies": { "dependencies": {
"array-includes": "^3.1.6", "array-includes": "^3.1.6",
"array.prototype.flatmap": "^1.3.1", "array.prototype.flatmap": "^1.3.1",
"array.prototype.tosorted": "^1.1.1", "array.prototype.tosorted": "^1.1.1",
"doctrine": "^2.1.0", "doctrine": "^2.1.0",
"es-iterator-helpers": "^1.0.12",
"estraverse": "^5.3.0", "estraverse": "^5.3.0",
"jsx-ast-utils": "^2.4.1 || ^3.0.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0",
"minimatch": "^3.1.2", "minimatch": "^3.1.2",
@@ -9858,7 +10117,7 @@
"object.values": "^1.1.6", "object.values": "^1.1.6",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"resolve": "^2.0.0-next.4", "resolve": "^2.0.0-next.4",
"semver": "^6.3.0", "semver": "^6.3.1",
"string.prototype.matchall": "^4.0.8" "string.prototype.matchall": "^4.0.8"
}, },
"engines": { "engines": {
@@ -9869,9 +10128,10 @@
} }
}, },
"node_modules/eslint-plugin-react-hooks": { "node_modules/eslint-plugin-react-hooks": {
"version": "4.6.0", "version": "4.6.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.1.tgz",
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", "integrity": "sha512-Ck77j8hF7l9N4S/rzSLOWEKpn994YH6iwUK8fr9mXIaQvGpQYmOnQLbiue1u5kI5T1y+gdgqosnEAO9NCz0DBg==",
"license": "MIT",
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@@ -9883,6 +10143,7 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"license": "Apache-2.0",
"dependencies": { "dependencies": {
"esutils": "^2.0.2" "esutils": "^2.0.2"
}, },
@@ -9894,6 +10155,7 @@
"version": "2.0.0-next.5", "version": "2.0.0-next.5",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
"integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
"license": "MIT",
"dependencies": { "dependencies": {
"is-core-module": "^2.13.0", "is-core-module": "^2.13.0",
"path-parse": "^1.0.7", "path-parse": "^1.0.7",
@@ -10098,16 +10360,6 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/event-emitter": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
"integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
"dev": true,
"dependencies": {
"d": "1",
"es5-ext": "~0.10.14"
}
},
"node_modules/event-target-shim": { "node_modules/event-target-shim": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
@@ -10184,17 +10436,16 @@
} }
}, },
"node_modules/express": { "node_modules/express": {
"version": "4.21.0", "version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"license": "MIT",
"dependencies": { "dependencies": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
"body-parser": "1.20.3", "body-parser": "1.20.3",
"content-disposition": "0.5.4", "content-disposition": "0.5.4",
"content-type": "~1.0.4", "content-type": "~1.0.4",
"cookie": "0.6.0", "cookie": "0.7.1",
"cookie-signature": "1.0.6", "cookie-signature": "1.0.6",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "2.0.0", "depd": "2.0.0",
@@ -10208,7 +10459,7 @@
"methods": "~1.1.2", "methods": "~1.1.2",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
"path-to-regexp": "0.1.10", "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7", "proxy-addr": "~2.0.7",
"qs": "6.13.0", "qs": "6.13.0",
"range-parser": "~1.2.1", "range-parser": "~1.2.1",
@@ -10223,6 +10474,10 @@
}, },
"engines": { "engines": {
"node": ">= 0.10.0" "node": ">= 0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/express/node_modules/debug": { "node_modules/express/node_modules/debug": {
@@ -10240,21 +10495,6 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/ext": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
"dev": true,
"dependencies": {
"type": "^2.7.2"
}
},
"node_modules/ext/node_modules/type": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==",
"dev": true
},
"node_modules/extend": { "node_modules/extend": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -10285,9 +10525,10 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
}, },
"node_modules/fast-defer": { "node_modules/fast-defer": {
"version": "1.1.7", "version": "1.1.8",
"resolved": "https://registry.npmjs.org/fast-defer/-/fast-defer-1.1.7.tgz", "resolved": "https://registry.npmjs.org/fast-defer/-/fast-defer-1.1.8.tgz",
"integrity": "sha512-tJ01ulDWT2WhqxMKS20nXX6wyX2iInBYpbN3GO7yjKwXMY4qvkdBRxak9IFwBLlFDESox+SwSvqMCZDfe1tqeg==" "integrity": "sha512-lEJeOH5VL5R09j6AA0D4Uvq7AgsHw0dAImQQ+F3iSyHZuAxyQfWobsagGpTcOPvJr3urmKRHrs+Gs9hV+/Qm/Q==",
"license": "MIT"
}, },
"node_modules/fast-fifo": { "node_modules/fast-fifo": {
"version": "1.3.2", "version": "1.3.2",
@@ -10295,9 +10536,10 @@
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="
}, },
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.1", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
"integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
"license": "MIT",
"dependencies": { "dependencies": {
"@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3", "@nodelib/fs.walk": "^1.2.3",
@@ -10777,6 +11019,7 @@
"version": "4.3.7", "version": "4.3.7",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
"integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
"license": "MIT",
"engines": { "engines": {
"node": "*" "node": "*"
}, },
@@ -10850,14 +11093,17 @@
} }
}, },
"node_modules/function.prototype.name": { "node_modules/function.prototype.name": {
"version": "1.1.6", "version": "1.1.8",
"resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
"integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.2", "call-bind": "^1.0.8",
"define-properties": "^1.2.0", "call-bound": "^1.0.3",
"es-abstract": "^1.22.1", "define-properties": "^1.2.1",
"functions-have-names": "^1.2.3" "functions-have-names": "^1.2.3",
"hasown": "^2.0.2",
"is-callable": "^1.2.7"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -10891,15 +11137,21 @@
} }
}, },
"node_modules/get-intrinsic": { "node_modules/get-intrinsic": {
"version": "1.2.4", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2", "function-bind": "^1.1.2",
"has-proto": "^1.0.1", "get-proto": "^1.0.1",
"has-symbols": "^1.0.3", "gopd": "^1.2.0",
"hasown": "^2.0.0" "has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -10924,6 +11176,19 @@
"node": ">=8.0.0" "node": ">=8.0.0"
} }
}, },
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/get-stream": { "node_modules/get-stream": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
@@ -10936,13 +11201,14 @@
} }
}, },
"node_modules/get-symbol-description": { "node_modules/get-symbol-description": {
"version": "1.0.2", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
"integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.5", "call-bound": "^1.0.3",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4" "get-intrinsic": "^1.2.6"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -11050,27 +11316,30 @@
} }
}, },
"node_modules/globby": { "node_modules/globby": {
"version": "13.2.2", "version": "14.0.2",
"resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz",
"integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==",
"license": "MIT",
"dependencies": { "dependencies": {
"dir-glob": "^3.0.1", "@sindresorhus/merge-streams": "^2.1.0",
"fast-glob": "^3.3.0", "fast-glob": "^3.3.2",
"ignore": "^5.2.4", "ignore": "^5.2.4",
"merge2": "^1.4.1", "path-type": "^5.0.0",
"slash": "^4.0.0" "slash": "^5.1.0",
"unicorn-magic": "^0.1.0"
}, },
"engines": { "engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0" "node": ">=18"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/globby/node_modules/slash": { "node_modules/globby/node_modules/path-type": {
"version": "4.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz",
"integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==",
"license": "MIT",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@@ -11078,12 +11347,25 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/globby/node_modules/slash": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
"integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
"license": "MIT",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/gopd": { "node_modules/gopd": {
"version": "1.0.1", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"dependencies": { "license": "MIT",
"get-intrinsic": "^1.1.3" "engines": {
"node": ">= 0.4"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
@@ -11193,9 +11475,13 @@
} }
}, },
"node_modules/has-proto": { "node_modules/has-proto": {
"version": "1.0.3", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.0"
},
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@@ -11204,9 +11490,10 @@
} }
}, },
"node_modules/has-symbols": { "node_modules/has-symbols": {
"version": "1.0.3", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@@ -11456,9 +11743,10 @@
} }
}, },
"node_modules/html-webpack-plugin": { "node_modules/html-webpack-plugin": {
"version": "5.6.0", "version": "5.6.3",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz",
"integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==", "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@types/html-minifier-terser": "^6.0.0", "@types/html-minifier-terser": "^6.0.0",
"html-minifier-terser": "^6.0.2", "html-minifier-terser": "^6.0.2",
@@ -11564,9 +11852,10 @@
} }
}, },
"node_modules/http-proxy-middleware": { "node_modules/http-proxy-middleware": {
"version": "2.0.6", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz",
"integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@types/http-proxy": "^1.17.8", "@types/http-proxy": "^1.17.8",
"http-proxy": "^1.18.1", "http-proxy": "^1.18.1",
@@ -11617,20 +11906,6 @@
"node": ">=10.17.0" "node": ">=10.17.0"
} }
}, },
"node_modules/husky": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz",
"integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==",
"bin": {
"husky": "lib/bin.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/hyphenate-style-name": { "node_modules/hyphenate-style-name": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
@@ -11814,11 +12089,6 @@
"url": "https://opencollective.com/immer" "url": "https://opencollective.com/immer"
} }
}, },
"node_modules/immutable": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
"integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA=="
},
"node_modules/import-fresh": { "node_modules/import-fresh": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -11986,13 +12256,14 @@
} }
}, },
"node_modules/internal-slot": { "node_modules/internal-slot": {
"version": "1.0.7", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
"integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
"license": "MIT",
"dependencies": { "dependencies": {
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"hasown": "^2.0.0", "hasown": "^2.0.2",
"side-channel": "^1.0.4" "side-channel": "^1.1.0"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -12007,34 +12278,56 @@
} }
}, },
"node_modules/intl-messageformat": { "node_modules/intl-messageformat": {
"version": "10.5.14", "version": "10.7.7",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz", "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.7.tgz",
"integrity": "sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==", "integrity": "sha512-F134jIoeYMro/3I0h08D0Yt4N9o9pjddU/4IIxMMURqbAtI2wu70X8hvG1V48W49zXHXv3RKSF/po+0fDfsGjA==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"@formatjs/ecma402-abstract": "2.0.0", "@formatjs/ecma402-abstract": "2.2.4",
"@formatjs/fast-memoize": "2.2.0", "@formatjs/fast-memoize": "2.2.3",
"@formatjs/icu-messageformat-parser": "2.7.8", "@formatjs/icu-messageformat-parser": "2.9.4",
"tslib": "^2.4.0" "tslib": "2"
} }
}, },
"node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract": { "node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract": {
"version": "2.0.0", "version": "2.2.4",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz",
"integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==", "integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@formatjs/intl-localematcher": "0.5.4", "@formatjs/fast-memoize": "2.2.3",
"tslib": "^2.4.0" "@formatjs/intl-localematcher": "0.5.8",
"tslib": "2"
}
},
"node_modules/intl-messageformat/node_modules/@formatjs/icu-messageformat-parser": {
"version": "2.9.4",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.4.tgz",
"integrity": "sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.2.4",
"@formatjs/icu-skeleton-parser": "1.8.8",
"tslib": "2"
}
},
"node_modules/intl-messageformat/node_modules/@formatjs/icu-skeleton-parser": {
"version": "1.8.8",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.8.tgz",
"integrity": "sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.2.4",
"tslib": "2"
} }
}, },
"node_modules/intl-messageformat/node_modules/@formatjs/intl-localematcher": { "node_modules/intl-messageformat/node_modules/@formatjs/intl-localematcher": {
"version": "0.5.4", "version": "0.5.8",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz",
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", "integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tslib": "^2.4.0" "tslib": "2"
} }
}, },
"node_modules/invariant": { "node_modules/invariant": {
@@ -12069,12 +12362,14 @@
} }
}, },
"node_modules/is-array-buffer": { "node_modules/is-array-buffer": {
"version": "3.0.4", "version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
"integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.2", "call-bind": "^1.0.8",
"get-intrinsic": "^1.2.1" "call-bound": "^1.0.3",
"get-intrinsic": "^1.2.6"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -12088,12 +12383,35 @@
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
}, },
"node_modules/is-bigint": { "node_modules/is-async-function": {
"version": "1.0.4", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
"integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"has-bigints": "^1.0.1" "async-function": "^1.0.0",
"call-bound": "^1.0.3",
"get-proto": "^1.0.1",
"has-tostringtag": "^1.0.2",
"safe-regex-test": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-bigint": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
"integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
"license": "MIT",
"dependencies": {
"has-bigints": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
@@ -12111,12 +12429,13 @@
} }
}, },
"node_modules/is-boolean-object": { "node_modules/is-boolean-object": {
"version": "1.1.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
"integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.2", "call-bound": "^1.0.3",
"has-tostringtag": "^1.0.0" "has-tostringtag": "^1.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -12159,21 +12478,28 @@
} }
}, },
"node_modules/is-core-module": { "node_modules/is-core-module": {
"version": "2.13.1", "version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"license": "MIT",
"dependencies": { "dependencies": {
"hasown": "^2.0.0" "hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-data-view": { "node_modules/is-data-view": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
"integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bound": "^1.0.2",
"get-intrinsic": "^1.2.6",
"is-typed-array": "^1.1.13" "is-typed-array": "^1.1.13"
}, },
"engines": { "engines": {
@@ -12184,11 +12510,13 @@
} }
}, },
"node_modules/is-date-object": { "node_modules/is-date-object": {
"version": "1.0.5", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
"integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
"license": "MIT",
"dependencies": { "dependencies": {
"has-tostringtag": "^1.0.0" "call-bound": "^1.0.2",
"has-tostringtag": "^1.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -12219,6 +12547,21 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/is-finalizationregistry": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
"integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-fullwidth-code-point": { "node_modules/is-fullwidth-code-point": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
@@ -12236,6 +12579,24 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/is-generator-function": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
"integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.3",
"get-proto": "^1.0.0",
"has-tostringtag": "^1.0.2",
"safe-regex-test": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-glob": { "node_modules/is-glob": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -12286,17 +12647,10 @@
} }
}, },
"node_modules/is-map": { "node_modules/is-map": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
"integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-negative-zero": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
"integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
"license": "MIT",
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@@ -12313,11 +12667,13 @@
} }
}, },
"node_modules/is-number-object": { "node_modules/is-number-object": {
"version": "1.0.7", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
"integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
"license": "MIT",
"dependencies": { "dependencies": {
"has-tostringtag": "^1.0.0" "call-bound": "^1.0.3",
"has-tostringtag": "^1.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -12391,19 +12747,16 @@
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
}, },
"node_modules/is-promise": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
"integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
"dev": true
},
"node_modules/is-regex": { "node_modules/is-regex": {
"version": "1.1.4", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
"integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.2", "call-bound": "^1.0.2",
"has-tostringtag": "^1.0.0" "gopd": "^1.2.0",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -12421,19 +12774,24 @@
} }
}, },
"node_modules/is-set": { "node_modules/is-set": {
"version": "2.0.2", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
"integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-shared-array-buffer": { "node_modules/is-shared-array-buffer": {
"version": "1.0.3", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
"integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7" "call-bound": "^1.0.3"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -12454,11 +12812,13 @@
} }
}, },
"node_modules/is-string": { "node_modules/is-string": {
"version": "1.0.7", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
"integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
"license": "MIT",
"dependencies": { "dependencies": {
"has-tostringtag": "^1.0.0" "call-bound": "^1.0.3",
"has-tostringtag": "^1.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -12468,11 +12828,14 @@
} }
}, },
"node_modules/is-symbol": { "node_modules/is-symbol": {
"version": "1.0.4", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
"integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
"license": "MIT",
"dependencies": { "dependencies": {
"has-symbols": "^1.0.2" "call-bound": "^1.0.2",
"has-symbols": "^1.1.0",
"safe-regex-test": "^1.1.0"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -12482,11 +12845,12 @@
} }
}, },
"node_modules/is-typed-array": { "node_modules/is-typed-array": {
"version": "1.1.13", "version": "1.1.15",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
"integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"which-typed-array": "^1.1.14" "which-typed-array": "^1.1.16"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -12518,31 +12882,43 @@
} }
}, },
"node_modules/is-weakmap": { "node_modules/is-weakmap": {
"version": "2.0.1", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
"integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-weakref": { "node_modules/is-weakref": {
"version": "1.0.2", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
"integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.2" "call-bound": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-weakset": { "node_modules/is-weakset": {
"version": "2.0.2", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
"integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.2", "call-bound": "^1.0.3",
"get-intrinsic": "^1.1.1" "get-intrinsic": "^1.2.6"
},
"engines": {
"node": ">= 0.4"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
@@ -12696,6 +13072,23 @@
"integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==", "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==",
"dev": true "dev": true
}, },
"node_modules/iterator.prototype": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
"integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",
"es-object-atoms": "^1.0.0",
"get-intrinsic": "^1.2.6",
"get-proto": "^1.0.0",
"has-symbols": "^1.1.0",
"set-function-name": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/jest": { "node_modules/jest": {
"version": "29.7.0", "version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
@@ -12888,26 +13281,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/jest-config/node_modules/babel-jest": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
"integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
"dependencies": {
"@jest/transform": "^29.7.0",
"@types/babel__core": "^7.1.14",
"babel-plugin-istanbul": "^6.1.1",
"babel-preset-jest": "^29.6.3",
"chalk": "^4.0.0",
"graceful-fs": "^4.2.9",
"slash": "^3.0.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
},
"peerDependencies": {
"@babel/core": "^7.8.0"
}
},
"node_modules/jest-config/node_modules/pretty-format": { "node_modules/jest-config/node_modules/pretty-format": {
"version": "29.7.0", "version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
@@ -13047,17 +13420,18 @@
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
}, },
"node_modules/jest-environment-jsdom": { "node_modules/jest-environment-jsdom": {
"version": "29.6.1", "version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.1.tgz", "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz",
"integrity": "sha512-PoY+yLaHzVRhVEjcVKSfJ7wXmJW4UqPYNhR05h7u/TK0ouf6DmRNZFBL/Z00zgQMyWGMBXn69/FmOvhEJu8cIw==", "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@jest/environment": "^29.6.1", "@jest/environment": "^29.7.0",
"@jest/fake-timers": "^29.6.1", "@jest/fake-timers": "^29.7.0",
"@jest/types": "^29.6.1", "@jest/types": "^29.6.3",
"@types/jsdom": "^20.0.0", "@types/jsdom": "^20.0.0",
"@types/node": "*", "@types/node": "*",
"jest-mock": "^29.6.1", "jest-mock": "^29.7.0",
"jest-util": "^29.6.1", "jest-util": "^29.7.0",
"jsdom": "^20.0.0" "jsdom": "^20.0.0"
}, },
"engines": { "engines": {
@@ -13574,10 +13948,11 @@
} }
}, },
"node_modules/jest-when": { "node_modules/jest-when": {
"version": "3.6.0", "version": "3.7.0",
"resolved": "https://registry.npmjs.org/jest-when/-/jest-when-3.6.0.tgz", "resolved": "https://registry.npmjs.org/jest-when/-/jest-when-3.7.0.tgz",
"integrity": "sha512-+cZWTy0ekAJo7M9Om0Scdor1jm3wDiYJWmXE8U22UVnkH54YCXAuaqz3P+up/FdtOg8g4wHOxV7Thd7nKhT6Dg==", "integrity": "sha512-aLbiyxmtksijcrKFir7n+t+XPbqSLV01eDkRyX28WM4VgA/iSc3mG8R8O2evDtOAa6SefrJiTIt/rTqqyrwVZg==",
"dev": true, "dev": true,
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"jest": ">= 25" "jest": ">= 25"
} }
@@ -13741,14 +14116,15 @@
} }
}, },
"node_modules/jsesc": { "node_modules/jsesc": {
"version": "2.5.2", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"license": "MIT",
"bin": { "bin": {
"jsesc": "bin/jsesc" "jsesc": "bin/jsesc"
}, },
"engines": { "engines": {
"node": ">=4" "node": ">=6"
} }
}, },
"node_modules/json-buffer": { "node_modules/json-buffer": {
@@ -13854,6 +14230,7 @@
"version": "3.3.5", "version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
"integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"array-includes": "^3.1.6", "array-includes": "^3.1.6",
"array.prototype.flat": "^1.3.1", "array.prototype.flat": "^1.3.1",
@@ -13904,12 +14281,14 @@
"node_modules/language-subtag-registry": { "node_modules/language-subtag-registry": {
"version": "0.3.23", "version": "0.3.23",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
"integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==" "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
"license": "CC0-1.0"
}, },
"node_modules/language-tags": { "node_modules/language-tags": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz",
"integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"language-subtag-registry": "~0.3.2" "language-subtag-registry": "~0.3.2"
} }
@@ -14178,15 +14557,6 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"node_modules/lru-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
"integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==",
"dev": true,
"dependencies": {
"es5-ext": "~0.10.2"
}
},
"node_modules/lz-string": { "node_modules/lz-string": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
@@ -14275,6 +14645,15 @@
"css-mediaquery": "^0.1.2" "css-mediaquery": "^0.1.2"
} }
}, },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mdast-util-definitions": { "node_modules/mdast-util-definitions": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz",
@@ -14367,22 +14746,6 @@
"node": ">= 4.0.0" "node": ">= 4.0.0"
} }
}, },
"node_modules/memoizee": {
"version": "0.4.15",
"resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz",
"integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==",
"dev": true,
"dependencies": {
"d": "^1.0.1",
"es5-ext": "^0.10.53",
"es6-weak-map": "^2.0.3",
"event-emitter": "^0.3.5",
"is-promise": "^2.2.2",
"lru-queue": "^0.1.0",
"next-tick": "^1.1.0",
"timers-ext": "^0.1.7"
}
},
"node_modules/memory-fs": { "node_modules/memory-fs": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz",
@@ -15006,9 +15369,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.7", "version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@@ -15035,7 +15398,8 @@
"node_modules/natural-compare-lite": { "node_modules/natural-compare-lite": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
"license": "MIT"
}, },
"node_modules/negotiator": { "node_modules/negotiator": {
"version": "0.6.3", "version": "0.6.3",
@@ -15050,12 +15414,6 @@
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
}, },
"node_modules/next-tick": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
"dev": true
},
"node_modules/no-case": { "node_modules/no-case": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@@ -15087,6 +15445,13 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"license": "MIT",
"optional": true
},
"node_modules/node-forge": { "node_modules/node-forge": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
@@ -15174,9 +15539,9 @@
"peer": true "peer": true
}, },
"node_modules/node-releases": { "node_modules/node-releases": {
"version": "2.0.18", "version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/normalize-package-data": { "node_modules/normalize-package-data": {
@@ -15212,6 +15577,7 @@
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
"license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -15252,9 +15618,10 @@
} }
}, },
"node_modules/object-code": { "node_modules/object-code": {
"version": "1.3.0", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/object-code/-/object-code-1.3.0.tgz", "resolved": "https://registry.npmjs.org/object-code/-/object-code-1.3.3.tgz",
"integrity": "sha512-PLplgvzuFhSPBuTX/mtaXEnU3c6g7qKflVVQbV9VWEnV/34iKeAX1jeDNCKq1OgGlsnkA/NjldCzTbHxa7Wj4A==" "integrity": "sha512-/Ds4Xd5xzrtUOJ+xJQ57iAy0BZsZltOHssnDgcZ8DOhgh41q1YJCnTPnWdWSLkNGNnxYzhYChjc5dgC9mEERCA==",
"license": "MIT"
}, },
"node_modules/object-filter": { "node_modules/object-filter": {
"version": "1.0.2", "version": "1.0.2",
@@ -15262,9 +15629,13 @@
"integrity": "sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA==" "integrity": "sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA=="
}, },
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.13.1", "version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
@@ -15293,13 +15664,16 @@
} }
}, },
"node_modules/object.assign": { "node_modules/object.assign": {
"version": "4.1.5", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
"integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.5", "call-bind": "^1.0.8",
"call-bound": "^1.0.3",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
"has-symbols": "^1.0.3", "es-object-atoms": "^1.0.0",
"has-symbols": "^1.1.0",
"object-keys": "^1.1.1" "object-keys": "^1.1.1"
}, },
"engines": { "engines": {
@@ -15313,6 +15687,7 @@
"version": "1.1.8", "version": "1.1.8",
"resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz",
"integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bind": "^1.0.7",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
@@ -15326,6 +15701,7 @@
"version": "2.0.8", "version": "2.0.8",
"resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
"integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bind": "^1.0.7",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
@@ -15339,10 +15715,25 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/object.groupby": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
"integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.7",
"define-properties": "^1.2.1",
"es-abstract": "^1.23.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/object.hasown": { "node_modules/object.hasown": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz",
"integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==",
"license": "MIT",
"dependencies": { "dependencies": {
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
"es-abstract": "^1.23.2", "es-abstract": "^1.23.2",
@@ -15493,6 +15884,23 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/own-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
"integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
"license": "MIT",
"dependencies": {
"get-intrinsic": "^1.2.6",
"object-keys": "^1.1.1",
"safe-push-apply": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/p-limit": { "node_modules/p-limit": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -15745,10 +16153,9 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
}, },
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "0.1.10", "version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
"license": "MIT"
}, },
"node_modules/path-type": { "node_modules/path-type": {
"version": "4.0.0", "version": "4.0.0",
@@ -15759,9 +16166,9 @@
} }
}, },
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
@@ -16059,9 +16466,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.39", "version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -16079,8 +16486,8 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.3.7", "nanoid": "^3.3.7",
"picocolors": "^1.0.1", "picocolors": "^1.1.1",
"source-map-js": "^1.2.0" "source-map-js": "^1.2.1"
}, },
"engines": { "engines": {
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
@@ -16862,9 +17269,10 @@
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
}, },
"node_modules/pubsub-js": { "node_modules/pubsub-js": {
"version": "1.9.4", "version": "1.9.5",
"resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.4.tgz", "resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.5.tgz",
"integrity": "sha512-hJYpaDvPH4w8ZX/0Fdf9ma1AwRgU353GfbaVfPjfJQf1KxZ2iHaHl3fAUw1qlJIR5dr4F3RzjGaWohYUEyoh7A==" "integrity": "sha512-5MZ0I9i5JWVO7SizvOviKvZU2qaBbl2KQX150FAA+fJBwYpwOUId7aNygURWSdPzlsA/xZ/InUKXqBbzM0czTA==",
"license": "MIT"
}, },
"node_modules/pump": { "node_modules/pump": {
"version": "3.0.0", "version": "3.0.0",
@@ -17358,21 +17766,21 @@
} }
}, },
"node_modules/react-intl": { "node_modules/react-intl": {
"version": "6.6.8", "version": "6.8.9",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.6.8.tgz", "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.8.9.tgz",
"integrity": "sha512-M0pkhzcgV31h++2901BiRXWl69hp2zPyLxRrSwRjd1ErXbNoubz/f4M6DrRTd4OiSUrT4ajRQzrmtS5plG4FtA==", "integrity": "sha512-TUfj5E7lyUDvz/GtovC9OMh441kBr08rtIbgh3p0R8iF3hVY+V2W9Am7rb8BpJ/29BH1utJOqOOhmvEVh3GfZg==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"@formatjs/ecma402-abstract": "2.0.0", "@formatjs/ecma402-abstract": "2.2.4",
"@formatjs/icu-messageformat-parser": "2.7.8", "@formatjs/icu-messageformat-parser": "2.9.4",
"@formatjs/intl": "2.10.4", "@formatjs/intl": "2.10.15",
"@formatjs/intl-displaynames": "6.6.8", "@formatjs/intl-displaynames": "6.8.5",
"@formatjs/intl-listformat": "7.5.7", "@formatjs/intl-listformat": "7.7.5",
"@types/hoist-non-react-statics": "^3.3.1", "@types/hoist-non-react-statics": "3",
"@types/react": "16 || 17 || 18", "@types/react": "16 || 17 || 18",
"hoist-non-react-statics": "^3.3.2", "hoist-non-react-statics": "3",
"intl-messageformat": "10.5.14", "intl-messageformat": "10.7.7",
"tslib": "^2.4.0" "tslib": "2"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.6.0 || 17 || 18", "react": "^16.6.0 || 17 || 18",
@@ -17385,22 +17793,44 @@
} }
}, },
"node_modules/react-intl/node_modules/@formatjs/ecma402-abstract": { "node_modules/react-intl/node_modules/@formatjs/ecma402-abstract": {
"version": "2.0.0", "version": "2.2.4",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz",
"integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==", "integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@formatjs/intl-localematcher": "0.5.4", "@formatjs/fast-memoize": "2.2.3",
"tslib": "^2.4.0" "@formatjs/intl-localematcher": "0.5.8",
"tslib": "2"
}
},
"node_modules/react-intl/node_modules/@formatjs/icu-messageformat-parser": {
"version": "2.9.4",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.4.tgz",
"integrity": "sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.2.4",
"@formatjs/icu-skeleton-parser": "1.8.8",
"tslib": "2"
}
},
"node_modules/react-intl/node_modules/@formatjs/icu-skeleton-parser": {
"version": "1.8.8",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.8.tgz",
"integrity": "sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.2.4",
"tslib": "2"
} }
}, },
"node_modules/react-intl/node_modules/@formatjs/intl-localematcher": { "node_modules/react-intl/node_modules/@formatjs/intl-localematcher": {
"version": "0.5.4", "version": "0.5.8",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz",
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", "integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tslib": "^2.4.0" "tslib": "2"
} }
}, },
"node_modules/react-is": { "node_modules/react-is": {
@@ -17519,9 +17949,9 @@
} }
}, },
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.14.2", "version": "0.16.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.16.0.tgz",
"integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "integrity": "sha512-FPvF2XxTSikpJxcr+bHut2H4gJ17+18Uy20D5/F+SKzFap62R3cM5wH6b8WN3LyGSYeQilLEcJcR1fjBSI2S1A==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@@ -17890,9 +18320,10 @@
} }
}, },
"node_modules/redux": { "node_modules/redux": {
"version": "4.1.2", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
"integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.9.2" "@babel/runtime": "^7.9.2"
} }
@@ -17905,6 +18336,28 @@
"redux": "^4" "redux": "^4"
} }
}, },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
"integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.8",
"define-properties": "^1.2.1",
"es-abstract": "^1.23.9",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.0.0",
"get-intrinsic": "^1.2.7",
"get-proto": "^1.0.1",
"which-builtin-type": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/regenerate": { "node_modules/regenerate": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@@ -17922,10 +18375,10 @@
} }
}, },
"node_modules/regenerator-runtime": { "node_modules/regenerator-runtime": {
"version": "0.13.11", "version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"peer": true "license": "MIT"
}, },
"node_modules/regenerator-transform": { "node_modules/regenerator-transform": {
"version": "0.15.2", "version": "0.15.2",
@@ -17942,14 +18395,17 @@
"integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==" "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg=="
}, },
"node_modules/regexp.prototype.flags": { "node_modules/regexp.prototype.flags": {
"version": "1.5.2", "version": "1.5.4",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
"integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.6", "call-bind": "^1.0.8",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"set-function-name": "^2.0.1" "get-proto": "^1.0.1",
"gopd": "^1.2.0",
"set-function-name": "^2.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -18254,13 +18710,15 @@
} }
}, },
"node_modules/safe-array-concat": { "node_modules/safe-array-concat": {
"version": "1.1.2", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
"integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bind": "^1.0.8",
"get-intrinsic": "^1.2.4", "call-bound": "^1.0.2",
"has-symbols": "^1.0.3", "get-intrinsic": "^1.2.6",
"has-symbols": "^1.1.0",
"isarray": "^2.0.5" "isarray": "^2.0.5"
}, },
"engines": { "engines": {
@@ -18289,14 +18747,31 @@
} }
] ]
}, },
"node_modules/safe-regex-test": { "node_modules/safe-push-apply": {
"version": "1.0.3", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
"integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.6",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"is-regex": "^1.1.4" "isarray": "^2.0.5"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/safe-regex-test": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
"integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"is-regex": "^1.2.1"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -18320,12 +18795,13 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
}, },
"node_modules/sass": { "node_modules/sass": {
"version": "1.79.3", "version": "1.85.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.79.3.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.85.1.tgz",
"integrity": "sha512-m7dZxh0W9EZ3cw50Me5GOuYm/tVAJAn91SUnohLRo9cXBixGUOdvmryN+dXpwR831bhoY3Zv7rEFt85PUwTmzA==", "integrity": "sha512-Uk8WpxM5v+0cMR0XjX9KfRIacmSG86RH4DCCZjLU2rFh5tyutt9siAXJ7G+YfxQ99Q6wrRMbMlVl6KqUms71ag==",
"license": "MIT",
"dependencies": { "dependencies": {
"chokidar": "^4.0.0", "chokidar": "^4.0.0",
"immutable": "^4.0.0", "immutable": "^5.0.2",
"source-map-js": ">=0.6.2 <2.0.0" "source-map-js": ">=0.6.2 <2.0.0"
}, },
"bin": { "bin": {
@@ -18333,12 +18809,16 @@
}, },
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=14.0.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
} }
}, },
"node_modules/sass-loader": { "node_modules/sass-loader": {
"version": "16.0.2", "version": "16.0.5",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.2.tgz", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.5.tgz",
"integrity": "sha512-Ll6iXZ1EYwYT19SqW4mSBb76vSSi8JgzElmzIerhEGgzB5hRjDQIWsPmuk1UrAXkR16KJHqVY0eH+5/uw9Tmfw==", "integrity": "sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==",
"license": "MIT",
"dependencies": { "dependencies": {
"neo-async": "^2.6.2" "neo-async": "^2.6.2"
}, },
@@ -18375,9 +18855,10 @@
} }
}, },
"node_modules/sass/node_modules/chokidar": { "node_modules/sass/node_modules/chokidar": {
"version": "4.0.1", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"license": "MIT",
"dependencies": { "dependencies": {
"readdirp": "^4.0.1" "readdirp": "^4.0.1"
}, },
@@ -18388,12 +18869,19 @@
"url": "https://paulmillr.com/funding/" "url": "https://paulmillr.com/funding/"
} }
}, },
"node_modules/sass/node_modules/immutable": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz",
"integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==",
"license": "MIT"
},
"node_modules/sass/node_modules/readdirp": { "node_modules/sass/node_modules/readdirp": {
"version": "4.0.1", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"license": "MIT",
"engines": { "engines": {
"node": ">= 14.16.0" "node": ">= 14.18.0"
}, },
"funding": { "funding": {
"type": "individual", "type": "individual",
@@ -18523,9 +19011,10 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/serialize-javascript": { "node_modules/serialize-javascript": {
"version": "6.0.1", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"randombytes": "^2.1.0" "randombytes": "^2.1.0"
} }
@@ -18645,6 +19134,20 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/set-proto": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
"integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": { "node_modules/setprototypeof": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -18739,14 +19242,69 @@
"peer": true "peer": true
}, },
"node_modules/side-channel": { "node_modules/side-channel": {
"version": "1.0.6", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4", "object-inspect": "^1.13.3",
"object-inspect": "^1.13.1" "side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-list": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-weakmap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3",
"side-channel-map": "^1.0.1"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -18892,9 +19450,10 @@
} }
}, },
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -19157,6 +19716,7 @@
"version": "4.0.11", "version": "4.0.11",
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz",
"integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bind": "^1.0.7",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
@@ -19179,14 +19739,18 @@
} }
}, },
"node_modules/string.prototype.trim": { "node_modules/string.prototype.trim": {
"version": "1.2.9", "version": "1.2.10",
"resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
"integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bind": "^1.0.8",
"call-bound": "^1.0.2",
"define-data-property": "^1.1.4",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
"es-abstract": "^1.23.0", "es-abstract": "^1.23.5",
"es-object-atoms": "^1.0.0" "es-object-atoms": "^1.0.0",
"has-property-descriptors": "^1.0.2"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -19196,14 +19760,19 @@
} }
}, },
"node_modules/string.prototype.trimend": { "node_modules/string.prototype.trimend": {
"version": "1.0.8", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
"integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bind": "^1.0.8",
"call-bound": "^1.0.2",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
"es-object-atoms": "^1.0.0" "es-object-atoms": "^1.0.0"
}, },
"engines": {
"node": ">= 0.4"
},
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
@@ -19558,15 +20127,16 @@
} }
}, },
"node_modules/terser-webpack-plugin": { "node_modules/terser-webpack-plugin": {
"version": "5.3.10", "version": "5.3.12",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.12.tgz",
"integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "integrity": "sha512-jDLYqo7oF8tJIttjXO6jBY5Hk8p3A8W4ttih7cCEq64fQFWmgJ4VqAQjKr7WwIDlmXKEc6QeoRb5ecjZ+2afcg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/trace-mapping": "^0.3.20", "@jridgewell/trace-mapping": "^0.3.25",
"jest-worker": "^27.4.5", "jest-worker": "^27.4.5",
"schema-utils": "^3.1.1", "schema-utils": "^4.3.0",
"serialize-javascript": "^6.0.1", "serialize-javascript": "^6.0.2",
"terser": "^5.26.0" "terser": "^5.31.1"
}, },
"engines": { "engines": {
"node": ">= 10.13.0" "node": ">= 10.13.0"
@@ -19590,10 +20160,39 @@
} }
} }
}, },
"node_modules/terser-webpack-plugin/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
"peerDependencies": {
"ajv": "^8.8.2"
}
},
"node_modules/terser-webpack-plugin/node_modules/jest-worker": { "node_modules/terser-webpack-plugin/node_modules/jest-worker": {
"version": "27.5.1", "version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@types/node": "*", "@types/node": "*",
"merge-stream": "^2.0.0", "merge-stream": "^2.0.0",
@@ -19603,10 +20202,36 @@
"node": ">= 10.13.0" "node": ">= 10.13.0"
} }
}, },
"node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/terser-webpack-plugin/node_modules/schema-utils": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
"integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.9.0",
"ajv-formats": "^2.1.1",
"ajv-keywords": "^5.1.0"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/terser-webpack-plugin/node_modules/supports-color": { "node_modules/terser-webpack-plugin/node_modules/supports-color": {
"version": "8.1.1", "version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"license": "MIT",
"dependencies": { "dependencies": {
"has-flag": "^4.0.0" "has-flag": "^4.0.0"
}, },
@@ -19659,16 +20284,6 @@
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
}, },
"node_modules/timers-ext": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz",
"integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==",
"dev": true,
"dependencies": {
"es5-ext": "~0.10.46",
"next-tick": "1"
}
},
"node_modules/tiny-invariant": { "node_modules/tiny-invariant": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
@@ -19695,14 +20310,6 @@
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw=="
}, },
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"engines": {
"node": ">=4"
}
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -19897,6 +20504,7 @@
"version": "3.21.0", "version": "3.21.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
"license": "MIT",
"dependencies": { "dependencies": {
"tslib": "^1.8.1" "tslib": "^1.8.1"
}, },
@@ -19910,7 +20518,8 @@
"node_modules/tsutils/node_modules/tslib": { "node_modules/tsutils/node_modules/tslib": {
"version": "1.14.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"license": "0BSD"
}, },
"node_modules/tunnel-agent": { "node_modules/tunnel-agent": {
"version": "0.6.0", "version": "0.6.0",
@@ -19923,12 +20532,6 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/type": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
"dev": true
},
"node_modules/type-check": { "node_modules/type-check": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -19972,28 +20575,30 @@
} }
}, },
"node_modules/typed-array-buffer": { "node_modules/typed-array-buffer": {
"version": "1.0.2", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
"integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bound": "^1.0.3",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"is-typed-array": "^1.1.13" "is-typed-array": "^1.1.14"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/typed-array-byte-length": { "node_modules/typed-array-byte-length": {
"version": "1.0.1", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
"integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bind": "^1.0.8",
"for-each": "^0.3.3", "for-each": "^0.3.3",
"gopd": "^1.0.1", "gopd": "^1.2.0",
"has-proto": "^1.0.3", "has-proto": "^1.2.0",
"is-typed-array": "^1.1.13" "is-typed-array": "^1.1.14"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -20003,16 +20608,18 @@
} }
}, },
"node_modules/typed-array-byte-offset": { "node_modules/typed-array-byte-offset": {
"version": "1.0.2", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
"integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"available-typed-arrays": "^1.0.7", "available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.7", "call-bind": "^1.0.8",
"for-each": "^0.3.3", "for-each": "^0.3.3",
"gopd": "^1.0.1", "gopd": "^1.2.0",
"has-proto": "^1.0.3", "has-proto": "^1.2.0",
"is-typed-array": "^1.1.13" "is-typed-array": "^1.1.15",
"reflect.getprototypeof": "^1.0.9"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -20022,16 +20629,17 @@
} }
}, },
"node_modules/typed-array-length": { "node_modules/typed-array-length": {
"version": "1.0.6", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
"integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bind": "^1.0.7",
"for-each": "^0.3.3", "for-each": "^0.3.3",
"gopd": "^1.0.1", "gopd": "^1.0.1",
"has-proto": "^1.0.3",
"is-typed-array": "^1.1.13", "is-typed-array": "^1.1.13",
"possible-typed-array-names": "^1.0.0" "possible-typed-array-names": "^1.0.0",
"reflect.getprototypeof": "^1.0.6"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -20053,14 +20661,18 @@
} }
}, },
"node_modules/unbox-primitive": { "node_modules/unbox-primitive": {
"version": "1.0.2", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
"integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
"license": "MIT",
"dependencies": { "dependencies": {
"call-bind": "^1.0.2", "call-bound": "^1.0.3",
"has-bigints": "^1.0.2", "has-bigints": "^1.0.2",
"has-symbols": "^1.0.3", "has-symbols": "^1.1.0",
"which-boxed-primitive": "^1.0.2" "which-boxed-primitive": "^1.1.1"
},
"engines": {
"node": ">= 0.4"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
@@ -20130,6 +20742,18 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/unicorn-magic": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
"integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/unified": { "node_modules/unified": {
"version": "10.1.2", "version": "10.1.2",
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz",
@@ -20254,9 +20878,9 @@
} }
}, },
"node_modules/update-browserslist-db": { "node_modules/update-browserslist-db": {
"version": "1.1.0", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
"integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -20273,8 +20897,8 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"escalade": "^3.1.2", "escalade": "^3.2.0",
"picocolors": "^1.0.1" "picocolors": "^1.1.1"
}, },
"bin": { "bin": {
"update-browserslist-db": "cli.js" "update-browserslist-db": "cli.js"
@@ -20367,6 +20991,15 @@
} }
} }
}, },
"node_modules/use-sync-external-store": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -20559,17 +21192,18 @@
} }
}, },
"node_modules/webpack": { "node_modules/webpack": {
"version": "5.94.0", "version": "5.98.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz",
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "^1.0.5", "@types/eslint-scope": "^3.7.7",
"@webassemblyjs/ast": "^1.12.1", "@types/estree": "^1.0.6",
"@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/ast": "^1.14.1",
"@webassemblyjs/wasm-parser": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.14.1",
"acorn": "^8.7.1", "@webassemblyjs/wasm-parser": "^1.14.1",
"acorn-import-attributes": "^1.9.5", "acorn": "^8.14.0",
"browserslist": "^4.21.10", "browserslist": "^4.24.0",
"chrome-trace-event": "^1.0.2", "chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.17.1", "enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1", "es-module-lexer": "^1.2.1",
@@ -20581,9 +21215,9 @@
"loader-runner": "^4.2.0", "loader-runner": "^4.2.0",
"mime-types": "^2.1.27", "mime-types": "^2.1.27",
"neo-async": "^2.6.2", "neo-async": "^2.6.2",
"schema-utils": "^3.2.0", "schema-utils": "^4.3.0",
"tapable": "^2.1.1", "tapable": "^2.1.1",
"terser-webpack-plugin": "^5.3.10", "terser-webpack-plugin": "^5.3.11",
"watchpack": "^2.4.1", "watchpack": "^2.4.1",
"webpack-sources": "^3.2.3" "webpack-sources": "^3.2.3"
}, },
@@ -20958,6 +21592,59 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/webpack/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/webpack/node_modules/ajv-keywords": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
"peerDependencies": {
"ajv": "^8.8.2"
}
},
"node_modules/webpack/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/webpack/node_modules/schema-utils": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
"integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.9.0",
"ajv-formats": "^2.1.1",
"ajv-keywords": "^5.1.0"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/webpack/node_modules/webpack-sources": { "node_modules/webpack/node_modules/webpack-sources": {
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
@@ -21044,43 +21731,80 @@
} }
}, },
"node_modules/which-boxed-primitive": { "node_modules/which-boxed-primitive": {
"version": "1.0.2", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
"integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
"license": "MIT",
"dependencies": { "dependencies": {
"is-bigint": "^1.0.1", "is-bigint": "^1.1.0",
"is-boolean-object": "^1.1.0", "is-boolean-object": "^1.2.1",
"is-number-object": "^1.0.4", "is-number-object": "^1.1.1",
"is-string": "^1.0.5", "is-string": "^1.1.1",
"is-symbol": "^1.0.3" "is-symbol": "^1.1.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/which-builtin-type": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
"integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"function.prototype.name": "^1.1.6",
"has-tostringtag": "^1.0.2",
"is-async-function": "^2.0.0",
"is-date-object": "^1.1.0",
"is-finalizationregistry": "^1.1.0",
"is-generator-function": "^1.0.10",
"is-regex": "^1.2.1",
"is-weakref": "^1.0.2",
"isarray": "^2.0.5",
"which-boxed-primitive": "^1.1.0",
"which-collection": "^1.0.2",
"which-typed-array": "^1.1.16"
},
"engines": {
"node": ">= 0.4"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/which-collection": { "node_modules/which-collection": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
"integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
"license": "MIT",
"dependencies": { "dependencies": {
"is-map": "^2.0.1", "is-map": "^2.0.3",
"is-set": "^2.0.1", "is-set": "^2.0.3",
"is-weakmap": "^2.0.1", "is-weakmap": "^2.0.2",
"is-weakset": "^2.0.1" "is-weakset": "^2.0.3"
},
"engines": {
"node": ">= 0.4"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/which-typed-array": { "node_modules/which-typed-array": {
"version": "1.1.15", "version": "1.1.18",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz",
"integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==",
"license": "MIT",
"dependencies": { "dependencies": {
"available-typed-arrays": "^1.0.7", "available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.7", "call-bind": "^1.0.8",
"call-bound": "^1.0.3",
"for-each": "^0.3.3", "for-each": "^0.3.3",
"gopd": "^1.0.1", "gopd": "^1.2.0",
"has-tostringtag": "^1.0.2" "has-tostringtag": "^1.0.2"
}, },
"engines": { "engines": {

View File

@@ -15,12 +15,12 @@
"i18n_extract": "fedx-scripts formatjs extract", "i18n_extract": "fedx-scripts formatjs extract",
"lint": "fedx-scripts eslint --ext .js --ext .jsx --ext .ts --ext .tsx .", "lint": "fedx-scripts eslint --ext .js --ext .jsx --ext .ts --ext .tsx .",
"lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx --ext .ts --ext .tsx .", "lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx --ext .ts --ext .tsx .",
"prepare": "husky install",
"postinstall": "patch-package", "postinstall": "patch-package",
"snapshot": "fedx-scripts jest --updateSnapshot", "snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress", "start": "fedx-scripts webpack-dev-server --progress",
"dev": "PUBLIC_PATH=/learning/ MFE_CONFIG_API_URL='http://localhost:8000/api/mfe_config/v1' fedx-scripts webpack-dev-server --progress --host apps.local.openedx.io", "dev": "PUBLIC_PATH=/learning/ MFE_CONFIG_API_URL='http://localhost:8000/api/mfe_config/v1' fedx-scripts webpack-dev-server --progress --host apps.local.openedx.io",
"test": "fedx-scripts jest --coverage --passWithNoTests", "test": "fedx-scripts jest --coverage --passWithNoTests",
"test:watch": "fedx-scripts jest --watch --passWithNoTests",
"types": "tsc --noEmit" "types": "tsc --noEmit"
}, },
"author": "edX", "author": "edX",
@@ -34,9 +34,9 @@
}, },
"dependencies": { "dependencies": {
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2", "@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/browserslist-config": "1.2.0", "@edx/browserslist-config": "1.5.0",
"@edx/frontend-component-header": "^5.6.0", "@edx/frontend-component-header": "^5.8.0",
"@edx/frontend-lib-learning-assistant": "^2.2.4", "@edx/frontend-lib-learning-assistant": "^2.19.2",
"@edx/frontend-lib-special-exams": "^3.1.3", "@edx/frontend-lib-special-exams": "^3.1.3",
"@edx/frontend-platform": "^8.0.0", "@edx/frontend-platform": "^8.0.0",
"@edx/openedx-atlas": "^0.6.0", "@edx/openedx-atlas": "^0.6.0",
@@ -45,16 +45,15 @@
"@fortawesome/free-regular-svg-icons": "5.15.4", "@fortawesome/free-regular-svg-icons": "5.15.4",
"@fortawesome/free-solid-svg-icons": "5.15.4", "@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/react-fontawesome": "^0.1.4", "@fortawesome/react-fontawesome": "^0.1.4",
"@openedx/frontend-build": "14.1.2", "@openedx/frontend-build": "^14.2.0",
"@openedx/frontend-plugin-framework": "^1.2.1", "@openedx/frontend-plugin-framework": "^1.2.1",
"@openedx/frontend-slot-footer": "^1.0.2", "@openedx/frontend-slot-footer": "^1.0.2",
"@openedx/paragon": "^22.3.0", "@openedx/paragon": "^22.3.0",
"@popperjs/core": "2.11.8", "@popperjs/core": "2.11.8",
"@reduxjs/toolkit": "1.8.1", "@reduxjs/toolkit": "1.9.7",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"classnames": "2.5.1", "classnames": "2.5.1",
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^12.0.0",
"husky": "7.0.4",
"joi": "^17.11.0", "joi": "^17.11.0",
"js-cookie": "3.0.5", "js-cookie": "3.0.5",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@@ -70,7 +69,7 @@
"react-router": "6.15.0", "react-router": "6.15.0",
"react-router-dom": "6.15.0", "react-router-dom": "6.15.0",
"react-share": "4.4.1", "react-share": "4.4.1",
"redux": "4.1.2", "redux": "4.2.1",
"reselect": "4.1.8", "reselect": "4.1.8",
"sass": "^1.79.3", "sass": "^1.79.3",
"sass-loader": "^16.0.2", "sass-loader": "^16.0.2",
@@ -83,8 +82,8 @@
"@testing-library/jest-dom": "5.17.0", "@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "12.1.5", "@testing-library/react": "12.1.5",
"@testing-library/react-hooks": "^8.0.1", "@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "13.5.0", "@testing-library/user-event": "14.6.1",
"axios-mock-adapter": "2.0.0", "axios-mock-adapter": "2.1.0",
"bundlewatch": "^0.4.0", "bundlewatch": "^0.4.0",
"eslint-import-resolver-webpack": "^0.13.9", "eslint-import-resolver-webpack": "^0.13.9",
"jest": "^29.7.0", "jest": "^29.7.0",
@@ -96,7 +95,7 @@
"files": [ "files": [
{ {
"path": "dist/*.js", "path": "dist/*.js",
"maxSize": "1300kB" "maxSize": "1400kB"
} }
], ],
"normalizeFilenames": "^.+?(\\..+?)\\.\\w+$" "normalizeFilenames": "^.+?(\\..+?)\\.\\w+$"

View File

@@ -9,6 +9,9 @@
<% if (htmlWebpackPlugin.options.OPTIMIZELY_PROJECT_ID) { %> <% if (htmlWebpackPlugin.options.OPTIMIZELY_PROJECT_ID) { %>
<script src="https://www.edx.org/optimizelyjs/<%= htmlWebpackPlugin.options.OPTIMIZELY_PROJECT_ID %>.js"></script> <script src="https://www.edx.org/optimizelyjs/<%= htmlWebpackPlugin.options.OPTIMIZELY_PROJECT_ID %>.js"></script>
<% } %> <% } %>
<% if (htmlWebpackPlugin.options.META_TAG_ROBOTS_CONTENT_ATTR) { %>
<meta name="robots" content="<%= htmlWebpackPlugin.options.META_TAG_ROBOTS_CONTENT_ATTR %>">
<% } %>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@@ -13,6 +13,8 @@ export const DECODE_ROUTES = {
'/course/:courseId/:sequenceId/:unitId', '/course/:courseId/:sequenceId/:unitId',
'/course/:courseId/:sequenceId', '/course/:courseId/:sequenceId',
'/course/:courseId', '/course/:courseId',
'/preview/course/:courseId/:sequenceId/:unitId',
'/preview/course/:courseId/:sequenceId',
], ],
REDIRECT_HOME: 'home/:courseId', REDIRECT_HOME: 'home/:courseId',
REDIRECT_SURVEY: 'survey/:courseId', REDIRECT_SURVEY: 'survey/:courseId',
@@ -46,6 +48,20 @@ export const VERIFIED_MODES = [
'paid-bootcamp', 'paid-bootcamp',
] as const satisfies readonly string[]; ] as const satisfies readonly string[];
export const AUDIT_MODES = [
'audit',
'honor',
'unpaid-executive-education',
'unpaid-bootcamp',
] as const satisfies readonly string[];
// In sync with CourseMode.UPSELL_TO_VERIFIED_MODES
// https://github.com/openedx/edx-platform/blob/master/common/djangoapps/course_modes/models.py#L231
export const ALLOW_UPSELL_MODES = [
'audit',
'honor',
] as const satisfies readonly string[];
export const WIDGETS = { export const WIDGETS = {
DISCUSSIONS: 'DISCUSSIONS', DISCUSSIONS: 'DISCUSSIONS',
NOTIFICATIONS: 'NOTIFICATIONS', NOTIFICATIONS: 'NOTIFICATIONS',

View File

@@ -63,6 +63,7 @@ export const CoursewareSearchResultsFilter = ({ intl }) => {
variant="tabs" variant="tabs"
activeKey={activeKey} activeKey={activeKey}
onSelect={setFilter} onSelect={setFilter}
aria-label={intl.formatMessage(messages.searchResultsFilterDescription)}
> >
{filters.filter(({ count }) => (count > 0)).map(({ key, label }) => ( {filters.filter(({ count }) => (count > 0)).map(({ key, label }) => (
<Tab key={key} eventKey={key} title={label} data-testid={`courseware-search-results-tabs-${key}`}> <Tab key={key} eventKey={key} title={label} data-testid={`courseware-search-results-tabs-${key}`}>

View File

@@ -1,8 +1,8 @@
import React, { useEffect } from 'react'; import React, { useEffect, useRef } from 'react';
import { useParams } from 'react-router'; import { useParams } from 'react-router';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { sendTrackingLogEvent } from '@edx/frontend-platform/analytics'; import { sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { import {
Alert, Button, Icon, Spinner, Alert, Button, Icon, Spinner,
} from '@openedx/paragon'; } from '@openedx/paragon';
@@ -18,7 +18,8 @@ import CoursewareSearchResultsFilterContainer from './CoursewareResultsFilter';
import { updateModel, useModel } from '../../generic/model-store'; import { updateModel, useModel } from '../../generic/model-store';
import { searchCourseContent } from '../data/thunks'; import { searchCourseContent } from '../data/thunks';
const CoursewareSearch = ({ intl, ...sectionProps }) => { const CoursewareSearch = ({ ...sectionProps }) => {
const { formatMessage } = useIntl();
const { courseId } = useParams(); const { courseId } = useParams();
const { query: searchKeyword, setQuery, clearSearchParams } = useCoursewareSearchParams(); const { query: searchKeyword, setQuery, clearSearchParams } = useCoursewareSearchParams();
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -29,6 +30,7 @@ const CoursewareSearch = ({ intl, ...sectionProps }) => {
errors, errors,
total, total,
} = useModel('contentSearchResults', courseId); } = useModel('contentSearchResults', courseId);
const dialogRef = useRef();
useLockScroll(); useLockScroll();
@@ -44,7 +46,8 @@ const CoursewareSearch = ({ intl, ...sectionProps }) => {
searchKeyword: '', searchKeyword: '',
results: [], results: [],
errors: undefined, errors: undefined,
loading: false, loading:
false,
}, },
})); }));
}; };
@@ -66,20 +69,46 @@ const CoursewareSearch = ({ intl, ...sectionProps }) => {
setQuery(value); setQuery(value);
}; };
useEffect(() => {
handleSubmit(searchKeyword);
}, []);
const handleOnChange = (value) => { const handleOnChange = (value) => {
if (value === searchKeyword) { return; } if (value === searchKeyword) { return; }
if (!value) { clearSearch(); } if (!value) { clearSearch(); }
}; };
const handleSearchCloseClick = () => { const close = () => {
clearSearch(); clearSearch();
dispatch(setShowSearch(false)); dispatch(setShowSearch(false));
}; };
const handlePopState = () => close();
const handleBackdropClick = function (event) {
if (event.target === dialogRef.current) {
dialogRef.current.close();
}
};
useEffect(() => {
// We need this to keep the dialog reference when unmounting.
const dialog = dialogRef.current;
// Open the dialog as a modal on render to confine focus within it.
dialogRef.current.showModal();
if (searchKeyword) {
handleSubmit(searchKeyword); // In case it's opened with a search link, we run the search.
}
const controller = new AbortController();
const { signal } = controller;
window.addEventListener('popstate', handlePopState, { signal });
dialog.addEventListener('click', handleBackdropClick, { signal });
return () => controller.abort(); // Removes event listeners.
}, []);
const handleSearchClose = () => close();
let status = 'idle'; let status = 'idle';
if (loading) { if (loading) {
status = 'loading'; status = 'loading';
@@ -90,59 +119,64 @@ const CoursewareSearch = ({ intl, ...sectionProps }) => {
} }
return ( return (
<section className="courseware-search" style={{ '--modal-top-position': top }} data-testid="courseware-search-section" {...sectionProps}> <dialog ref={dialogRef} className="courseware-search" style={{ '--modal-top-position': top }} data-testid="courseware-search-dialog" onClose={handleSearchClose} {...sectionProps}>
<div className="courseware-search__close">
<Button
variant="tertiary"
className="p-1"
aria-label={intl.formatMessage(messages.searchCloseAction)}
onClick={handleSearchCloseClick}
data-testid="courseware-search-close-button"
><Icon src={Close} />
</Button>
</div>
<div className="courseware-search__outer-content"> <div className="courseware-search__outer-content">
<div className="courseware-search__content"> <div className="courseware-search__content" data-testid="courseware-search-content">
<h1 className="h2">{intl.formatMessage(messages.searchModuleTitle)}</h1> <div className="courseware-search__form">
<CoursewareSearchForm <h1 className="h2">{formatMessage(messages.searchModuleTitle)}</h1>
searchTerm={searchKeyword} <CoursewareSearchForm
onSubmit={handleSubmit} searchTerm={searchKeyword}
onChange={handleOnChange} onSubmit={handleSubmit}
placeholder={intl.formatMessage(messages.searchBarPlaceholderText)} onChange={handleOnChange}
/> placeholder={formatMessage(messages.searchBarPlaceholderText)}
{status === 'loading' ? ( />
<div className="courseware-search__spinner" data-testid="courseware-search-spinner"> <div className="courseware-search__close">
<Spinner animation="border" variant="light" screenReaderText={intl.formatMessage(messages.loading)} /> <Button
variant="tertiary"
className="p-1"
aria-label={formatMessage(messages.searchCloseAction)}
onClick={() => dialogRef.current.close()}
data-testid="courseware-search-close-button"
><Icon src={Close} />
</Button>
</div> </div>
) : null} </div>
{status === 'error' && ( <div
<Alert className="mt-4" variant="danger" data-testid="courseware-search-error"> key={status}
{intl.formatMessage(messages.searchResultsError)} className="courseware-search__results"
</Alert> aria-live="assertive"
)} aria-atomic="true"
{status === 'results' ? ( aria-busy={status === 'loading'}
<> data-testid="courseware-search-results"
{total > 0 ? ( role={status === 'results' ? 'alert' : 'none'}
<div >
className="courseware-search__results-summary" {status === 'loading' ? (
aria-live="polite" <div className="courseware-search__spinner" data-testid="courseware-search-spinner">
aria-relevant="all" <Spinner animation="border" variant="light" screenReaderText={formatMessage(messages.loading)} />
aria-atomic="true" </div>
data-testid="courseware-search-summary" ) : null}
>{intl.formatMessage(messages.searchResultsLabel, { total, keyword: lastSearchKeyword })} {status === 'error' && (
</div> <Alert className="mt-4" variant="danger" data-testid="courseware-search-error">
) : null} {formatMessage(messages.searchResultsError)}
<CoursewareSearchResultsFilterContainer /> </Alert>
</> )}
) : null} {status === 'results' ? (
<>
{total > 0 ? (
<div
className="courseware-search__results-summary"
data-testid="courseware-search-summary"
>{formatMessage(messages.searchResultsLabel, { total, keyword: lastSearchKeyword })}
</div>
) : null}
<CoursewareSearchResultsFilterContainer />
</>
) : null}
</div>
</div> </div>
</div> </div>
</section> </dialog>
); );
}; };
CoursewareSearch.propTypes = { export default CoursewareSearch;
intl: intlShape.isRequired,
};
export default injectIntl(CoursewareSearch);

View File

@@ -9,6 +9,7 @@ import {
screen, screen,
waitFor, waitFor,
fireEvent, fireEvent,
within,
} from '../../setupTest'; } from '../../setupTest';
import { CoursewareSearch } from './index'; import { CoursewareSearch } from './index';
import { useElementBoundingBox, useLockScroll, useCoursewareSearchParams } from './hooks'; import { useElementBoundingBox, useLockScroll, useCoursewareSearchParams } from './hooks';
@@ -19,6 +20,7 @@ import { updateModel, useModel } from '../../generic/model-store';
jest.mock('./hooks'); jest.mock('./hooks');
jest.mock('../../generic/model-store', () => ({ jest.mock('../../generic/model-store', () => ({
...jest.requireActual('../../generic/model-store'),
updateModel: jest.fn(), updateModel: jest.fn(),
useModel: jest.fn(), useModel: jest.fn(),
})); }));
@@ -56,7 +58,7 @@ const defaultProps = {
total: 0, total: 0,
}; };
const coursewareSearch = { const defaultSearchParams = {
query: '', query: '',
filter: '', filter: '',
setQuery: jest.fn(), setQuery: jest.fn(),
@@ -96,14 +98,20 @@ const mockModels = ((props = defaultProps) => {
}); });
}); });
const mockSearchParams = ((props = coursewareSearch) => { const mockSearchParams = ((params) => {
const props = { ...defaultSearchParams, ...params };
useCoursewareSearchParams.mockReturnValue(props); useCoursewareSearchParams.mockReturnValue(props);
}); });
describe('CoursewareSearch', () => { describe('CoursewareSearch', () => {
beforeAll(initializeMockApp); beforeAll(() => initializeMockApp());
beforeEach(() => { beforeEach(() => {
mockModels();
mockSearchParams();
});
afterEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
}); });
@@ -113,27 +121,22 @@ describe('CoursewareSearch', () => {
}); });
it('should use useElementBoundingBox() and useLockScroll() hooks', () => { it('should use useElementBoundingBox() and useLockScroll() hooks', () => {
mockModels();
mockSearchParams();
renderComponent(); renderComponent();
expect(useElementBoundingBox).toBeCalledTimes(1); expect(useElementBoundingBox).toHaveBeenCalledTimes(1);
expect(useLockScroll).toBeCalledTimes(1); expect(useLockScroll).toHaveBeenCalledTimes(1);
}); });
it('should have a "--modal-top-position" CSS variable matching the CourseTabsNavigation top position', () => { it('should have a "--modal-top-position" CSS variable matching the CourseTabsNavigation top position', () => {
mockModels();
mockSearchParams();
renderComponent(); renderComponent();
const section = screen.getByTestId('courseware-search-section'); const section = screen.getByTestId('courseware-search-dialog');
expect(section.style.getPropertyValue('--modal-top-position')).toBe(`${tabsTopPosition}px`); expect(section.style.getPropertyValue('--modal-top-position')).toBe(`${tabsTopPosition}px`);
}); });
}); });
describe('when clicking on the "Close" button', () => { describe('when clicking on the "Close" button', () => {
it('should dispatch setShowSearch(false)', async () => { it('should close the dialog', async () => {
mockModels();
renderComponent(); renderComponent();
await waitFor(() => { await waitFor(() => {
@@ -141,7 +144,8 @@ describe('CoursewareSearch', () => {
fireEvent.click(close); fireEvent.click(close);
}); });
expect(setShowSearch).toBeCalledWith(false); expect(HTMLDialogElement.prototype.close).toHaveBeenCalled();
expect(setShowSearch).toHaveBeenCalledWith(false);
}); });
}); });
@@ -149,29 +153,24 @@ describe('CoursewareSearch', () => {
it('should use "--modal-top-position: 0" if nce element is not present', () => { it('should use "--modal-top-position: 0" if nce element is not present', () => {
useElementBoundingBox.mockImplementation(() => undefined); useElementBoundingBox.mockImplementation(() => undefined);
mockModels();
mockSearchParams();
renderComponent(); renderComponent();
const section = screen.getByTestId('courseware-search-section'); const section = screen.getByTestId('courseware-search-dialog');
expect(section.style.getPropertyValue('--modal-top-position')).toBe('0'); expect(section.style.getPropertyValue('--modal-top-position')).toBe('0');
}); });
}); });
describe('when passing extra props', () => { describe('when passing extra props', () => {
it('should pass on extra props to section element', () => { it('should pass on extra props to section element', () => {
mockModels();
mockSearchParams();
renderComponent({ foo: 'bar' }); renderComponent({ foo: 'bar' });
const section = screen.getByTestId('courseware-search-section'); const section = screen.getByTestId('courseware-search-dialog');
expect(section).toHaveAttribute('foo', 'bar'); expect(section).toHaveAttribute('foo', 'bar');
}); });
}); });
describe('when submitting an empty search', () => { describe('when submitting an empty search', () => {
it('should clear the search by dispatch updateModel', async () => { it('should clear the search by dispatch updateModel', async () => {
mockModels();
renderComponent(); renderComponent();
await waitFor(() => { await waitFor(() => {
@@ -203,7 +202,6 @@ describe('CoursewareSearch', () => {
}); });
it('should call searchCourseContent', async () => { it('should call searchCourseContent', async () => {
mockModels();
renderComponent(); renderComponent();
const searchKeyword = 'course'; const searchKeyword = 'course';
@@ -246,19 +244,25 @@ describe('CoursewareSearch', () => {
expect(screen.queryByTestId('courseware-search-summary')).not.toBeInTheDocument(); expect(screen.queryByTestId('courseware-search-summary')).not.toBeInTheDocument();
}); });
it('should show a summary for the results', () => { it('should show a wrapper div with proper aria attributes', () => {
mockModels({ mockModels({
searchKeyword: 'fubar', searchKeyword: 'fubar',
total: 1, total: 1,
}); });
renderComponent(); renderComponent();
expect(screen.queryByTestId('courseware-search-summary').textContent).toBe('Results for "fubar":'); const results = screen.queryByTestId('courseware-search-results');
expect(results).toHaveAttribute('aria-live', 'assertive');
expect(results).toHaveAttribute('aria-atomic', 'true');
expect(results).toHaveAttribute('role', 'alert');
expect(within(results).queryByTestId('courseware-search-summary').textContent).toBe('Results for "fubar":');
}); });
}); });
describe('when clearing the search input', () => { describe('when clearing the search input', () => {
it('should clear the search by dispatch updateModel', async () => { it('should clear the search by dispatch updateModel', async () => {
mockSearchParams({ query: 'fubar' });
mockModels({ mockModels({
searchKeyword: 'fubar', searchKeyword: 'fubar',
total: 2, total: 2,

View File

@@ -1,43 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { SearchField } from '@openedx/paragon'; import { SearchField } from '@openedx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import messages from './messages'; import messages from './messages';
const CoursewareSearchForm = ({ const CoursewareSearchForm = ({
intl,
searchTerm, searchTerm,
onSubmit, onSubmit,
onChange, onChange,
placeholder, placeholder,
}) => ( }) => {
<SearchField.Advanced const { formatMessage } = useIntl();
value={searchTerm}
onSubmit={onSubmit} return (
onChange={onChange} <SearchField.Advanced
submitButtonLocation="external" value={searchTerm}
className="courseware-search-form" onSubmit={onSubmit}
screenReaderText={{ onChange={onChange}
label: intl.formatMessage(messages.searchSubmitLabel),
clearButton: intl.formatMessage(messages.searchClearAction),
submitButton: null, // Remove the sr-only label in the button.
}}
>
<div className="pgn__searchfield_wrapper" data-testid="courseware-search-form">
<SearchField.Label />
<SearchField.Input placeholder={placeholder} autoFocus />
<SearchField.ClearButton />
</div>
<SearchField.SubmitButton
buttonText={intl.formatMessage(messages.searchSubmitLabel)}
submitButtonLocation="external" submitButtonLocation="external"
data-testid="courseware-search-form-submit" className="courseware-search-form"
/> screenReaderText={{
</SearchField.Advanced> label: formatMessage(messages.searchSubmitLabel),
); clearButton: formatMessage(messages.searchClearAction),
submitButton: null, // Remove the sr-only label in the button.
}}
>
<div className="pgn__searchfield_wrapper" data-testid="courseware-search-form">
<SearchField.Label />
<SearchField.Input placeholder={placeholder} autoFocus />
<SearchField.ClearButton />
</div>
<SearchField.SubmitButton
buttonText={formatMessage(messages.searchSubmitLabel)}
submitButtonLocation="external"
data-testid="courseware-search-form-submit"
/>
</SearchField.Advanced>
);
};
CoursewareSearchForm.propTypes = { CoursewareSearchForm.propTypes = {
intl: intlShape.isRequired,
searchTerm: PropTypes.string, searchTerm: PropTypes.string,
onSubmit: PropTypes.func, onSubmit: PropTypes.func,
onChange: PropTypes.func, onChange: PropTypes.func,
@@ -51,4 +52,4 @@ CoursewareSearchForm.defaultProps = {
placeholder: undefined, placeholder: undefined,
}; };
export default injectIntl(CoursewareSearchForm); export default CoursewareSearchForm;

View File

@@ -1,4 +1,4 @@
import React from 'react'; import { useIntl } from '@edx/frontend-platform/i18n';
import { import {
Folder, TextFields, VideoCamera, Article, Folder, TextFields, VideoCamera, Article,
} from '@openedx/paragon/icons'; } from '@openedx/paragon/icons';
@@ -6,6 +6,7 @@ import { getConfig } from '@edx/frontend-platform';
import { Icon } from '@openedx/paragon'; import { Icon } from '@openedx/paragon';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import CoursewareSearchEmpty from './CoursewareSearchEmpty'; import CoursewareSearchEmpty from './CoursewareSearchEmpty';
import messages from './messages';
const iconTypeMapping = { const iconTypeMapping = {
text: TextFields, text: TextFields,
@@ -21,6 +22,8 @@ const CoursewareSearchResults = ({ results = [] }) => {
return <CoursewareSearchEmpty />; return <CoursewareSearchEmpty />;
} }
const { formatMessage } = useIntl();
const baseUrl = `${getConfig().LMS_BASE_URL}`; const baseUrl = `${getConfig().LMS_BASE_URL}`;
return ( return (
@@ -42,24 +45,30 @@ const CoursewareSearchResults = ({ results = [] }) => {
rel: 'nofollow', rel: 'nofollow',
} : { href: `${baseUrl}${url}` }; } : { href: `${baseUrl}${url}` };
const ariaSeaparator = formatMessage(messages.searchResultsBreadcrumbSeparator);
const ariaLocation = location?.length ? formatMessage(messages.searchResultsBreadcrumb, { path: location.join(ariaSeaparator) }) : '';
return ( return (
<a key={id} className="courseware-search-results__item" {...linkProps}> <a key={id} className="courseware-search-results__item" {...linkProps}>
<div className="courseware-search-results__icon"><Icon src={icon} /></div> <div className="courseware-search-results__icon"><Icon src={icon} /></div>
<div className="courseware-search-results__info"> <div className="courseware-search-results__info">
<div className="courseware-search-results__title"> <div className="courseware-search-results__title">
<span>{title}</span> <h3>{title}</h3>
{contentHits ? (<em>{contentHits}</em>) : null } {contentHits ? (<em aria-hidden="true">{contentHits}</em>) : null }
</div> </div>
{location?.length ? (
<ul className="courseware-search-results__breadcrumbs"> <div aria-label={ariaLocation}>
{ {location?.length ? (
<ul className="courseware-search-results__breadcrumbs" aria-hidden="true">
{
// This ignore is necessary because the breadcrumb texts might have duplicates. // This ignore is necessary because the breadcrumb texts might have duplicates.
// The breadcrumbs are not expected to change. // The breadcrumbs are not expected to change.
// eslint-disable-next-line react/no-array-index-key // eslint-disable-next-line react/no-array-index-key
location.map((breadcrumb, i) => (<li key={`${i}:${breadcrumb}`}><div>{breadcrumb}</div></li>)) location.map((breadcrumb, i) => (<li key={`${i}:${breadcrumb}`}><div>{breadcrumb}</div></li>))
} }
</ul> </ul>
) : null} ) : null}
</div>
</div> </div>
</a> </a>
); );

View File

@@ -38,24 +38,29 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Demo Course Overview Demo Course Overview
</span> </h3>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Introduction, then Demo Course Overview."
> >
<li> <ul
<div> aria-hidden="true"
Introduction class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Introduction
Demo Course Overview </div>
</div> </li>
</li> <li>
</ul> <div>
Demo Course Overview
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -91,32 +96,39 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Passing a Course Passing a Course
</span> </h3>
<em> <em
aria-hidden="true"
>
1 1
</em> </em>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: About Exams and Certificates, then edX Exams, then Passing a Course."
> >
<li> <ul
<div> aria-hidden="true"
About Exams and Certificates class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> About Exams and Certificates
edX Exams </div>
</div> </li>
</li> <li>
<li> <div>
<div> edX Exams
Passing a Course </div>
</div> </li>
</li> <li>
</ul> <div>
Passing a Course
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -152,29 +164,34 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Passing a Course Passing a Course
</span> </h3>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: About Exams and Certificates, then edX Exams, then Passing a Course."
> >
<li> <ul
<div> aria-hidden="true"
About Exams and Certificates class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> About Exams and Certificates
edX Exams </div>
</div> </li>
</li> <li>
<li> <div>
<div> edX Exams
Passing a Course </div>
</div> </li>
</li> <li>
</ul> <div>
Passing a Course
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -210,29 +227,34 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Text Input Text Input
</span> </h3>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 1: Getting Started, then Homework - Question Styles, then Text input."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 1: Getting Started class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 1: Getting Started
Homework - Question Styles </div>
</div> </li>
</li> <li>
<li> <div>
<div> Homework - Question Styles
Text input </div>
</div> </li>
</li> <li>
</ul> <div>
Text input
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -268,29 +290,34 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Pointing on a Picture Pointing on a Picture
</span> </h3>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 1: Getting Started, then Homework - Question Styles, then Pointing on a Picture."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 1: Getting Started class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 1: Getting Started
Homework - Question Styles </div>
</div> </li>
</li> <li>
<li> <div>
<div> Homework - Question Styles
Pointing on a Picture </div>
</div> </li>
</li> <li>
</ul> <div>
Pointing on a Picture
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -326,29 +353,34 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Getting Answers Getting Answers
</span> </h3>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: About Exams and Certificates, then edX Exams, then Getting Answers."
> >
<li> <ul
<div> aria-hidden="true"
About Exams and Certificates class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> About Exams and Certificates
edX Exams </div>
</div> </li>
</li> <li>
<li> <div>
<div> edX Exams
Getting Answers </div>
</div> </li>
</li> <li>
</ul> <div>
Getting Answers
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -384,32 +416,39 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Welcome! Welcome!
</span> </h3>
<em> <em
aria-hidden="true"
>
30 30
</em> </em>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Introduction, then Demo Course Overview, then Introduction: Video and Sequences."
> >
<li> <ul
<div> aria-hidden="true"
Introduction class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Introduction
Demo Course Overview </div>
</div> </li>
</li> <li>
<li> <div>
<div> Demo Course Overview
Introduction: Video and Sequences </div>
</div> </li>
</li> <li>
</ul> <div>
Introduction: Video and Sequences
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -445,29 +484,34 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Multiple Choice Questions Multiple Choice Questions
</span> </h3>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 1: Getting Started, then Homework - Question Styles, then Multiple Choice Questions."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 1: Getting Started class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 1: Getting Started
Homework - Question Styles </div>
</div> </li>
</li> <li>
<li> <div>
<div> Homework - Question Styles
Multiple Choice Questions </div>
</div> </li>
</li> <li>
</ul> <div>
Multiple Choice Questions
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -503,29 +547,34 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Numerical Input Numerical Input
</span> </h3>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 1: Getting Started, then Homework - Question Styles, then Numerical Input."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 1: Getting Started class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 1: Getting Started
Homework - Question Styles </div>
</div> </li>
</li> <li>
<li> <div>
<div> Homework - Question Styles
Numerical Input </div>
</div> </li>
</li> <li>
</ul> <div>
Numerical Input
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -561,32 +610,39 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Connecting a Circuit and a Circuit Diagram Connecting a Circuit and a Circuit Diagram
</span> </h3>
<em> <em
aria-hidden="true"
>
3 3
</em> </em>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 1: Getting Started, then Lesson 1 - Getting Started, then Video Presentation Styles."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 1: Getting Started class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 1: Getting Started
Lesson 1 - Getting Started </div>
</div> </li>
</li> <li>
<li> <div>
<div> Lesson 1 - Getting Started
Video Presentation Styles </div>
</div> </li>
</li> <li>
</ul> <div>
Video Presentation Styles
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -622,29 +678,34 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
CAPA CAPA
</span> </h3>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 2: Get Interactive, then Homework - Labs and Demos, then Code Grader."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 2: Get Interactive class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 2: Get Interactive
Homework - Labs and Demos </div>
</div> </li>
</li> <li>
<li> <div>
<div> Homework - Labs and Demos
Code Grader </div>
</div> </li>
</li> <li>
</ul> <div>
Code Grader
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -680,29 +741,34 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Interactive Questions Interactive Questions
</span> </h3>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 1: Getting Started, then Lesson 1 - Getting Started, then Interactive Questions."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 1: Getting Started class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 1: Getting Started
Lesson 1 - Getting Started </div>
</div> </li>
</li> <li>
<li> <div>
<div> Lesson 1 - Getting Started
Interactive Questions </div>
</div> </li>
</li> <li>
</ul> <div>
Interactive Questions
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -738,32 +804,39 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Blank HTML Page Blank HTML Page
</span> </h3>
<em> <em
aria-hidden="true"
>
6 6
</em> </em>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Introduction, then Demo Course Overview, then Introduction: Video and Sequences."
> >
<li> <ul
<div> aria-hidden="true"
Introduction class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Introduction
Demo Course Overview </div>
</div> </li>
</li> <li>
<li> <div>
<div> Demo Course Overview
Introduction: Video and Sequences </div>
</div> </li>
</li> <li>
</ul> <div>
Introduction: Video and Sequences
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -799,32 +872,39 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Discussion Forums Discussion Forums
</span> </h3>
<em> <em
aria-hidden="true"
>
5 5
</em> </em>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 3: Be Social, then Lesson 3 - Be Social, then Discussion Forums."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 3: Be Social class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 3: Be Social
Lesson 3 - Be Social </div>
</div> </li>
</li> <li>
<li> <div>
<div> Lesson 3 - Be Social
Discussion Forums </div>
</div> </li>
</li> <li>
</ul> <div>
Discussion Forums
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -860,32 +940,39 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Overall Grade Overall Grade
</span> </h3>
<em> <em
aria-hidden="true"
>
7 7
</em> </em>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: About Exams and Certificates, then edX Exams, then Overall Grade Performance."
> >
<li> <ul
<div> aria-hidden="true"
About Exams and Certificates class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> About Exams and Certificates
edX Exams </div>
</div> </li>
</li> <li>
<li> <div>
<div> edX Exams
Overall Grade Performance </div>
</div> </li>
</li> <li>
</ul> <div>
Overall Grade Performance
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -921,32 +1008,39 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Blank HTML Page Blank HTML Page
</span> </h3>
<em> <em
aria-hidden="true"
>
3 3
</em> </em>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 3: Be Social, then Lesson 3 - Be Social, then Homework - Find Your Study Buddy."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 3: Be Social class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 3: Be Social
Lesson 3 - Be Social </div>
</div> </li>
</li> <li>
<li> <div>
<div> Lesson 3 - Be Social
Homework - Find Your Study Buddy </div>
</div> </li>
</li> <li>
</ul> <div>
Homework - Find Your Study Buddy
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -982,32 +1076,39 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Find Your Study Buddy Find Your Study Buddy
</span> </h3>
<em> <em
aria-hidden="true"
>
3 3
</em> </em>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 3: Be Social, then Homework - Find Your Study Buddy, then Homework - Find Your Study Buddy."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 3: Be Social class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 3: Be Social
Homework - Find Your Study Buddy </div>
</div> </li>
</li> <li>
<li> <div>
<div> Homework - Find Your Study Buddy
Homework - Find Your Study Buddy </div>
</div> </li>
</li> <li>
</ul> <div>
Homework - Find Your Study Buddy
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -1043,32 +1144,39 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
Be Social Be Social
</span> </h3>
<em> <em
aria-hidden="true"
>
4 4
</em> </em>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 3: Be Social, then Lesson 3 - Be Social, then Be Social."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 3: Be Social class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 3: Be Social
Lesson 3 - Be Social </div>
</div> </li>
</li> <li>
<li> <div>
<div> Lesson 3 - Be Social
Be Social </div>
</div> </li>
</li> <li>
</ul> <div>
Be Social
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -1104,32 +1212,39 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
EdX Exams EdX Exams
</span> </h3>
<em> <em
aria-hidden="true"
>
4 4
</em> </em>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: About Exams and Certificates, then edX Exams, then EdX Exams."
> >
<li> <ul
<div> aria-hidden="true"
About Exams and Certificates class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> About Exams and Certificates
edX Exams </div>
</div> </li>
</li> <li>
<li> <div>
<div> edX Exams
EdX Exams </div>
</div> </li>
</li> <li>
</ul> <div>
EdX Exams
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -1165,32 +1280,39 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
When Are Your Exams? When Are Your Exams?
</span> </h3>
<em> <em
aria-hidden="true"
>
2 2
</em> </em>
</div> </div>
<ul <div
class="courseware-search-results__breadcrumbs" aria-label="Location: Example Week 1: Getting Started, then Lesson 1 - Getting Started, then When Are Your Exams? ."
> >
<li> <ul
<div> aria-hidden="true"
Example Week 1: Getting Started class="courseware-search-results__breadcrumbs"
</div> >
</li> <li>
<li> <div>
<div> Example Week 1: Getting Started
Lesson 1 - Getting Started </div>
</div> </li>
</li> <li>
<li> <div>
<div> Lesson 1 - Getting Started
When Are Your Exams? </div>
</div> </li>
</li> <li>
</ul> <div>
When Are Your Exams?
</div>
</li>
</ul>
</div>
</div> </div>
</a> </a>
<a <a
@@ -1228,10 +1350,13 @@ exports[`CoursewareSearchResults when list of results is provided should match t
<div <div
class="courseware-search-results__title" class="courseware-search-results__title"
> >
<span> <h3>
External Course Link Test External Course Link Test
</span> </h3>
</div> </div>
<div
aria-label=""
/>
</div> </div>
</a> </a>
</div> </div>

View File

@@ -5,13 +5,25 @@
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
width: 100%;
height: 100%;
max-width: none;
margin: 0;
border-top: 1px solid $light-300; border-top: 1px solid $light-300;
z-index: $zindex-modal; // Bootstrap's z-index layer for Modals. z-index: $zindex-modal; // Bootstrap's z-index layer for Modals.
&__form {
position: relative;
.h2 {
margin-right: 2.5rem;
}
}
&__close { &__close {
position: absolute !important; // For some reason it gets overridden position: absolute !important; // For some reason it gets overridden
top: 0.5rem; top: 0;
right: 1rem; right: 0;
font-size: 1.5rem; font-size: 1.5rem;
line-height: 1; line-height: 1;
} }
@@ -89,6 +101,11 @@
font-size: 0.875rem; font-size: 0.875rem;
color: $black; color: $black;
> h3 {
font-size: inherit;
margin: 0;
}
> span { > span {
display: block; display: block;
overflow: hidden; overflow: hidden;
@@ -152,9 +169,16 @@
} }
@media (min-width: map-get($grid-breakpoints, 'md')) { @media (min-width: map-get($grid-breakpoints, 'md')) {
.courseware-search__content { .courseware-search {
padding-top: 8rem; &__close {
right: -2.5rem;
}
&__content {
padding-top: 8rem;
}
} }
} }
body._search-no-scroll { body._search-no-scroll {

View File

@@ -56,7 +56,21 @@ const messages = defineMessages({
defaultMessage: 'There was an error on the search process. Please try again in a few minutes. If the problem persists, please contact the support team.', defaultMessage: 'There was an error on the search process. Please try again in a few minutes. If the problem persists, please contact the support team.',
description: 'Error message to show to the users when there\'s an error with the endpoint or the returned payload format.', description: 'Error message to show to the users when there\'s an error with the endpoint or the returned payload format.',
}, },
searchResultsFilterDescription: {
id: 'learn.coursewareSearch.searchResultsFilterDescription',
defaultMessage: 'Search result filters',
description: 'Screen Reader text to describe the filter options.',
},
searchResultsBreadcrumb: {
id: 'learn.coursewareSearch.searchResultsBreadcrumb',
defaultMessage: 'Location: {path}.',
description: 'Screen Reader text to describe the search result breadcrumbs.',
},
searchResultsBreadcrumbSeparator: {
id: 'learn.searchResultsBreadcrumbSeparator',
defaultMessage: ', then ',
description: 'Screen Reader text to connect breadcrumb sections. i.e.: "Introduction, then Register, then Something else.',
},
// These are translations for labeling the filters // These are translations for labeling the filters
'filter:all': { 'filter:all': {
id: 'learn.coursewareSearch.filter:all', id: 'learn.coursewareSearch.filter:all',

View File

@@ -289,9 +289,17 @@ export async function getProgressTabData(courseId, targetUserId) {
} }
export async function getProctoringInfoData(courseId, username) { export async function getProctoringInfoData(courseId, username) {
let url = `${getConfig().LMS_BASE_URL}/api/edx_proctoring/v1/user_onboarding/status?is_learning_mfe=true&course_id=${encodeURIComponent(courseId)}`; let url;
if (username) { if (!getConfig().EXAMS_BASE_URL) {
url += `&username=${encodeURIComponent(username)}`; url = `${getConfig().LMS_BASE_URL}/api/edx_proctoring/v1/user_onboarding/status?is_learning_mfe=true&course_id=${encodeURIComponent(courseId)}`;
if (username) {
url += `&username=${encodeURIComponent(username)}`;
}
} else {
url = `${getConfig().EXAMS_BASE_URL}/api/v1/student/course_id/${encodeURIComponent(courseId)}/onboarding`;
if (username) {
url += `?username=${encodeURIComponent(username)}`;
}
} }
try { try {
const { data } = await getAuthenticatedHttpClient().get(url); const { data } = await getAuthenticatedHttpClient().get(url);
@@ -449,7 +457,7 @@ export async function unsubscribeFromCourseGoal(token) {
.then(res => camelCaseObject(res)); .then(res => camelCaseObject(res));
} }
export async function getCoursewareSearchEnabledFlag(courseId) { export async function getCoursewareSearchEnabled(courseId) {
const url = new URL(`${getConfig().LMS_BASE_URL}/courses/${courseId}/courseware-search/enabled/`); const url = new URL(`${getConfig().LMS_BASE_URL}/courses/${courseId}/courseware-search/enabled/`);
const { data } = await getAuthenticatedHttpClient().get(url.href); const { data } = await getAuthenticatedHttpClient().get(url.href);
return { enabled: data.enabled || false }; return { enabled: data.enabled || false };

View File

@@ -89,6 +89,7 @@ describe('Course Home Service', () => {
}), }),
title: string('Demonstration Course'), title: string('Demonstration Course'),
username: string('edx'), username: string('edx'),
has_course_author_access: boolean(true),
}, },
}, },
}); });
@@ -133,6 +134,7 @@ describe('Course Home Service', () => {
], ],
title: 'Demonstration Course', title: 'Demonstration Course',
username: 'edx', username: 'edx',
hasCourseAuthorAccess: true,
}; };
const response = getCourseHomeCourseMetadata(courseId, 'outline'); const response = getCourseHomeCourseMetadata(courseId, 'outline');
expect(response).toBeTruthy(); expect(response).toBeTruthy();

View File

@@ -12,7 +12,7 @@ import {
postDismissWelcomeMessage, postDismissWelcomeMessage,
postRequestCert, postRequestCert,
getLiveTabIframe, getLiveTabIframe,
getCoursewareSearchEnabledFlag, getCoursewareSearchEnabled,
searchCourseContentFromAPI, searchCourseContentFromAPI,
} from './api'; } from './api';
@@ -159,7 +159,7 @@ export function processEvent(eventData, getTabData) {
export async function fetchCoursewareSearchSettings(courseId) { export async function fetchCoursewareSearchSettings(courseId) {
try { try {
const { enabled } = await getCoursewareSearchEnabledFlag(courseId); const { enabled } = await getCoursewareSearchEnabled(courseId);
return { enabled }; return { enabled };
} catch (e) { } catch (e) {
return { enabled: false }; return { enabled: false };

View File

@@ -135,6 +135,7 @@ describe('DatesTab', () => {
}); });
it('shows extra info', async () => { it('shows extra info', async () => {
const user = userEvent.setup();
const { items } = await getDay('Sat, Aug 17, 2030'); const { items } = await getDay('Sat, Aug 17, 2030');
expect(items).toHaveLength(3); expect(items).toHaveLength(3);
@@ -142,10 +143,12 @@ describe('DatesTab', () => {
const tipText = "ORA Dates are set by the instructor, and can't be changed"; const tipText = "ORA Dates are set by the instructor, and can't be changed";
expect(screen.queryByText(tipText)).toBeNull(); // tooltip does not start in DOM expect(screen.queryByText(tipText)).toBeNull(); // tooltip does not start in DOM
userEvent.hover(tipIcon); await user.hover(tipIcon);
const tooltip = screen.getByText(tipText); // now it's there screen.getByText(tipText); // now it's there
userEvent.unhover(tipIcon); await user.unhover(tipIcon);
await waitForElementToBeRemoved(tooltip); // and it's gone again await waitFor(() => {
expect(screen.queryByText(tipText)).toBeNull(); // and it's gone again
});
}); });
}); });

View File

@@ -1,9 +1,9 @@
import React, { useEffect, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { Button } from '@openedx/paragon'; import { Button } from '@openedx/paragon';
import { PluginSlot } from '@openedx/frontend-plugin-framework'; import { PluginSlot } from '@openedx/frontend-plugin-framework';
import { AlertList } from '../../generic/user-messages'; import { AlertList } from '../../generic/user-messages';
@@ -15,7 +15,6 @@ import WeeklyLearningGoalCard from './widgets/WeeklyLearningGoalCard';
import CourseTools from './widgets/CourseTools'; import CourseTools from './widgets/CourseTools';
import { fetchOutlineTab } from '../data'; import { fetchOutlineTab } from '../data';
import messages from './messages'; import messages from './messages';
import Section from './Section';
import ShiftDatesAlert from '../suggested-schedule-messaging/ShiftDatesAlert'; import ShiftDatesAlert from '../suggested-schedule-messaging/ShiftDatesAlert';
import UpgradeNotification from '../../generic/upgrade-notification/UpgradeNotification'; import UpgradeNotification from '../../generic/upgrade-notification/UpgradeNotification';
import UpgradeToShiftDatesAlert from '../suggested-schedule-messaging/UpgradeToShiftDatesAlert'; import UpgradeToShiftDatesAlert from '../suggested-schedule-messaging/UpgradeToShiftDatesAlert';
@@ -28,8 +27,10 @@ import { useModel } from '../../generic/model-store';
import WelcomeMessage from './widgets/WelcomeMessage'; import WelcomeMessage from './widgets/WelcomeMessage';
import ProctoringInfoPanel from './widgets/ProctoringInfoPanel'; import ProctoringInfoPanel from './widgets/ProctoringInfoPanel';
import AccountActivationAlert from '../../alerts/logistration-alert/AccountActivationAlert'; import AccountActivationAlert from '../../alerts/logistration-alert/AccountActivationAlert';
import CourseHomeSectionOutlineSlot from '../../plugin-slots/CourseHomeSectionOutlineSlot';
const OutlineTab = ({ intl }) => { const OutlineTab = () => {
const intl = useIntl();
const { const {
courseId, courseId,
proctoringPanelStatus, proctoringPanelStatus,
@@ -42,6 +43,8 @@ const OutlineTab = ({ intl }) => {
userTimezone, userTimezone,
} = useModel('courseHomeMeta', courseId); } = useModel('courseHomeMeta', courseId);
const expandButtonRef = useRef();
const { const {
accessExpiration, accessExpiration,
courseBlocks: { courseBlocks: {
@@ -159,27 +162,21 @@ const OutlineTab = ({ intl }) => {
</> </>
)} )}
<StartOrResumeCourseCard /> <StartOrResumeCourseCard />
<WelcomeMessage courseId={courseId} /> <WelcomeMessage courseId={courseId} nextElementRef={expandButtonRef} />
{rootCourseId && ( {rootCourseId && (
<> <>
<div className="row w-100 m-0 mb-3 justify-content-end"> <div id="expand-button-row" className="row w-100 m-0 mb-3 justify-content-end">
<div className="col-12 col-md-auto p-0"> <div className="col-12 col-md-auto p-0">
<Button variant="outline-primary" block onClick={() => { setExpandAll(!expandAll); }}> <Button ref={expandButtonRef} variant="outline-primary" block onClick={() => { setExpandAll(!expandAll); }}>
{expandAll ? intl.formatMessage(messages.collapseAll) : intl.formatMessage(messages.expandAll)} {expandAll ? intl.formatMessage(messages.collapseAll) : intl.formatMessage(messages.expandAll)}
</Button> </Button>
</div> </div>
</div> </div>
<ol id="courseHome-outline" className="list-unstyled"> <CourseHomeSectionOutlineSlot
{courses[rootCourseId].sectionIds.map((sectionId) => ( expandAll={expandAll}
<Section sectionIds={courses[rootCourseId].sectionIds}
key={sectionId} sections={sections}
courseId={courseId} />
defaultOpen={sections[sectionId].resumeBlock}
expand={expandAll}
section={sections[sectionId]}
/>
))}
</ol>
</> </>
)} )}
</div> </div>
@@ -225,8 +222,4 @@ const OutlineTab = ({ intl }) => {
); );
}; };
OutlineTab.propTypes = { export default OutlineTab;
intl: intlShape.isRequired,
};
export default injectIntl(OutlineTab);

View File

@@ -54,7 +54,7 @@ describe('Outline Tab', () => {
const goalUrl = `${getConfig().LMS_BASE_URL}/api/course_home/save_course_goal`; const goalUrl = `${getConfig().LMS_BASE_URL}/api/course_home/save_course_goal`;
const masqueradeUrl = `${getConfig().LMS_BASE_URL}/courses/${courseId}/masquerade`; const masqueradeUrl = `${getConfig().LMS_BASE_URL}/courses/${courseId}/masquerade`;
const outlineUrl = `${getConfig().LMS_BASE_URL}/api/course_home/outline/${courseId}`; const outlineUrl = `${getConfig().LMS_BASE_URL}/api/course_home/outline/${courseId}`;
const proctoringInfoUrl = `${getConfig().LMS_BASE_URL}/api/edx_proctoring/v1/user_onboarding/status?is_learning_mfe=true&course_id=${encodeURIComponent(courseId)}&username=MockUser`; const proctoringInfoUrl = `${getConfig().EXAMS_BASE_URL}/api/v1/student/course_id/${encodeURIComponent(courseId)}/onboarding?username=MockUser`;
const store = initializeStore(); const store = initializeStore();
const defaultMetadata = Factory.build('courseHomeMetadata'); const defaultMetadata = Factory.build('courseHomeMetadata');
@@ -143,6 +143,7 @@ describe('Outline Tab', () => {
}); });
it('handles expand/collapse all button click', async () => { it('handles expand/collapse all button click', async () => {
const user = userEvent.setup();
await fetchAndRender(); await fetchAndRender();
// Button renders as "Expand All" // Button renders as "Expand All"
const expandButton = screen.getByRole('button', { name: 'Expand all' }); const expandButton = screen.getByRole('button', { name: 'Expand all' });
@@ -153,11 +154,11 @@ describe('Outline Tab', () => {
expect(collapsedSectionNode).toHaveAttribute('aria-expanded', 'false'); expect(collapsedSectionNode).toHaveAttribute('aria-expanded', 'false');
// Click to expand section // Click to expand section
userEvent.click(expandButton); await user.click(expandButton);
await waitFor(() => expect(collapsedSectionNode).toHaveAttribute('aria-expanded', 'true')); await waitFor(() => expect(collapsedSectionNode).toHaveAttribute('aria-expanded', 'true'));
// Click to collapse section // Click to collapse section
userEvent.click(expandButton); await user.click(expandButton);
await waitFor(() => expect(collapsedSectionNode).toHaveAttribute('aria-expanded', 'false')); await waitFor(() => expect(collapsedSectionNode).toHaveAttribute('aria-expanded', 'false'));
}); });
@@ -167,7 +168,7 @@ describe('Outline Tab', () => {
course_blocks: { blocks: courseBlocks.blocks }, course_blocks: { blocks: courseBlocks.blocks },
}); });
await fetchAndRender(); await fetchAndRender();
expect(screen.getByTitle('Completed section')).toBeInTheDocument(); expect(screen.getByLabelText('Completed section')).toBeInTheDocument();
}); });
it('displays correct icon for incomplete assignment', async () => { it('displays correct icon for incomplete assignment', async () => {
@@ -176,7 +177,7 @@ describe('Outline Tab', () => {
course_blocks: { blocks: courseBlocks.blocks }, course_blocks: { blocks: courseBlocks.blocks },
}); });
await fetchAndRender(); await fetchAndRender();
expect(screen.getByTitle('Incomplete section')).toBeInTheDocument(); expect(screen.getByLabelText('Incomplete section')).toBeInTheDocument();
}); });
it('SequenceLink displays link', async () => { it('SequenceLink displays link', async () => {
@@ -275,21 +276,34 @@ describe('Outline Tab', () => {
}); });
it('renders show more/less button and handles click', async () => { it('renders show more/less button and handles click', async () => {
const user = userEvent.setup();
expect(screen.getByTestId('alert-container-welcome')).toBeInTheDocument(); expect(screen.getByTestId('alert-container-welcome')).toBeInTheDocument();
let showMoreButton = screen.getByRole('button', { name: 'Show More' }); let showMoreButton = screen.getByRole('button', { name: 'Show More' });
expect(showMoreButton).toBeInTheDocument(); expect(showMoreButton).toBeInTheDocument();
userEvent.click(showMoreButton); await user.click(showMoreButton);
let showLessButton = screen.getByRole('button', { name: 'Show Less' }); let showLessButton = screen.getByRole('button', { name: 'Show Less' });
expect(showLessButton).toBeInTheDocument(); expect(showLessButton).toBeInTheDocument();
expect(screen.getByTestId('long-welcome-message-iframe')).toBeInTheDocument(); expect(screen.getByTestId('long-welcome-message-iframe')).toBeInTheDocument();
userEvent.click(showLessButton); await user.click(showLessButton);
showLessButton = screen.queryByRole('button', { name: 'Show Less' }); showLessButton = screen.queryByRole('button', { name: 'Show Less' });
expect(showLessButton).not.toBeInTheDocument(); expect(showLessButton).not.toBeInTheDocument();
showMoreButton = screen.getByRole('button', { name: 'Show More' }); showMoreButton = screen.getByRole('button', { name: 'Show More' });
expect(showMoreButton).toBeInTheDocument(); expect(showMoreButton).toBeInTheDocument();
}); });
it('dismisses message', async () => {
expect(screen.getByTestId('alert-container-welcome')).toBeInTheDocument();
const dismissButton = screen.queryByRole('button', { name: 'Dismiss' });
const expandButton = screen.queryByRole('button', { name: 'Expand all' });
fireEvent.click(dismissButton);
expect(expandButton).toHaveFocus();
expect(screen.queryByText('Welcome Message')).toBeNull();
});
}); });
it('ignores comments and misformatted HTML', async () => { it('ignores comments and misformatted HTML', async () => {

View File

@@ -1,137 +0,0 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Collapsible, IconButton, Icon } from '@openedx/paragon';
import { faCheckCircle as fasCheckCircle, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';
import { faCheckCircle as farCheckCircle } from '@fortawesome/free-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { DisabledVisible } from '@openedx/paragon/icons';
import SequenceLink from './SequenceLink';
import { useModel } from '../../generic/model-store';
import genericMessages from '../../generic/messages';
import messages from './messages';
const Section = ({
courseId,
defaultOpen,
expand,
intl,
section,
}) => {
const {
complete,
sequenceIds,
title,
hideFromTOC,
} = section;
const {
courseBlocks: {
sequences,
},
} = useModel('outline', courseId);
const [open, setOpen] = useState(defaultOpen);
useEffect(() => {
setOpen(expand);
}, [expand]);
useEffect(() => {
setOpen(defaultOpen);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const sectionTitle = (
<div className="d-flex row w-100 m-0">
<div className="col-auto p-0">
{complete ? (
<FontAwesomeIcon
icon={fasCheckCircle}
fixedWidth
className="float-left mt-1 text-success"
aria-hidden="true"
title={intl.formatMessage(messages.completedSection)}
/>
) : (
<FontAwesomeIcon
icon={farCheckCircle}
fixedWidth
className="float-left mt-1 text-gray-400"
aria-hidden="true"
title={intl.formatMessage(messages.incompleteSection)}
/>
)}
</div>
<div className="col-7 ml-3 p-0 font-weight-bold text-dark-500">
<span className="align-middle col-6">{title}</span>
<span className="sr-only">
, {intl.formatMessage(complete ? messages.completedSection : messages.incompleteSection)}
</span>
</div>
{hideFromTOC && (
<div className="row">
{hideFromTOC && (
<span className="small d-flex align-content-end">
<Icon className="mr-2" src={DisabledVisible} data-testid="hide-from-toc-section-icon" />
<span data-testid="hide-from-toc-section-text">
{intl.formatMessage(messages.hiddenSection)}
</span>
</span>
)}
</div>
)}
</div>
);
return (
<li>
<Collapsible
className="mb-2"
styling="card-lg"
title={sectionTitle}
open={open}
onToggle={() => { setOpen(!open); }}
iconWhenClosed={(
<IconButton
alt={intl.formatMessage(messages.openSection)}
icon={faPlus}
onClick={() => { setOpen(true); }}
size="sm"
/>
)}
iconWhenOpen={(
<IconButton
alt={intl.formatMessage(genericMessages.close)}
icon={faMinus}
onClick={() => { setOpen(false); }}
size="sm"
/>
)}
>
<ol className="list-unstyled">
{sequenceIds.map((sequenceId, index) => (
<SequenceLink
key={sequenceId}
id={sequenceId}
courseId={courseId}
sequence={sequences[sequenceId]}
first={index === 0}
/>
))}
</ol>
</Collapsible>
</li>
);
};
Section.propTypes = {
courseId: PropTypes.string.isRequired,
defaultOpen: PropTypes.bool.isRequired,
expand: PropTypes.bool.isRequired,
intl: intlShape.isRequired,
section: PropTypes.shape().isRequired,
};
export default injectIntl(Section);

View File

@@ -1,147 +0,0 @@
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import {
FormattedMessage,
FormattedTime,
injectIntl,
intlShape,
} from '@edx/frontend-platform/i18n';
import { faCheckCircle as fasCheckCircle } from '@fortawesome/free-solid-svg-icons';
import { faCheckCircle as farCheckCircle } from '@fortawesome/free-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Icon } from '@openedx/paragon';
import { Block } from '@openedx/paragon/icons';
import EffortEstimate from '../../shared/effort-estimate';
import { useModel } from '../../generic/model-store';
import messages from './messages';
const SequenceLink = ({
id,
intl,
courseId,
first,
sequence,
}) => {
const {
complete,
description,
due,
showLink,
title,
hideFromTOC,
} = sequence;
const {
userTimezone,
} = useModel('outline', courseId);
const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {};
const coursewareUrl = <Link to={`/course/${courseId}/${id}`}>{title}</Link>;
const displayTitle = showLink ? coursewareUrl : title;
const dueDateMessage = (
<FormattedMessage
id="learning.outline.sequence-due-date-set"
defaultMessage="{description} due {assignmentDue}"
description="Used below an assignment title"
values={{
assignmentDue: (
<FormattedTime
key={`${id}-due`}
day="numeric"
month="short"
year="numeric"
timeZoneName="short"
value={due}
{...timezoneFormatArgs}
/>
),
description: description || '',
}}
/>
);
const noDueDateMessage = (
<FormattedMessage
id="learning.outline.sequence-due-date-not-set"
defaultMessage="{description}"
description="Used below an assignment title"
values={{
assignmentDue: (
<FormattedTime
key={`${id}-due`}
day="numeric"
month="short"
year="numeric"
timeZoneName="short"
value={due}
{...timezoneFormatArgs}
/>
),
description: description || '',
}}
/>
);
return (
<li>
<div className={classNames('', { 'mt-2 pt-2 border-top border-light': !first })}>
<div className="row w-100 m-0">
<div className="col-auto p-0">
{complete ? (
<FontAwesomeIcon
icon={fasCheckCircle}
fixedWidth
className="float-left text-success mt-1"
aria-hidden={complete}
title={intl.formatMessage(messages.completedAssignment)}
/>
) : (
<FontAwesomeIcon
icon={farCheckCircle}
fixedWidth
className="float-left text-gray-400 mt-1"
aria-hidden={complete}
title={intl.formatMessage(messages.incompleteAssignment)}
/>
)}
</div>
<div className="col-10 p-0 ml-3 text-break">
<span className="align-middle">{displayTitle}</span>
<span className="sr-only">
, {intl.formatMessage(complete ? messages.completedAssignment : messages.incompleteAssignment)}
</span>
<EffortEstimate className="ml-3 align-middle" block={sequence} />
</div>
</div>
{hideFromTOC && (
<div className="row w-100 my-2 mx-4 pl-3">
<span className="small d-flex">
<Icon className="mr-2" src={Block} data-testid="hide-from-toc-sequence-link-icon" />
<span data-testid="hide-from-toc-sequence-link-text">
{intl.formatMessage(messages.hiddenSequenceLink)}
</span>
</span>
</div>
)}
<div className="row w-100 m-0 ml-3 pl-3">
<small className="text-body pl-2">
{due ? dueDateMessage : noDueDateMessage}
</small>
</div>
</div>
</li>
);
};
SequenceLink.propTypes = {
id: PropTypes.string.isRequired,
intl: intlShape.isRequired,
courseId: PropTypes.string.isRequired,
first: PropTypes.bool.isRequired,
sequence: PropTypes.shape().isRequired,
};
export default injectIntl(SequenceLink);

View File

@@ -341,6 +341,16 @@ const messages = defineMessages({
defaultMessage: 'Onboarding Past Due', defaultMessage: 'Onboarding Past Due',
description: 'Text that show when the deadline of proctortrack onboarding exam has passed, it appears on button that start the onboarding exam however for this case the button is disabled for obvious reason', description: 'Text that show when the deadline of proctortrack onboarding exam has passed, it appears on button that start the onboarding exam however for this case the button is disabled for obvious reason',
}, },
sequenceDueDate: {
id: 'learning.outline.sequence-due-date-set',
defaultMessage: '{description} due {assignmentDue}',
description: 'Used below an assignment title',
},
sequenceNoDueDate: {
id: 'learning.outline.sequence-due-date-not-set',
defaultMessage: '{description}',
description: 'Used below an assignment title',
},
}); });
export default messages; export default messages;

View File

@@ -0,0 +1,25 @@
import React from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Icon } from '@openedx/paragon';
import { Block } from '@openedx/paragon/icons';
import messages from '../messages';
interface Props {}
const HiddenSequenceLink: React.FC<Props> = () => {
const intl = useIntl();
return (
<div className="row w-100 my-2 mx-4 pl-3">
<span className="small d-flex">
<Icon className="mr-2" src={Block} data-testid="hide-from-toc-sequence-link-icon" />
<span data-testid="hide-from-toc-sequence-link-text">
{intl.formatMessage(messages.hiddenSequenceLink)}
</span>
</span>
</div>
);
};
export default HiddenSequenceLink;

View File

@@ -0,0 +1,94 @@
import React, { useEffect, useState } from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Collapsible, IconButton } from '@openedx/paragon';
import { Minus, Plus } from '@openedx/paragon/icons';
import { useModel } from '../../../generic/model-store';
import genericMessages from '../../../generic/messages';
import { useContextId } from '../../../data/hooks';
import messages from '../messages';
import SectionTitle from './SectionTitle';
import SequenceLink from './SequenceLink';
interface Props {
defaultOpen: boolean;
expand: boolean;
section: {
complete: boolean;
sequenceIds: string[];
title: string;
hideFromTOC: boolean;
};
}
const Section: React.FC<Props> = ({
defaultOpen,
expand,
section,
}) => {
const intl = useIntl();
const courseId = useContextId();
const {
complete,
sequenceIds,
title,
hideFromTOC,
} = section;
const {
courseBlocks: {
sequences,
},
} = useModel('outline', courseId);
const [open, setOpen] = useState(defaultOpen);
useEffect(() => {
setOpen(expand);
}, [expand]);
useEffect(() => {
setOpen(defaultOpen);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<li>
<Collapsible
className="mb-2"
styling="card-lg"
title={<SectionTitle {...{ complete, hideFromTOC, title }} />}
open={open}
onToggle={() => { setOpen(!open); }}
iconWhenClosed={(
<IconButton
alt={intl.formatMessage(messages.openSection)}
iconAs={Plus}
onClick={() => { setOpen(true); }}
size="sm"
/>
)}
iconWhenOpen={(
<IconButton
alt={intl.formatMessage(genericMessages.close)}
iconAs={Minus}
onClick={() => { setOpen(false); }}
size="sm"
/>
)}
>
<ol className="list-unstyled">
{sequenceIds.map((sequenceId, index) => (
<SequenceLink
key={sequenceId}
id={sequenceId}
sequence={sequences[sequenceId]}
first={index === 0}
/>
))}
</ol>
</Collapsible>
</li>
);
};
export default Section;

View File

@@ -0,0 +1,59 @@
import React from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Icon } from '@openedx/paragon';
import { CheckCircle, CheckCircleOutline, DisabledVisible } from '@openedx/paragon/icons';
import messages from '../messages';
interface Props {
complete: boolean;
hideFromTOC: boolean;
title: string;
}
const SectionTitle: React.FC<Props> = ({ complete, hideFromTOC, title }) => {
const intl = useIntl();
return (
<div className="d-flex row w-100 m-0">
<div className="col-auto p-0">
{complete ? (
<Icon
src={CheckCircle}
className="float-left mt-1 text-success"
aria-hidden="true"
svgAttrs={{ 'aria-label': intl.formatMessage(messages.completedSection) }}
size="sm"
/>
) : (
<Icon
src={CheckCircleOutline}
className="float-left mt-1 text-gray-400"
aria-hidden="true"
svgAttrs={{ 'aria-label': intl.formatMessage(messages.incompleteSection) }}
size="sm"
/>
)}
</div>
<div className="col-7 ml-3 p-0 font-weight-bold text-dark-500">
<span className="align-middle col-6">{title}</span>
<span className="sr-only">
, {intl.formatMessage(complete ? messages.completedSection : messages.incompleteSection)}
</span>
</div>
{hideFromTOC && (
<div className="row">
{hideFromTOC && (
<span className="small d-flex align-content-end">
<Icon className="mr-2" src={DisabledVisible} data-testid="hide-from-toc-section-icon" />
<span data-testid="hide-from-toc-section-text">
{intl.formatMessage(messages.hiddenSection)}
</span>
</span>
)}
</div>
)}
</div>
);
};
export default SectionTitle;

View File

@@ -0,0 +1,60 @@
import React from 'react';
import { FormattedTime, useIntl } from '@edx/frontend-platform/i18n';
import { useModel } from '../../../generic/model-store';
import { useContextId } from '../../../data/hooks';
import messages from '../messages';
interface Props {
due: string;
id: string;
description: string;
}
const SequenceDueDate: React.FC<Props> = ({
due,
id,
description,
}) => {
const intl = useIntl();
const courseId = useContextId();
let dueDateMessage: string | React.ReactNode = intl.formatMessage(
messages.sequenceNoDueDate,
{ description: description || '' },
);
const {
userTimezone,
} = useModel('outline', courseId);
if (due) {
const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {};
dueDateMessage = intl.formatMessage(
messages.sequenceDueDate,
{
assignmentDue: (
<FormattedTime
key={`${id}-due`}
day="numeric"
month="short"
year="numeric"
timeZoneName="short"
value={due}
{...timezoneFormatArgs}
/>
),
description: description || '',
},
);
}
return (
<div className="row w-100 m-0 ml-3 pl-3">
<small className="text-body pl-2">
{dueDateMessage}
</small>
</div>
);
};
export default SequenceDueDate;

View File

@@ -0,0 +1,56 @@
import React from 'react';
import classNames from 'classnames';
import SequenceDueDate from './SequenceDueDate';
import HiddenSequenceLink from './HiddenSequenceLink';
import SequenceTitle from './SequenceTitle';
interface Props {
id: string;
first: boolean;
sequence: {
complete: boolean;
description: string;
due: string;
showLink: boolean;
title: string;
hideFromTOC: boolean;
}
}
const SequenceLink: React.FC<Props> = ({
id,
first,
sequence,
}) => {
const {
complete,
description,
due,
showLink,
title,
hideFromTOC,
} = sequence;
return (
<li>
<div className={classNames('', { 'mt-2 pt-2 border-top border-light': !first })}>
<SequenceTitle
{...{
complete,
showLink,
title,
sequence,
id,
}}
/>
{hideFromTOC && (
<HiddenSequenceLink />
)}
<SequenceDueDate {...{ due, id, description }} />
</div>
</li>
);
};
export default SequenceLink;

View File

@@ -0,0 +1,63 @@
import React from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Link } from 'react-router-dom';
import { Icon } from '@openedx/paragon';
import { CheckCircleOutline, CheckCircle } from '@openedx/paragon/icons';
import EffortEstimate from '../../../shared/effort-estimate';
import messages from '../messages';
import { useContextId } from '../../../data/hooks';
interface Props {
complete: boolean;
showLink: boolean;
title: string;
sequence: object;
id: string;
}
const SequenceTitle: React.FC<Props> = ({
complete,
showLink,
title,
sequence,
id,
}) => {
const intl = useIntl();
const courseId = useContextId();
const coursewareUrl = <Link to={`/course/${courseId}/${id}`}>{title}</Link>;
const displayTitle = showLink ? coursewareUrl : title;
return (
<div className="row w-100 m-0">
<div className="col-auto p-0">
{complete ? (
<Icon
src={CheckCircle}
className="float-left text-success mt-1"
aria-hidden={complete}
svgAttrs={{ 'aria-label': intl.formatMessage(messages.completedAssignment) }}
size="sm"
/>
) : (
<Icon
src={CheckCircleOutline}
className="float-left text-gray-400 mt-1"
aria-hidden={complete}
svgAttrs={{ 'aria-label': intl.formatMessage(messages.incompleteAssignment) }}
size="sm"
/>
)}
</div>
<div className="col-10 p-0 ml-3 text-break">
<span className="align-middle">{displayTitle}</span>
<span className="sr-only">
, {intl.formatMessage(complete ? messages.completedAssignment : messages.incompleteAssignment)}
</span>
<EffortEstimate className="ml-3 align-middle" block={sequence} />
</div>
</div>
);
};
export default SequenceTitle;

View File

@@ -1,7 +1,7 @@
import React, { useState, useMemo } from 'react'; import { useState, useMemo, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { Alert, Button, TransitionReplace } from '@openedx/paragon'; import { Alert, Button, TransitionReplace } from '@openedx/paragon';
import truncate from 'truncate-html'; import truncate from 'truncate-html';
@@ -11,11 +11,13 @@ import messages from '../messages';
import { useModel } from '../../../generic/model-store'; import { useModel } from '../../../generic/model-store';
import { dismissWelcomeMessage } from '../../data/thunks'; import { dismissWelcomeMessage } from '../../data/thunks';
const WelcomeMessage = ({ courseId, intl }) => { const WelcomeMessage = ({ courseId, nextElementRef }) => {
const intl = useIntl();
const { const {
welcomeMessageHtml, welcomeMessageHtml,
} = useModel('outline', courseId); } = useModel('outline', courseId);
const messageBodyRef = useRef();
const [display, setDisplay] = useState(true); const [display, setDisplay] = useState(true);
// welcomeMessageHtml can contain comments or malformatted HTML which can impact the length that determines // welcomeMessageHtml can contain comments or malformatted HTML which can impact the length that determines
@@ -49,13 +51,20 @@ const WelcomeMessage = ({ courseId, intl }) => {
dismissible dismissible
show={display} show={display}
onClose={() => { onClose={() => {
nextElementRef.current?.focus();
setDisplay(false); setDisplay(false);
dispatch(dismissWelcomeMessage(courseId)); dispatch(dismissWelcomeMessage(courseId));
}} }}
className="raised-card" className="raised-card"
actions={messageCanBeShortened ? [ actions={messageCanBeShortened ? [
<Button <Button
onClick={() => setShowShortMessage(!showShortMessage)} onClick={() => {
if (showShortMessage) {
messageBodyRef.current?.focus();
}
setShowShortMessage(!showShortMessage);
}}
variant="outline-primary" variant="outline-primary"
> >
{showShortMessage ? intl.formatMessage(messages.welcomeMessageShowMoreButton) {showShortMessage ? intl.formatMessage(messages.welcomeMessageShowMoreButton)
@@ -63,32 +72,34 @@ const WelcomeMessage = ({ courseId, intl }) => {
</Button>, </Button>,
] : []} ] : []}
> >
<TransitionReplace className="mb-3" enterDuration={400} exitDuration={200}> <div ref={messageBodyRef} tabIndex="-1">
{showShortMessage ? ( <TransitionReplace className="mb-3" enterDuration={400} exitDuration={200}>
<LmsHtmlFragment {showShortMessage ? (
className="inline-link" <LmsHtmlFragment
data-testid="short-welcome-message-iframe" className="inline-link"
key="short-html" data-testid="short-welcome-message-iframe"
html={shortWelcomeMessageHtml} key="short-html"
title={intl.formatMessage(messages.welcomeMessage)} html={shortWelcomeMessageHtml}
/> title={intl.formatMessage(messages.welcomeMessage)}
) : ( />
<LmsHtmlFragment ) : (
className="inline-link" <LmsHtmlFragment
data-testid="long-welcome-message-iframe" className="inline-link"
key="full-html" data-testid="long-welcome-message-iframe"
html={cleanedWelcomeMessageHtml} key="full-html"
title={intl.formatMessage(messages.welcomeMessage)} html={cleanedWelcomeMessageHtml}
/> title={intl.formatMessage(messages.welcomeMessage)}
)} />
</TransitionReplace> )}
</TransitionReplace>
</div>
</Alert> </Alert>
); );
}; };
WelcomeMessage.propTypes = { WelcomeMessage.propTypes = {
courseId: PropTypes.string.isRequired, courseId: PropTypes.string.isRequired,
intl: intlShape.isRequired, nextElementRef: PropTypes.shape({ current: PropTypes.instanceOf(HTMLInputElement) }),
}; };
export default injectIntl(WelcomeMessage); export default WelcomeMessage;

View File

@@ -1,15 +1,14 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { Button } from '@openedx/paragon'; import { Button } from '@openedx/paragon';
import { useSelector } from 'react-redux';
import { useModel } from '../../generic/model-store'; import { useModel } from '../../generic/model-store';
import messages from './messages'; import messages from './messages';
const ProgressHeader = ({ intl }) => { const ProgressHeader = () => {
const intl = useIntl();
const { const {
courseId, courseId,
targetUserId, targetUserId,
@@ -37,8 +36,4 @@ const ProgressHeader = ({ intl }) => {
); );
}; };
ProgressHeader.propTypes = { export default ProgressHeader;
intl: intlShape.isRequired,
};
export default injectIntl(ProgressHeader);

View File

@@ -1,27 +1,20 @@
import React from 'react'; import React from 'react';
import { useSelector } from 'react-redux'; import { useWindowSize } from '@openedx/paragon';
import { breakpoints, useWindowSize } from '@openedx/paragon'; import { useContextId } from '../../data/hooks';
import ProgressTabCertificateStatusSidePanelSlot from '../../plugin-slots/ProgressTabCertificateStatusSidePanelSlot';
import CertificateStatus from './certificate-status/CertificateStatus';
import CourseCompletion from './course-completion/CourseCompletion'; import CourseCompletion from './course-completion/CourseCompletion';
import CourseGrade from './grades/course-grade/CourseGrade';
import DetailedGrades from './grades/detailed-grades/DetailedGrades';
import GradeSummary from './grades/grade-summary/GradeSummary';
import ProgressHeader from './ProgressHeader'; import ProgressHeader from './ProgressHeader';
import RelatedLinks from './related-links/RelatedLinks';
import ProgressTabCertificateStatusMainBodySlot from '../../plugin-slots/ProgressTabCertificateStatusMainBodySlot';
import ProgressTabCourseGradeSlot from '../../plugin-slots/ProgressTabCourseGradeSlot';
import ProgressTabGradeBreakdownSlot from '../../plugin-slots/ProgressTabGradeBreakdownSlot';
import ProgressTabRelatedLinksSlot from '../../plugin-slots/ProgressTabRelatedLinksSlot';
import { useModel } from '../../generic/model-store'; import { useModel } from '../../generic/model-store';
const ProgressTab = () => { const ProgressTab = () => {
const { const courseId = useContextId();
courseId, const { disableProgressGraph } = useModel('progress', courseId);
} = useSelector(state => state.courseHome);
const {
gradesFeatureIsFullyLocked, disableProgressGraph,
} = useModel('progress', courseId);
const applyLockedOverlay = gradesFeatureIsFullyLocked ? 'locked-overlay' : '';
const windowWidth = useWindowSize().width; const windowWidth = useWindowSize().width;
if (windowWidth === undefined) { if (windowWidth === undefined) {
@@ -31,7 +24,6 @@ const ProgressTab = () => {
return null; return null;
} }
const wideScreen = windowWidth >= breakpoints.large.minWidth;
return ( return (
<> <>
<ProgressHeader /> <ProgressHeader />
@@ -39,18 +31,15 @@ const ProgressTab = () => {
{/* Main body */} {/* Main body */}
<div className="col-12 col-md-8 p-0"> <div className="col-12 col-md-8 p-0">
{!disableProgressGraph && <CourseCompletion />} {!disableProgressGraph && <CourseCompletion />}
{!wideScreen && <CertificateStatus />} <ProgressTabCertificateStatusMainBodySlot />
<CourseGrade /> <ProgressTabCourseGradeSlot />
<div className={`grades my-4 p-4 rounded raised-card ${applyLockedOverlay}`} aria-hidden={gradesFeatureIsFullyLocked}> <ProgressTabGradeBreakdownSlot />
<GradeSummary />
<DetailedGrades />
</div>
</div> </div>
{/* Side panel */} {/* Side panel */}
<div className="col-12 col-md-4 p-0 px-md-4"> <div className="col-12 col-md-4 p-0 px-md-4">
{wideScreen && <CertificateStatus />} <ProgressTabCertificateStatusSidePanelSlot />
<RelatedLinks /> <ProgressTabRelatedLinksSlot />
</div> </div>
</div> </div>
</> </>

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { Factory } from 'rosie'; import { Factory } from 'rosie';
import { getConfig } from '@edx/frontend-platform'; import { getConfig, setConfig } from '@edx/frontend-platform';
import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { breakpoints } from '@openedx/paragon'; import { breakpoints } from '@openedx/paragon';
@@ -111,7 +111,7 @@ describe('Progress Tab', () => {
await fetchAndRender(); await fetchAndRender();
sendTrackEvent.mockClear(); sendTrackEvent.mockClear();
const outlineTabLink = screen.getAllByRole('link', { name: 'Course Outline' }); const outlineTabLink = screen.getAllByRole('link', { name: 'Course outline' });
fireEvent.click(outlineTabLink[1]); // outlineTabLink[0] corresponds to the link in the DetailedGrades component fireEvent.click(outlineTabLink[1]); // outlineTabLink[0] corresponds to the link in the DetailedGrades component
expect(sendTrackEvent).toHaveBeenCalledTimes(1); expect(sendTrackEvent).toHaveBeenCalledTimes(1);
@@ -471,9 +471,12 @@ describe('Progress Tab', () => {
await fetchAndRender(); await fetchAndRender();
expect(screen.getByText('limited feature')).toBeInTheDocument(); expect(screen.getByText('limited feature')).toBeInTheDocument();
expect(screen.getByText('Unlock to work towards a certificate.')).toBeInTheDocument(); expect(screen.getByText('Unlock to work towards a certificate.')).toBeInTheDocument();
expect(screen.queryAllByText('You have limited access to graded assignments as part of the audit track in this course.')).toHaveLength(2); expect(screen.queryAllByText(
'You have limited access to graded assignments as part of the audit track in this course.',
{ exact: false },
)).toHaveLength(2);
expect(screen.queryAllByTestId('blocked-icon')).toHaveLength(4); expect(screen.queryAllByTestId('locked-icon')).toHaveLength(4);
}); });
it('does not render subsections for which showGrades is false', async () => { it('does not render subsections for which showGrades is false', async () => {
@@ -545,6 +548,111 @@ describe('Progress Tab', () => {
await fetchAndRender(); await fetchAndRender();
expect(screen.getByText('Grades & Credit')).toBeInTheDocument(); expect(screen.getByText('Grades & Credit')).toBeInTheDocument();
}); });
it('does not render ungraded subsections when SHOW_UNGRADED_ASSIGNMENT_PROGRESS is false', async () => {
// The second assignment has has_graded_assignment set to false, so it should not be shown.
setTabData({
section_scores: [
{
display_name: 'First section',
subsections: [
{
assignment_type: 'Homework',
block_key: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@12345',
display_name: 'First subsection',
learner_has_access: true,
has_graded_assignment: true,
num_points_earned: 1,
num_points_possible: 2,
percent_graded: 1.0,
show_correctness: 'always',
show_grades: true,
url: 'http://learning.edx.org/course/course-v1:edX+Test+run/first_subsection',
},
],
},
{
display_name: 'Second section',
subsections: [
{
assignment_type: 'Homework',
display_name: 'Second subsection',
learner_has_access: true,
has_graded_assignment: false,
num_points_earned: 1,
num_points_possible: 1,
percent_graded: 1.0,
show_correctness: 'always',
show_grades: true,
url: 'http://learning.edx.org/course/course-v1:edX+Test+run/second_subsection',
},
],
},
],
});
await fetchAndRender();
expect(screen.getByText('First subsection')).toBeInTheDocument();
expect(screen.queryByText('Second subsection')).not.toBeInTheDocument();
});
it('renders both graded and ungraded subsections when SHOW_UNGRADED_ASSIGNMENT_PROGRESS is true', async () => {
// The second assignment has has_graded_assignment set to false.
setConfig({
...getConfig(),
SHOW_UNGRADED_ASSIGNMENT_PROGRESS: true,
});
setTabData({
section_scores: [
{
display_name: 'First section',
subsections: [
{
assignment_type: 'Homework',
block_key: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@12345',
display_name: 'First subsection',
learner_has_access: true,
has_graded_assignment: true,
num_points_earned: 1,
num_points_possible: 2,
percent_graded: 1.0,
show_correctness: 'always',
show_grades: true,
url: 'http://learning.edx.org/course/course-v1:edX+Test+run/first_subsection',
},
],
},
{
display_name: 'Second section',
subsections: [
{
assignment_type: 'Homework',
display_name: 'Second subsection',
learner_has_access: true,
has_graded_assignment: false,
num_points_earned: 1,
num_points_possible: 1,
percent_graded: 1.0,
show_correctness: 'always',
show_grades: true,
url: 'http://learning.edx.org/course/course-v1:edX+Test+run/second_subsection',
},
],
},
],
});
await fetchAndRender();
expect(screen.getByText('First subsection')).toBeInTheDocument();
expect(screen.getByText('Second subsection')).toBeInTheDocument();
// reset config for other tests
setConfig({
...getConfig(),
SHOW_UNGRADED_ASSIGNMENT_PROGRESS: false,
});
});
}); });
describe('Grade Summary', () => { describe('Grade Summary', () => {
@@ -788,7 +896,7 @@ describe('Progress Tab', () => {
sendTrackEvent.mockClear(); sendTrackEvent.mockClear();
expect(screen.getByText('Detailed grades')).toBeInTheDocument(); expect(screen.getByText('Detailed grades')).toBeInTheDocument();
const outlineLink = screen.getAllByRole('link', { name: 'Course Outline' })[0]; const outlineLink = screen.getAllByRole('link', { name: 'Course outline' })[0];
fireEvent.click(outlineLink); fireEvent.click(outlineLink);
expect(sendTrackEvent).toHaveBeenCalledTimes(1); expect(sendTrackEvent).toHaveBeenCalledTimes(1);
@@ -809,7 +917,7 @@ describe('Progress Tab', () => {
// Open the problem score drawer // Open the problem score drawer
fireEvent.click(problemScoreDrawerToggle); fireEvent.click(problemScoreDrawerToggle);
expect(screen.getByText('Problem Scores:')).toBeInTheDocument(); expect(screen.getAllByText('Graded Scores:').length).toBeGreaterThan(1);
expect(screen.getAllByText('0/1')).toHaveLength(3); expect(screen.getAllByText('0/1')).toHaveLength(3);
}); });
@@ -821,6 +929,14 @@ describe('Progress Tab', () => {
expect(screen.getByText('Detailed grades')).toBeInTheDocument(); expect(screen.getByText('Detailed grades')).toBeInTheDocument();
expect(screen.getByText('You currently have no graded problem scores.')).toBeInTheDocument(); expect(screen.getByText('You currently have no graded problem scores.')).toBeInTheDocument();
}); });
it('renders Detailed Grades table when section scores are populated', async () => {
await fetchAndRender();
expect(screen.getByText('Detailed grades')).toBeInTheDocument();
expect(screen.getByText('First subsection'));
expect(screen.getByText('Second subsection'));
});
}); });
describe('Certificate Status', () => { describe('Certificate Status', () => {

View File

@@ -1,23 +1,22 @@
import React, { useEffect } from 'react'; import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch } from 'react-redux';
import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { import { FormattedDate, FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
FormattedDate, FormattedMessage, injectIntl, intlShape,
} from '@edx/frontend-platform/i18n';
import { Button, Card } from '@openedx/paragon'; import { Button, Card } from '@openedx/paragon';
import { getConfig } from '@edx/frontend-platform'; import { getConfig } from '@edx/frontend-platform';
import { useContextId } from '../../../data/hooks';
import { useModel } from '../../../generic/model-store'; import { useModel } from '../../../generic/model-store';
import { COURSE_EXIT_MODES, getCourseExitMode } from '../../../courseware/course/course-exit/utils'; import { COURSE_EXIT_MODES, getCourseExitMode } from '../../../courseware/course/course-exit/utils';
import { DashboardLink, IdVerificationSupportLink, ProfileLink } from '../../../shared/links'; import { DashboardLink, IdVerificationSupportLink, ProfileLink } from '../../../shared/links';
import { requestCert } from '../../data/thunks'; import { requestCert } from '../../data/thunks';
import messages from './messages'; import messages from './messages';
import ProgressCertificateStatusSlot from '../../../plugin-slots/ProgressCertificateStatusSlot';
const CertificateStatus = ({ intl }) => { const CertificateStatus = () => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
entranceExamData, entranceExamData,
@@ -215,7 +214,6 @@ const CertificateStatus = ({ intl }) => {
}); });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
if (!certCase) { if (!certCase) {
return null; return null;
} }
@@ -243,32 +241,32 @@ const CertificateStatus = ({ intl }) => {
return ( return (
<section data-testid="certificate-status-component" className="text-dark-700 mb-4"> <section data-testid="certificate-status-component" className="text-dark-700 mb-4">
<Card className="bg-light-200 raised-card"> <Card className="bg-light-200 raised-card">
<Card.Header title={header} /> <ProgressCertificateStatusSlot courseId={courseId}>
<Card.Section className="small text-gray-700"> <div id={`${certCase}_certificate_status`}>
{body} <Card.Header title={header} />
</Card.Section> <Card.Section className="small text-gray-700">
<Card.Footer> {body}
{buttonText && (buttonLocation || buttonAction) && ( </Card.Section>
<Button <Card.Footer>
variant="outline-brand" {buttonText && (buttonLocation || buttonAction) && (
onClick={() => { <Button
logCertificateStatusButtonClicked(certStatus); variant="outline-brand"
if (buttonAction) { buttonAction(); } onClick={() => {
}} logCertificateStatusButtonClicked(certStatus);
href={buttonLocation} if (buttonAction) { buttonAction(); }
block }}
> href={buttonLocation}
{buttonText} block
</Button> >
)} {buttonText}
</Card.Footer> </Button>
)}
</Card.Footer>
</div>
</ProgressCertificateStatusSlot>
</Card> </Card>
</section> </section>
); );
}; };
CertificateStatus.propTypes = { export default CertificateStatus;
intl: intlShape.isRequired,
};
export default injectIntl(CertificateStatus);

View File

@@ -1,12 +1,13 @@
import React, { useState } from 'react'; import { useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { OverlayTrigger, Popover } from '@openedx/paragon'; import { OverlayTrigger, Popover } from '@openedx/paragon';
import messages from './messages'; import messages from './messages';
const CompleteDonutSegment = ({ completePercentage, intl, lockedPercentage }) => { const CompleteDonutSegment = ({ completePercentage, lockedPercentage }) => {
const intl = useIntl();
const [showCompletePopover, setShowCompletePopover] = useState(false); const [showCompletePopover, setShowCompletePopover] = useState(false);
if (!completePercentage) { if (!completePercentage) {
@@ -82,8 +83,7 @@ const CompleteDonutSegment = ({ completePercentage, intl, lockedPercentage }) =>
CompleteDonutSegment.propTypes = { CompleteDonutSegment.propTypes = {
completePercentage: PropTypes.number.isRequired, completePercentage: PropTypes.number.isRequired,
intl: intlShape.isRequired,
lockedPercentage: PropTypes.number.isRequired, lockedPercentage: PropTypes.number.isRequired,
}; };
export default injectIntl(CompleteDonutSegment); export default CompleteDonutSegment;

View File

@@ -1,8 +1,5 @@
import React from 'react'; import { getLocale, isRtl, useIntl } from '@edx/frontend-platform/i18n';
import { useSelector } from 'react-redux'; import { useContextId } from '../../../data/hooks';
import {
getLocale, injectIntl, intlShape, isRtl,
} from '@edx/frontend-platform/i18n';
import { useModel } from '../../../generic/model-store'; import { useModel } from '../../../generic/model-store';
import CompleteDonutSegment from './CompleteDonutSegment'; import CompleteDonutSegment from './CompleteDonutSegment';
@@ -10,10 +7,9 @@ import IncompleteDonutSegment from './IncompleteDonutSegment';
import LockedDonutSegment from './LockedDonutSegment'; import LockedDonutSegment from './LockedDonutSegment';
import messages from './messages'; import messages from './messages';
const CompletionDonutChart = ({ intl }) => { const CompletionDonutChart = () => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
completionSummary: { completionSummary: {
@@ -62,8 +58,4 @@ const CompletionDonutChart = ({ intl }) => {
); );
}; };
CompletionDonutChart.propTypes = { export default CompletionDonutChart;
intl: intlShape.isRequired,
};
export default injectIntl(CompletionDonutChart);

View File

@@ -1,27 +1,26 @@
import React from 'react'; import { useIntl } from '@edx/frontend-platform/i18n';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import CompletionDonutChart from './CompletionDonutChart'; import CompletionDonutChart from './CompletionDonutChart';
import messages from './messages'; import messages from './messages';
const CourseCompletion = ({ intl }) => ( const CourseCompletion = () => {
<section className="text-dark-700 mb-4 rounded raised-card p-4"> const intl = useIntl();
<div className="row w-100 m-0">
<div className="col-12 col-sm-6 col-md-7 p-0">
<h2>{intl.formatMessage(messages.courseCompletion)}</h2>
<p className="small">
{intl.formatMessage(messages.completionBody)}
</p>
</div>
<div className="col-12 col-sm-6 col-md-5 mt-sm-n3 p-0 text-center">
<CompletionDonutChart />
</div>
</div>
</section>
);
CourseCompletion.propTypes = { return (
intl: intlShape.isRequired, <section className="text-dark-700 mb-4 rounded raised-card p-4">
<div className="row w-100 m-0">
<div className="col-12 col-sm-6 col-md-7 p-0">
<h2>{intl.formatMessage(messages.courseCompletion)}</h2>
<p className="small">
{intl.formatMessage(messages.completionBody)}
</p>
</div>
<div className="col-12 col-sm-6 col-md-5 mt-sm-n3 p-0 text-center">
<CompletionDonutChart />
</div>
</div>
</section>
);
}; };
export default injectIntl(CourseCompletion); export default CourseCompletion;

View File

@@ -1,12 +1,13 @@
import React, { useState } from 'react'; import { useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { OverlayTrigger, Popover } from '@openedx/paragon'; import { OverlayTrigger, Popover } from '@openedx/paragon';
import messages from './messages'; import messages from './messages';
const IncompleteDonutSegment = ({ incompletePercentage, intl }) => { const IncompleteDonutSegment = ({ incompletePercentage }) => {
const intl = useIntl();
const [showIncompletePopover, setShowIncompletePopover] = useState(false); const [showIncompletePopover, setShowIncompletePopover] = useState(false);
if (!incompletePercentage) { if (!incompletePercentage) {
@@ -53,7 +54,6 @@ const IncompleteDonutSegment = ({ incompletePercentage, intl }) => {
IncompleteDonutSegment.propTypes = { IncompleteDonutSegment.propTypes = {
incompletePercentage: PropTypes.number.isRequired, incompletePercentage: PropTypes.number.isRequired,
intl: intlShape.isRequired,
}; };
export default injectIntl(IncompleteDonutSegment); export default IncompleteDonutSegment;

View File

@@ -1,12 +1,13 @@
import React, { useState } from 'react'; import { useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { OverlayTrigger, Popover } from '@openedx/paragon'; import { OverlayTrigger, Popover } from '@openedx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import messages from './messages'; import messages from './messages';
const LockedDonutSegment = ({ intl, lockedPercentage }) => { const LockedDonutSegment = ({ lockedPercentage }) => {
const intl = useIntl();
const [showLockedPopover, setShowLockedPopover] = useState(false); const [showLockedPopover, setShowLockedPopover] = useState(false);
if (!lockedPercentage) { if (!lockedPercentage) {
@@ -65,8 +66,7 @@ const LockedDonutSegment = ({ intl, lockedPercentage }) => {
}; };
LockedDonutSegment.propTypes = { LockedDonutSegment.propTypes = {
intl: intlShape.isRequired,
lockedPercentage: PropTypes.number.isRequired, lockedPercentage: PropTypes.number.isRequired,
}; };
export default injectIntl(LockedDonutSegment); export default LockedDonutSegment;

View File

@@ -1,19 +1,17 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { getConfig } from '@edx/frontend-platform'; import { getConfig } from '@edx/frontend-platform';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { CheckCircle, WarningFilled, WatchFilled } from '@openedx/paragon/icons'; import { CheckCircle, WarningFilled, WatchFilled } from '@openedx/paragon/icons';
import { Hyperlink, Icon } from '@openedx/paragon'; import { Hyperlink, Icon } from '@openedx/paragon';
import { useContextId } from '../../../data/hooks';
import { useModel } from '../../../generic/model-store'; import { useModel } from '../../../generic/model-store';
import { DashboardLink } from '../../../shared/links'; import { DashboardLink } from '../../../shared/links';
import messages from './messages'; import messages from './messages';
const CreditInformation = ({ intl }) => { const CreditInformation = () => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
creditCourseRequirements, creditCourseRequirements,
@@ -36,36 +34,13 @@ const CreditInformation = ({ intl }) => {
switch (creditCourseRequirements.eligibilityStatus) { switch (creditCourseRequirements.eligibilityStatus) {
case 'not_eligible': case 'not_eligible':
eligibilityStatus = ( eligibilityStatus = intl.formatMessage(messages.creditNotEligibleStatus, { creditLink });
<FormattedMessage
id="progress.creditInformation.creditNotEligible"
defaultMessage="You are no longer eligible for credit in this course. Learn more about {creditLink}."
description="Message to learner who are not eligible for course credit, it can because the a requirement deadline have passed"
values={{ creditLink }}
/>
);
break; break;
case 'eligible': case 'eligible':
eligibilityStatus = ( eligibilityStatus = intl.formatMessage(messages.creditEligibleStatus, { dashboardLink, creditLink });
<FormattedMessage
id="progress.creditInformation.creditEligible"
defaultMessage="
You have met the requirements for credit in this course. Go to your
{dashboardLink} to purchase course credit. Or learn more about {creditLink}."
description="After the credit requirements are met, leaners can then do the last step which purchasing the credit. Note that is only doable for leaners after they met all the requirements"
values={{ dashboardLink, creditLink }}
/>
);
break; break;
case 'partial_eligible': case 'partial_eligible':
eligibilityStatus = ( eligibilityStatus = intl.formatMessage(messages.creditPartialEligibleStatus, { creditLink });
<FormattedMessage
id="progress.creditInformation.creditPartialEligible"
defaultMessage="You have not yet met the requirements for credit. Learn more about {creditLink}."
description="This means that one or more requirements is not satisfied yet"
values={{ creditLink }}
/>
);
break; break;
default: default:
break; break;
@@ -108,8 +83,4 @@ const CreditInformation = ({ intl }) => {
); );
}; };
CreditInformation.propTypes = { export default CreditInformation;
intl: intlShape.isRequired,
};
export default injectIntl(CreditInformation);

View File

@@ -35,6 +35,22 @@ const messages = defineMessages({
defaultMessage: 'Verification submitted', defaultMessage: 'Verification submitted',
description: 'It indicate that the learner submitted a requirement but is not graded or reviewed yet', description: 'It indicate that the learner submitted a requirement but is not graded or reviewed yet',
}, },
creditNotEligibleStatus: {
id: 'progress.creditInformation.creditNotEligible',
defaultMessage: 'You are no longer eligible for credit in this course. Learn more about {creditLink}.',
description: 'Message to learner who are not eligible for course credit, it can be that a requirement deadline has passed',
},
creditEligibleStatus: {
id: 'progress.creditInformation.creditEligible',
defaultMessage: `You have met the requirements for credit in this course. Go to your
{dashboardLink} to purchase course credit. Or learn more about {creditLink}.`,
description: 'After the credit requirements are met, leaners can then do the last step which purchasing the credit. Note that is only doable for leaners after they met all the requirements',
},
creditPartialEligibleStatus: {
id: 'progress.creditInformation.creditPartialEligible',
defaultMessage: 'You have not yet met the requirements for credit. Learn more about {creditLink}.',
description: 'This means that one or more requirements is not satisfied yet',
},
}); });
export default messages; export default messages;

View File

@@ -1,6 +1,5 @@
import React from 'react'; import { useIntl } from '@edx/frontend-platform/i18n';
import { useSelector } from 'react-redux'; import { useContextId } from '../../../../data/hooks';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
@@ -11,10 +10,9 @@ import CreditInformation from '../../credit-information/CreditInformation';
import messages from '../messages'; import messages from '../messages';
const CourseGrade = ({ intl }) => { const CourseGrade = () => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
creditCourseRequirements, creditCourseRequirements,
@@ -54,8 +52,4 @@ const CourseGrade = ({ intl }) => {
); );
}; };
CourseGrade.propTypes = { export default CourseGrade;
intl: intlShape.isRequired,
};
export default injectIntl(CourseGrade);

View File

@@ -1,19 +1,17 @@
import React from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { CheckCircle, WarningFilled } from '@openedx/paragon/icons'; import { CheckCircle, WarningFilled } from '@openedx/paragon/icons';
import { breakpoints, Icon, useWindowSize } from '@openedx/paragon'; import { breakpoints, Icon, useWindowSize } from '@openedx/paragon';
import { useContextId } from '../../../../data/hooks';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import GradeRangeTooltip from './GradeRangeTooltip'; import GradeRangeTooltip from './GradeRangeTooltip';
import messages from '../messages'; import messages from '../messages';
const CourseGradeFooter = ({ intl, passingGrade }) => { const CourseGradeFooter = ({ passingGrade }) => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
courseGrade: { courseGrade: {
@@ -86,8 +84,7 @@ const CourseGradeFooter = ({ intl, passingGrade }) => {
}; };
CourseGradeFooter.propTypes = { CourseGradeFooter.propTypes = {
intl: intlShape.isRequired,
passingGrade: PropTypes.number.isRequired, passingGrade: PropTypes.number.isRequired,
}; };
export default injectIntl(CourseGradeFooter); export default CourseGradeFooter;

View File

@@ -1,19 +1,16 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { Locked } from '@openedx/paragon/icons'; import { Locked } from '@openedx/paragon/icons';
import { Button, Icon } from '@openedx/paragon'; import { Button, Icon } from '@openedx/paragon';
import { useContextId } from '../../../../data/hooks';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import messages from '../messages'; import messages from '../messages';
const CourseGradeHeader = ({ intl }) => { const CourseGradeHeader = () => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
org, org,
} = useModel('courseHomeMeta', courseId); } = useModel('courseHomeMeta', courseId);
@@ -83,8 +80,4 @@ const CourseGradeHeader = ({ intl }) => {
); );
}; };
CourseGradeHeader.propTypes = { export default CourseGradeHeader;
intl: intlShape.isRequired,
};
export default injectIntl(CourseGradeHeader);

View File

@@ -1,20 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { import { getLocale, isRtl, useIntl } from '@edx/frontend-platform/i18n';
getLocale, injectIntl, intlShape, isRtl,
} from '@edx/frontend-platform/i18n';
import { OverlayTrigger, Popover } from '@openedx/paragon'; import { OverlayTrigger, Popover } from '@openedx/paragon';
import { useContextId } from '../../../../data/hooks';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import messages from '../messages'; import messages from '../messages';
const CurrentGradeTooltip = ({ intl, tooltipClassName }) => { const CurrentGradeTooltip = ({ tooltipClassName }) => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
courseGrade: { courseGrade: {
@@ -69,8 +65,7 @@ CurrentGradeTooltip.defaultProps = {
}; };
CurrentGradeTooltip.propTypes = { CurrentGradeTooltip.propTypes = {
intl: intlShape.isRequired,
tooltipClassName: PropTypes.string, tooltipClassName: PropTypes.string,
}; };
export default injectIntl(CurrentGradeTooltip); export default CurrentGradeTooltip;

View File

@@ -1,20 +1,16 @@
import React from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import { getLocale, isRtl, useIntl } from '@edx/frontend-platform/i18n';
getLocale, injectIntl, intlShape, isRtl, import { useContextId } from '../../../../data/hooks';
} from '@edx/frontend-platform/i18n';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import CurrentGradeTooltip from './CurrentGradeTooltip'; import CurrentGradeTooltip from './CurrentGradeTooltip';
import PassingGradeTooltip from './PassingGradeTooltip'; import PassingGradeTooltip from './PassingGradeTooltip';
import messages from '../messages'; import messages from '../messages';
const GradeBar = ({ intl, passingGrade }) => { const GradeBar = ({ passingGrade }) => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
courseGrade: { courseGrade: {
@@ -52,8 +48,7 @@ const GradeBar = ({ intl, passingGrade }) => {
}; };
GradeBar.propTypes = { GradeBar.propTypes = {
intl: intlShape.isRequired,
passingGrade: PropTypes.number.isRequired, passingGrade: PropTypes.number.isRequired,
}; };
export default injectIntl(GradeBar); export default GradeBar;

View File

@@ -1,20 +1,19 @@
import React, { useState } from 'react'; import { useState } from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { InfoOutline } from '@openedx/paragon/icons'; import { InfoOutline } from '@openedx/paragon/icons';
import { import {
Icon, IconButton, OverlayTrigger, Popover, Icon, IconButton, OverlayTrigger, Popover,
} from '@openedx/paragon'; } from '@openedx/paragon';
import { useContextId } from '../../../../data/hooks';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import messages from '../messages'; import messages from '../messages';
const GradeRangeTooltip = ({ intl, iconButtonClassName, passingGrade }) => { const GradeRangeTooltip = ({ iconButtonClassName, passingGrade }) => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
gradesFeatureIsFullyLocked, gradesFeatureIsFullyLocked,
@@ -80,8 +79,7 @@ GradeRangeTooltip.defaultProps = {
GradeRangeTooltip.propTypes = { GradeRangeTooltip.propTypes = {
iconButtonClassName: PropTypes.string, iconButtonClassName: PropTypes.string,
intl: intlShape.isRequired,
passingGrade: PropTypes.number.isRequired, passingGrade: PropTypes.number.isRequired,
}; };
export default injectIntl(GradeRangeTooltip); export default GradeRangeTooltip;

View File

@@ -1,14 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import { getLocale, isRtl, useIntl } from '@edx/frontend-platform/i18n';
getLocale, injectIntl, intlShape, isRtl,
} from '@edx/frontend-platform/i18n';
import { OverlayTrigger, Popover } from '@openedx/paragon'; import { OverlayTrigger, Popover } from '@openedx/paragon';
import messages from '../messages'; import messages from '../messages';
const PassingGradeTooltip = ({ intl, passingGrade, tooltipClassName }) => { const PassingGradeTooltip = ({ passingGrade, tooltipClassName }) => {
const intl = useIntl();
const isLocaleRtl = isRtl(getLocale()); const isLocaleRtl = isRtl(getLocale());
let passingGradeDirection = passingGrade < 50 ? '' : '-'; let passingGradeDirection = passingGrade < 50 ? '' : '-';
@@ -54,9 +52,8 @@ PassingGradeTooltip.defaultProps = {
}; };
PassingGradeTooltip.propTypes = { PassingGradeTooltip.propTypes = {
intl: intlShape.isRequired,
passingGrade: PropTypes.number.isRequired, passingGrade: PropTypes.number.isRequired,
tooltipClassName: PropTypes.string, tooltipClassName: PropTypes.string,
}; };
export default injectIntl(PassingGradeTooltip); export default PassingGradeTooltip;

View File

@@ -1,22 +1,20 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { Blocked } from '@openedx/paragon/icons'; import { Locked } from '@openedx/paragon/icons';
import { Icon, Hyperlink } from '@openedx/paragon'; import { Icon, Hyperlink } from '@openedx/paragon';
import { useContextId } from '../../../../data/hooks';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import { showUngradedAssignments } from '../../utils';
import DetailedGradesTable from './DetailedGradesTable'; import DetailedGradesTable from './DetailedGradesTable';
import messages from '../messages'; import messages from '../messages';
const DetailedGrades = ({ intl }) => { const DetailedGrades = () => {
const intl = useIntl();
const { administrator } = getAuthenticatedUser(); const { administrator } = getAuthenticatedUser();
const { const courseId = useContextId();
courseId,
} = useSelector(state => state.courseHome);
const { const {
org, org,
tabs, tabs,
@@ -28,6 +26,8 @@ const DetailedGrades = ({ intl }) => {
} = useModel('progress', courseId); } = useModel('progress', courseId);
const hasSectionScores = sectionScores.length > 0; const hasSectionScores = sectionScores.length > 0;
const emptyTableMsg = showUngradedAssignments()
? messages.detailedGradesEmpty : messages.detailedGradesEmptyOnlyGraded;
const logOutlineLinkClick = () => { const logOutlineLinkClick = () => {
sendTrackEvent('edx.ui.lms.course_progress.detailed_grades.course_outline_link.clicked', { sendTrackEvent('edx.ui.lms.course_progress.detailed_grades.course_outline_link.clicked', {
@@ -54,35 +54,36 @@ const DetailedGrades = ({ intl }) => {
return ( return (
<section className="text-dark-700"> <section className="text-dark-700">
<h3 className="h4 mb-3">{intl.formatMessage(messages.detailedGrades)}</h3> <h3 className="h4">{intl.formatMessage(messages.detailedGrades)}</h3>
<ul className="micro mb-3 pl-3 text-gray-700">
<li>
<b>{intl.formatMessage(messages.practiceScoreLabel)} </b>
{intl.formatMessage(messages.practiceScoreInfoText)}
</li>
<li>
<b>{intl.formatMessage(messages.gradedScoreLabel)} </b>
{intl.formatMessage(messages.gradedScoreInfoText)}
</li>
</ul>
{gradesFeatureIsPartiallyLocked && ( {gradesFeatureIsPartiallyLocked && (
<div className="mb-3 small ml-0 d-inline"> <div className="mb-3 small ml-0 d-inline">
<Icon className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" /> <Icon className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Locked} data-testid="locked-icon" />
{intl.formatMessage(messages.gradeSummaryLimitedAccessExplanation)} {intl.formatMessage(messages.gradeSummaryLimitedAccessExplanation, { upgradeLink: '' })}
</div> </div>
)} )}
{hasSectionScores && ( {hasSectionScores && (
<DetailedGradesTable /> <DetailedGradesTable />
)} )}
{!hasSectionScores && ( {!hasSectionScores && (
<p className="small">{intl.formatMessage(messages.detailedGradesEmpty)}</p> <p className="small">{intl.formatMessage(emptyTableMsg)}</p>
)} )}
{overviewTabUrl && ( {overviewTabUrl && !showUngradedAssignments() && (
<p className="x-small m-0"> <p className="x-small m-0">
<FormattedMessage {intl.formatMessage(messages.ungradedAlert, { outlineLink })}
id="progress.ungradedAlert"
defaultMessage="For progress on ungraded aspects of the course, view your {outlineLink}."
description="Text that precede link that redirect to course outline page"
values={{ outlineLink }}
/>
</p> </p>
)} )}
</section> </section>
); );
}; };
DetailedGrades.propTypes = { export default DetailedGrades;
intl: intlShape.isRequired,
};
export default injectIntl(DetailedGrades);

View File

@@ -1,19 +1,15 @@
import React from 'react'; import { getLocale, isRtl, useIntl } from '@edx/frontend-platform/i18n';
import { useSelector } from 'react-redux';
import {
getLocale, injectIntl, intlShape, isRtl,
} from '@edx/frontend-platform/i18n';
import { DataTable } from '@openedx/paragon'; import { DataTable } from '@openedx/paragon';
import { useContextId } from '../../../../data/hooks';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import messages from '../messages'; import messages from '../messages';
import SubsectionTitleCell from './SubsectionTitleCell'; import SubsectionTitleCell from './SubsectionTitleCell';
import { showUngradedAssignments } from '../../utils';
const DetailedGradesTable = ({ intl }) => { const DetailedGradesTable = () => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
sectionScores, sectionScores,
@@ -24,9 +20,10 @@ const DetailedGradesTable = ({ intl }) => {
sectionScores.map((chapter) => { sectionScores.map((chapter) => {
const subsectionScores = chapter.subsections.filter( const subsectionScores = chapter.subsections.filter(
(subsection) => !!( (subsection) => !!(
subsection.hasGradedAssignment (showUngradedAssignments() || subsection.hasGradedAssignment)
&& subsection.showGrades && subsection.showGrades
&& (subsection.numPointsPossible > 0 || subsection.numPointsEarned > 0)), && (subsection.numPointsPossible > 0 || subsection.numPointsEarned > 0)
),
); );
if (subsectionScores.length === 0) { if (subsectionScores.length === 0) {
@@ -66,8 +63,4 @@ const DetailedGradesTable = ({ intl }) => {
); );
}; };
DetailedGradesTable.propTypes = { export default DetailedGradesTable;
intl: intlShape.isRequired,
};
export default injectIntl(DetailedGradesTable);

View File

@@ -1,18 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { import { getLocale, isRtl, useIntl } from '@edx/frontend-platform/i18n';
getLocale, injectIntl, intlShape, isRtl,
} from '@edx/frontend-platform/i18n';
import messages from '../messages'; import messages from '../messages';
const ProblemScoreDrawer = ({ intl, problemScores, subsection }) => { const ProblemScoreDrawer = ({ problemScores, subsection }) => {
const intl = useIntl();
const isLocaleRtl = isRtl(getLocale()); const isLocaleRtl = isRtl(getLocale());
const scoreLabel = subsection.hasGradedAssignment ? messages.gradedScoreLabel : messages.practiceScoreLabel;
return ( return (
<span className="row w-100 m-0 x-small ml-4 pt-2 pl-1 text-gray-700 flex-nowrap"> <span className="row w-100 m-0 x-small ml-4 pt-2 pl-1 text-gray-700 flex-nowrap">
<span id="problem-score-label" className="col-auto p-0">{intl.formatMessage(messages.problemScoreLabel)}</span> <span id="problem-score-label" className="col-auto p-0">{intl.formatMessage(scoreLabel)}</span>
<div className={classNames('col', 'p-0', { 'greyed-out': !subsection.learnerHasAccess })}> <div className={classNames('col', 'p-0', { 'greyed-out': !subsection.learnerHasAccess })}>
<ul className="list-unstyled row w-100 m-0" aria-labelledby="problem-score-label"> <ul className="list-unstyled row w-100 m-0" aria-labelledby="problem-score-label">
{problemScores.map((problemScore, i) => ( {problemScores.map((problemScore, i) => (
@@ -26,12 +27,14 @@ const ProblemScoreDrawer = ({ intl, problemScores, subsection }) => {
}; };
ProblemScoreDrawer.propTypes = { ProblemScoreDrawer.propTypes = {
intl: intlShape.isRequired,
problemScores: PropTypes.arrayOf(PropTypes.shape({ problemScores: PropTypes.arrayOf(PropTypes.shape({
earned: PropTypes.number.isRequired, earned: PropTypes.number.isRequired,
possible: PropTypes.number.isRequired, possible: PropTypes.number.isRequired,
})).isRequired, })).isRequired,
subsection: PropTypes.shape({ learnerHasAccess: PropTypes.bool }).isRequired, subsection: PropTypes.shape({
learnerHasAccess: PropTypes.bool,
hasGradedAssignment: PropTypes.bool,
}).isRequired,
}; };
export default injectIntl(ProblemScoreDrawer); export default ProblemScoreDrawer;

View File

@@ -1,23 +1,24 @@
import React from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { Collapsible, Icon, Row } from '@openedx/paragon'; import { Collapsible, Icon, Row } from '@openedx/paragon';
import { import {
ArrowDropDown, ArrowDropUp, Blocked, Info, ArrowDropDown,
ArrowDropUp,
Info,
Locked,
} from '@openedx/paragon/icons'; } from '@openedx/paragon/icons';
import { useContextId } from '../../../../data/hooks';
import messages from '../messages'; import messages from '../messages';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import ProblemScoreDrawer from './ProblemScoreDrawer'; import ProblemScoreDrawer from './ProblemScoreDrawer';
const SubsectionTitleCell = ({ intl, subsection }) => { const SubsectionTitleCell = ({ subsection }) => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
org, org,
} = useModel('courseHomeMeta', courseId); } = useModel('courseHomeMeta', courseId);
@@ -61,8 +62,8 @@ const SubsectionTitleCell = ({ intl, subsection }) => {
aria-label={intl.formatMessage(messages.noAccessToSubsection, { displayName })} aria-label={intl.formatMessage(messages.noAccessToSubsection, { displayName })}
className="mr-1 mt-1 d-inline-flex" className="mr-1 mt-1 d-inline-flex"
style={{ height: '1rem', width: '1rem' }} style={{ height: '1rem', width: '1rem' }}
src={Blocked} src={Locked}
data-testid="blocked-icon" data-testid="locked-icon"
/> />
)} )}
{url ? ( {url ? (
@@ -102,7 +103,6 @@ const SubsectionTitleCell = ({ intl, subsection }) => {
}; };
SubsectionTitleCell.propTypes = { SubsectionTitleCell.propTypes = {
intl: intlShape.isRequired,
subsection: PropTypes.shape({ subsection: PropTypes.shape({
blockKey: PropTypes.string.isRequired, blockKey: PropTypes.string.isRequired,
displayName: PropTypes.string.isRequired, displayName: PropTypes.string.isRequired,
@@ -119,4 +119,4 @@ SubsectionTitleCell.propTypes = {
}).isRequired, }).isRequired,
}; };
export default injectIntl(SubsectionTitleCell); export default SubsectionTitleCell;

View File

@@ -1,24 +1,22 @@
import React from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { Blocked } from '@openedx/paragon/icons'; import { Locked } from '@openedx/paragon/icons';
import { Icon } from '@openedx/paragon'; import { Icon } from '@openedx/paragon';
import { useContextId } from '../../../../data/hooks';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import messages from '../messages'; import messages from '../messages';
const AssignmentTypeCell = ({ const AssignmentTypeCell = ({
intl, assignmentType, footnoteMarker, footnoteId, locked, assignmentType, footnoteMarker, footnoteId, locked,
}) => { }) => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
gradesFeatureIsFullyLocked, gradesFeatureIsFullyLocked,
} = useModel('progress', courseId); } = useModel('progress', courseId);
const lockedIcon = locked ? <Icon id={`assignmentTypeBlockedIcon${assignmentType}`} aria-label={intl.formatMessage(messages.noAccessToAssignmentType, { assignmentType })} className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" /> : ''; const lockedIcon = locked ? <Icon id={`assignmentTypeBlockedIcon${assignmentType}`} aria-label={intl.formatMessage(messages.noAccessToAssignmentType, { assignmentType })} className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Locked} data-testid="locked-icon" /> : '';
return ( return (
<div className="d-flex small"> <div className="d-flex small">
@@ -45,7 +43,6 @@ const AssignmentTypeCell = ({
}; };
AssignmentTypeCell.propTypes = { AssignmentTypeCell.propTypes = {
intl: intlShape.isRequired,
assignmentType: PropTypes.string.isRequired, assignmentType: PropTypes.string.isRequired,
footnoteId: PropTypes.string, footnoteId: PropTypes.string,
footnoteMarker: PropTypes.number, footnoteMarker: PropTypes.number,
@@ -58,4 +55,4 @@ AssignmentTypeCell.defaultProps = {
locked: false, locked: false,
}; };
export default injectIntl(AssignmentTypeCell); export default AssignmentTypeCell;

View File

@@ -1,16 +1,14 @@
import React from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { useContextId } from '../../../../data/hooks';
import messages from '../messages'; import messages from '../messages';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
const DroppableAssignmentFootnote = ({ footnotes, intl }) => { const DroppableAssignmentFootnote = ({ footnotes }) => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
gradesFeatureIsFullyLocked, gradesFeatureIsFullyLocked,
} = useModel('progress', courseId); } = useModel('progress', courseId);
@@ -21,14 +19,10 @@ const DroppableAssignmentFootnote = ({ footnotes, intl }) => {
{footnotes.map((footnote, index) => ( {footnotes.map((footnote, index) => (
<li id={`${footnote.id}-footnote`} key={footnote.id} className="x-small mt-1"> <li id={`${footnote.id}-footnote`} key={footnote.id} className="x-small mt-1">
<sup>{index + 1}</sup> <sup>{index + 1}</sup>
<FormattedMessage {intl.formatMessage(messages.droppableAssignmentsText, {
id="progress.footnotes.droppableAssignments" numDroppable: footnote.numDroppable,
defaultMessage="The lowest {numDroppable, plural, one{# {assignmentType} score is} other{# {assignmentType} scores are}} dropped." assignmentType: footnote.assignmentType,
values={{ })}
numDroppable: footnote.numDroppable,
assignmentType: footnote.assignmentType,
}}
/>
<a className="sr-only" href={`#${footnote.id}-ref`} tabIndex={gradesFeatureIsFullyLocked ? '-1' : '0'}> <a className="sr-only" href={`#${footnote.id}-ref`} tabIndex={gradesFeatureIsFullyLocked ? '-1' : '0'}>
{intl.formatMessage(messages.backToContent)} {intl.formatMessage(messages.backToContent)}
</a> </a>
@@ -45,7 +39,6 @@ DroppableAssignmentFootnote.propTypes = {
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
numDroppable: PropTypes.number.isRequired, numDroppable: PropTypes.number.isRequired,
})).isRequired, })).isRequired,
intl: intlShape.isRequired,
}; };
export default injectIntl(DroppableAssignmentFootnote); export default DroppableAssignmentFootnote;

View File

@@ -1,14 +1,13 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { useContextId } from '../../../../data/hooks';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import GradeSummaryHeader from './GradeSummaryHeader'; import GradeSummaryHeader from './GradeSummaryHeader';
import GradeSummaryTable from './GradeSummaryTable'; import GradeSummaryTable from './GradeSummaryTable';
const GradeSummary = () => { const GradeSummary = () => {
const { const courseId = useContextId();
courseId,
} = useSelector(state => state.courseHome);
const { const {
gradingPolicy: { gradingPolicy: {

View File

@@ -1,64 +1,69 @@
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { import {
Icon, IconButton, OverlayTrigger, Popover, Hyperlink,
Icon,
OverlayTrigger,
Stack,
Tooltip,
} from '@openedx/paragon'; } from '@openedx/paragon';
import { Blocked, InfoOutline } from '@openedx/paragon/icons'; import { InfoOutline, Locked } from '@openedx/paragon/icons';
import { useContextId } from '../../../../data/hooks';
import messages from '../messages'; import messages from '../messages';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
const GradeSummaryHeader = ({ intl, allOfSomeAssignmentTypeIsLocked }) => { const GradeSummaryHeader = ({ allOfSomeAssignmentTypeIsLocked }) => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
verifiedMode,
gradesFeatureIsFullyLocked, gradesFeatureIsFullyLocked,
} = useModel('progress', courseId); } = useModel('progress', courseId);
const [showTooltip, setShowTooltip] = useState(false);
return ( return (
<div className="row w-100 m-0 align-items-center"> <Stack gap={2} className="mb-3">
<h3 className="h4 mb-3 mr-1">{intl.formatMessage(messages.gradeSummary)}</h3> <Stack direction="horizontal" gap={2}>
<OverlayTrigger <h3 className="h4 m-0">{intl.formatMessage(messages.gradeSummary)}</h3>
trigger="click" <OverlayTrigger
placement="top" trigger="hover"
show={showTooltip} placement="top"
overlay={( overlay={(
<Popover> <Tooltip>
<Popover.Content className="small text-dark-700">
{intl.formatMessage(messages.gradeSummaryTooltipBody)} {intl.formatMessage(messages.gradeSummaryTooltipBody)}
</Popover.Content> </Tooltip>
</Popover> )}
)} >
> <Icon
<IconButton alt={intl.formatMessage(messages.gradeSummaryTooltipAlt)}
onClick={() => { setShowTooltip(!showTooltip); }} src={InfoOutline}
onBlur={() => { setShowTooltip(false); }} size="sm"
alt={intl.formatMessage(messages.gradeSummaryTooltipAlt)} />
src={InfoOutline} </OverlayTrigger>
iconAs={Icon} </Stack>
className="mb-3"
size="sm"
disabled={gradesFeatureIsFullyLocked}
/>
</OverlayTrigger>
{!gradesFeatureIsFullyLocked && allOfSomeAssignmentTypeIsLocked && ( {!gradesFeatureIsFullyLocked && allOfSomeAssignmentTypeIsLocked && (
<div className="mb-3 small ml-0 d-inline"> <Stack direction="horizontal" className="small" gap={2}>
<Icon className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" /> <Icon size="sm" src={Locked} data-testid="locked-icon" />
{intl.formatMessage(messages.gradeSummaryLimitedAccessExplanation)} <span>
</div> {intl.formatMessage(
messages.gradeSummaryLimitedAccessExplanation,
{
upgradeLink: verifiedMode && (
<Hyperlink destination={verifiedMode.upgradeUrl}>
{intl.formatMessage(messages.courseGradePreviewUpgradeButton)}.
</Hyperlink>
),
},
)}
</span>
</Stack>
)} )}
</div> </Stack>
); );
}; };
GradeSummaryHeader.propTypes = { GradeSummaryHeader.propTypes = {
intl: intlShape.isRequired,
allOfSomeAssignmentTypeIsLocked: PropTypes.bool.isRequired, allOfSomeAssignmentTypeIsLocked: PropTypes.bool.isRequired,
}; };
export default injectIntl(GradeSummaryHeader); export default GradeSummaryHeader;

View File

@@ -1,11 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { import { getLocale, isRtl, useIntl } from '@edx/frontend-platform/i18n';
getLocale, injectIntl, intlShape, isRtl,
} from '@edx/frontend-platform/i18n';
import { DataTable } from '@openedx/paragon'; import { DataTable } from '@openedx/paragon';
import { useContextId } from '../../../../data/hooks';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import AssignmentTypeCell from './AssignmentTypeCell'; import AssignmentTypeCell from './AssignmentTypeCell';
@@ -14,10 +11,9 @@ import GradeSummaryTableFooter from './GradeSummaryTableFooter';
import messages from '../messages'; import messages from '../messages';
const GradeSummaryTable = ({ intl, setAllOfSomeAssignmentTypeIsLocked }) => { const GradeSummaryTable = ({ setAllOfSomeAssignmentTypeIsLocked }) => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
gradingPolicy: { gradingPolicy: {
@@ -34,6 +30,14 @@ const GradeSummaryTable = ({ intl, setAllOfSomeAssignmentTypeIsLocked }) => {
return footnoteId.replace(/[^A-Za-z0-9.-_]+/g, '-'); return footnoteId.replace(/[^A-Za-z0-9.-_]+/g, '-');
}; };
const getGradePercent = (grade) => {
if (Number.isInteger(grade * 100)) {
return (grade * 100).toFixed(0);
}
return (grade * 100).toFixed(2);
};
const hasNoAccessToAssignmentsOfType = (assignmentType) => { const hasNoAccessToAssignmentsOfType = (assignmentType) => {
const subsectionAssignmentsOfType = sectionScores.map((chapter) => chapter.subsections.filter((subsection) => ( const subsectionAssignmentsOfType = sectionScores.map((chapter) => chapter.subsections.filter((subsection) => (
subsection.assignmentType === assignmentType && subsection.hasGradedAssignment subsection.assignmentType === assignmentType && subsection.hasGradedAssignment
@@ -52,31 +56,37 @@ const GradeSummaryTable = ({ intl, setAllOfSomeAssignmentTypeIsLocked }) => {
}; };
const gradeSummaryData = assignmentPolicies.map((assignment) => { const gradeSummaryData = assignmentPolicies.map((assignment) => {
const {
averageGrade,
numDroppable,
type: assignmentType,
weight,
weightedGrade,
} = assignment;
let footnoteId = ''; let footnoteId = '';
let footnoteMarker; let footnoteMarker;
if (assignment.numDroppable > 0) { if (numDroppable > 0) {
footnoteId = getFootnoteId(assignment); footnoteId = getFootnoteId(assignment);
footnotes.push({ footnotes.push({
id: footnoteId, id: footnoteId,
numDroppable: assignment.numDroppable, numDroppable,
assignmentType: assignment.type, assignmentType,
}); });
footnoteMarker = footnotes.length; footnoteMarker = footnotes.length;
} }
const locked = !gradesFeatureIsFullyLocked && hasNoAccessToAssignmentsOfType(assignment.type); const locked = !gradesFeatureIsFullyLocked && hasNoAccessToAssignmentsOfType(assignmentType);
const isLocaleRtl = isRtl(getLocale()); const isLocaleRtl = isRtl(getLocale());
return { return {
type: { type: {
footnoteId, footnoteMarker, type: assignment.type, locked, footnoteId, footnoteMarker, type: assignmentType, locked,
}, },
weight: { weight: `${(assignment.weight * 100).toFixed(0)}${isLocaleRtl ? '\u200f' : ''}%`, locked }, weight: { weight: `${(weight * 100).toFixed(0)}${isLocaleRtl ? '\u200f' : ''}%`, locked },
grade: { grade: `${(assignment.averageGrade * 100).toFixed(0)}${isLocaleRtl ? '\u200f' : ''}%`, locked }, grade: { grade: `${getGradePercent(averageGrade)}${isLocaleRtl ? '\u200f' : ''}%`, locked },
weightedGrade: { weightedGrade: `${(assignment.weightedGrade * 100).toFixed(0)}${isLocaleRtl ? '\u200f' : ''}%`, locked }, weightedGrade: { weightedGrade: `${getGradePercent(weightedGrade)}${isLocaleRtl ? '\u200f' : ''}%`, locked },
}; };
}); });
const getAssignmentTypeCell = (value) => ( const getAssignmentTypeCell = (value) => (
@@ -137,8 +147,7 @@ const GradeSummaryTable = ({ intl, setAllOfSomeAssignmentTypeIsLocked }) => {
}; };
GradeSummaryTable.propTypes = { GradeSummaryTable.propTypes = {
intl: intlShape.isRequired,
setAllOfSomeAssignmentTypeIsLocked: PropTypes.func.isRequired, setAllOfSomeAssignmentTypeIsLocked: PropTypes.func.isRequired,
}; };
export default injectIntl(GradeSummaryTable); export default GradeSummaryTable;

View File

@@ -1,18 +1,35 @@
import React from 'react'; import { useContext } from 'react';
import { useSelector } from 'react-redux';
import { getLocale, isRtl, useIntl } from '@edx/frontend-platform/i18n';
import { import {
getLocale, injectIntl, intlShape, isRtl, DataTable,
} from '@edx/frontend-platform/i18n'; DataTableContext,
import { DataTable } from '@openedx/paragon'; Icon,
import { useModel } from '../../../../generic/model-store'; OverlayTrigger,
Stack,
Tooltip,
} from '@openedx/paragon';
import { InfoOutline } from '@openedx/paragon/icons';
import { useContextId } from '../../../../data/hooks';
import { useModel } from '../../../../generic/model-store';
import messages from '../messages'; import messages from '../messages';
const GradeSummaryTableFooter = ({ intl }) => { const GradeSummaryTableFooter = () => {
const { const intl = useIntl();
courseId,
} = useSelector(state => state.courseHome); const { data } = useContext(DataTableContext);
const rawGrade = data.reduce(
(grade, currentValue) => {
const { weightedGrade } = currentValue.weightedGrade;
const percent = weightedGrade.replace(/%/g, '').trim();
return grade + parseFloat(percent);
},
0,
).toFixed(2);
const courseId = useContextId();
const { const {
courseGrade: { courseGrade: {
@@ -29,15 +46,33 @@ const GradeSummaryTableFooter = ({ intl }) => {
return ( return (
<DataTable.TableFooter className={`border-top border-primary ${bgColor}`}> <DataTable.TableFooter className={`border-top border-primary ${bgColor}`}>
<div className="row w-100 m-0"> <div className="row w-100 m-0">
<div id="weighted-grade-summary" className="col-8 p-0 small">{intl.formatMessage(messages.weightedGradeSummary)}</div> <div id="weighted-grade-summary" className="col-8 p-0 small">
<Stack gap={2} direction="horizontal">
{intl.formatMessage(messages.weightedGradeSummary)}
<OverlayTrigger
trigger="hover"
placement="bottom"
overlay={(
<Tooltip>
{intl.formatMessage(
messages.weightedGradeSummaryTooltip,
{ roundedGrade: totalGrade, rawGrade },
)}
</Tooltip>
)}
>
<Icon
src={InfoOutline}
size="sm"
alt={intl.formatMessage(messages.gradeSummaryTooltipAlt)}
/>
</OverlayTrigger>
</Stack>
</div>
<div data-testid="gradeSummaryFooterTotalWeightedGrade" aria-labelledby="weighted-grade-summary" className="col-4 p-0 text-right font-weight-bold small">{totalGrade}{isLocaleRtl && '\u200f'}%</div> <div data-testid="gradeSummaryFooterTotalWeightedGrade" aria-labelledby="weighted-grade-summary" className="col-4 p-0 text-right font-weight-bold small">{totalGrade}{isLocaleRtl && '\u200f'}%</div>
</div> </div>
</DataTable.TableFooter> </DataTable.TableFooter>
); );
}; };
GradeSummaryTableFooter.propTypes = { export default GradeSummaryTableFooter;
intl: intlShape.isRequired,
};
export default injectIntl(GradeSummaryTableFooter);

View File

@@ -78,7 +78,7 @@ const messages = defineMessages({
}, },
courseOutline: { courseOutline: {
id: 'progress.courseOutline', id: 'progress.courseOutline',
defaultMessage: 'Course Outline', defaultMessage: 'Course outline',
description: 'Anchor text for link that redirects to (course outline) tab', description: 'Anchor text for link that redirects to (course outline) tab',
}, },
currentGradeLabel: { currentGradeLabel: {
@@ -91,9 +91,14 @@ const messages = defineMessages({
defaultMessage: 'Detailed grades', defaultMessage: 'Detailed grades',
description: 'Headline for the (detailed grade) section in the progress tab', description: 'Headline for the (detailed grade) section in the progress tab',
}, },
detailedGradesEmpty: { detailedGradesEmptyOnlyGraded: {
id: 'progress.detailedGrades.emptyTable', id: 'progress.detailedGrades.emptyTable',
defaultMessage: 'You currently have no graded problem scores.', defaultMessage: 'You currently have no graded problem scores.',
description: 'It indicate that there are no graded problem or assignments to be scored',
},
detailedGradesEmpty: {
id: 'progress.detailedGrades.including-ungraded.emptyTable',
defaultMessage: 'You currently have no graded or ungraded problem scores.',
description: 'It indicate that there are no problem or assignments to be scored', description: 'It indicate that there are no problem or assignments to be scored',
}, },
footnotesTitle: { footnotesTitle: {
@@ -128,7 +133,7 @@ const messages = defineMessages({
}, },
gradeSummaryLimitedAccessExplanation: { gradeSummaryLimitedAccessExplanation: {
id: 'progress.gradeSummary.limitedAccessExplanation', id: 'progress.gradeSummary.limitedAccessExplanation',
defaultMessage: 'You have limited access to graded assignments as part of the audit track in this course.', defaultMessage: 'You have limited access to graded assignments as part of the audit track in this course. {upgradeLink}',
description: 'Text shown when learner has limited access to grade feature', description: 'Text shown when learner has limited access to grade feature',
}, },
gradeSummaryTooltipAlt: { gradeSummaryTooltipAlt: {
@@ -158,11 +163,16 @@ const messages = defineMessages({
defaultMessage: 'Passing grade', defaultMessage: 'Passing grade',
description: 'Label for mark on the (grade bar) chart which indicate the poisition of passing grade on the bar', description: 'Label for mark on the (grade bar) chart which indicate the poisition of passing grade on the bar',
}, },
problemScoreLabel: { gradedScoreLabel: {
id: 'progress.detailedGrades.problemScore.label', id: 'progress.detailedGrades.problemScore.label',
defaultMessage: 'Problem Scores:', defaultMessage: 'Graded Scores:',
description: 'Label text which precedes detailed view of all scores per assignment', description: 'Label text which precedes detailed view of all scores per assignment',
}, },
practiceScoreLabel: {
id: 'progress.detailedGrades.practice.problemScore.label',
defaultMessage: 'Practice Scores:',
description: 'Label text which precedes detailed view of all ungraded problem scores per assignment',
},
problemScoreToggleAltText: { problemScoreToggleAltText: {
id: 'progress.detailedGrades.problemScore.toggleButton', id: 'progress.detailedGrades.problemScore.toggleButton',
defaultMessage: 'Toggle individual problem scores for {subsectionTitle}', defaultMessage: 'Toggle individual problem scores for {subsectionTitle}',
@@ -193,7 +203,31 @@ const messages = defineMessages({
defaultMessage: 'Your current weighted grade summary', defaultMessage: 'Your current weighted grade summary',
description: 'It the text precede the sum of weighted grades of all the assignment', description: 'It the text precede the sum of weighted grades of all the assignment',
}, },
weightedGradeSummaryTooltip: {
id: 'progress.weightedGradeSummary',
defaultMessage: 'Your raw weighted grade summary is {rawGrade} and rounds to {roundedGrade}.',
description: 'Tooltip content that explains the rounding of the summary versus individual assignments',
},
practiceScoreInfoText: {
id: 'progress.detailedGrades.practice-label.info.text',
defaultMessage: 'Scores from non-graded activities meant for practice and self-assessment.',
description: 'Information text about non-graded practice score label',
},
gradedScoreInfoText: {
id: 'progress.detailedGrades.problem-label.info.text',
defaultMessage: 'Scores from activities that contribute to your final grade.',
description: 'Information text about graded problem score label',
},
ungradedAlert: {
id: 'progress.ungradedAlert',
defaultMessage: 'For progress on ungraded aspects of the course, view your {outlineLink}.',
description: 'Text that precede link that redirect to course outline page',
},
droppableAssignmentsText: {
id: 'progress.footnotes.droppableAssignments',
defaultMessage: 'The lowest {numDroppable, plural, one{# {assignmentType} score is} other{# {assignmentType} scores are}} dropped.',
description: 'Footnote text stating how many assignments are dropped',
},
}); });
export default messages; export default messages;

View File

@@ -1,18 +1,15 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { Hyperlink } from '@openedx/paragon'; import { Hyperlink } from '@openedx/paragon';
import { useContextId } from '../../../data/hooks';
import messages from './messages'; import messages from './messages';
import { useModel } from '../../../generic/model-store'; import { useModel } from '../../../generic/model-store';
const RelatedLinks = ({ intl }) => { const RelatedLinks = () => {
const { const intl = useIntl();
courseId, const courseId = useContextId();
} = useSelector(state => state.courseHome);
const { const {
org, org,
tabs, tabs,
@@ -58,8 +55,4 @@ const RelatedLinks = ({ intl }) => {
); );
}; };
RelatedLinks.propTypes = { export default RelatedLinks;
intl: intlShape.isRequired,
};
export default injectIntl(RelatedLinks);

View File

@@ -18,7 +18,7 @@ const messages = defineMessages({
}, },
outlineCardLink: { outlineCardLink: {
id: 'progress.relatedLinks.outlineCard.link', id: 'progress.relatedLinks.outlineCard.link',
defaultMessage: 'Course Outline', defaultMessage: 'Course outline',
description: 'Anchor text for link that redirects to course outline tab', description: 'Anchor text for link that redirects to course outline tab',
}, },
relatedLinks: { relatedLinks: {

View File

@@ -0,0 +1,7 @@
import { getConfig } from '@edx/frontend-platform';
/* eslint-disable import/prefer-default-export */
export const showUngradedAssignments = () => (
getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === 'true'
|| getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === true
);

View File

@@ -73,13 +73,13 @@ describe('Course Tabs Navigation', () => {
it('should NOT render CoursewareSearch if the flag is off', () => { it('should NOT render CoursewareSearch if the flag is off', () => {
renderComponent(); renderComponent();
expect(screen.queryByTestId('courseware-search-section')).not.toBeInTheDocument(); expect(screen.queryByTestId('courseware-search-dialog')).not.toBeInTheDocument();
}); });
it('should render CoursewareSearch if the flag is on', () => { it('should render CoursewareSearch if the flag is on', () => {
useCoursewareSearchState.mockImplementation(() => ({ show: true })); useCoursewareSearchState.mockImplementation(() => ({ show: true }));
renderComponent(); renderComponent();
expect(screen.queryByTestId('courseware-search-section')).toBeInTheDocument(); expect(screen.queryByTestId('courseware-search-dialog')).toBeInTheDocument();
}); });
}); });

View File

@@ -19,62 +19,50 @@ import { handleNextSectionCelebration } from './course/celebration';
import withParamsAndNavigation from './utils'; import withParamsAndNavigation from './utils';
// Look at where this is called in componentDidUpdate for more info about its usage // Look at where this is called in componentDidUpdate for more info about its usage
const checkResumeRedirect = memoize((courseStatus, courseId, sequenceId, firstSequenceId, navigate) => { export const checkResumeRedirect = memoize(
if (courseStatus === 'loaded' && !sequenceId) { (courseStatus, courseId, sequenceId, firstSequenceId, navigate, isPreview) => {
// Note that getResumeBlock is just an API call, not a redux thunk. if (courseStatus === 'loaded' && !sequenceId) {
getResumeBlock(courseId).then((data) => { // Note that getResumeBlock is just an API call, not a redux thunk.
// This is a replace because we don't want this change saved in the browser's history. getResumeBlock(courseId).then((data) => {
if (data.sectionId && data.unitId) { // This is a replace because we don't want this change saved in the browser's history.
navigate(`/course/${courseId}/${data.sectionId}/${data.unitId}`, { replace: true }); if (data.sectionId && data.unitId) {
} else if (firstSequenceId) { const baseUrl = `/course/${courseId}/${data.sectionId}`;
navigate(`/course/${courseId}/${firstSequenceId}`, { replace: true }); const sequenceUrl = isPreview ? `/preview${baseUrl}` : baseUrl;
} navigate(`${sequenceUrl}/${data.unitId}`, { replace: true });
}); } else if (firstSequenceId) {
} navigate(`/course/${courseId}/${firstSequenceId}`, { replace: true });
}); }
}, () => {});
// Look at where this is called in componentDidUpdate for more info about its usage
const checkSectionUnitToUnitRedirect = memoize((courseStatus, courseId, sequenceStatus, section, unitId, navigate) => {
if (courseStatus === 'loaded' && sequenceStatus === 'failed' && section && unitId) {
navigate(`/course/${courseId}/${unitId}`, { replace: true });
}
});
// Look at where this is called in componentDidUpdate for more info about its usage
const checkSectionToSequenceRedirect = memoize((courseStatus, courseId, sequenceStatus, section, unitId, navigate) => {
if (courseStatus === 'loaded' && sequenceStatus === 'failed' && section && !unitId) {
// If the section is non-empty, redirect to its first sequence.
if (section.sequenceIds && section.sequenceIds[0]) {
navigate(`/course/${courseId}/${section.sequenceIds[0]}`, { replace: true });
// Otherwise, just go to the course root, letting the resume redirect take care of things.
} else {
navigate(`/course/${courseId}`, { replace: true });
} }
},
);
// Look at where this is called in componentDidUpdate for more info about its usage
export const checkSectionUnitToUnitRedirect = memoize((
courseStatus,
courseId,
sequenceStatus,
section,
unitId,
navigate,
isPreview,
) => {
if (courseStatus === 'loaded' && sequenceStatus === 'failed' && section && unitId) {
const baseUrl = `/course/${courseId}`;
const courseUrl = isPreview ? `/preview${baseUrl}` : baseUrl;
navigate(`${courseUrl}/${unitId}`, { replace: true });
} }
}); });
// Look at where this is called in componentDidUpdate for more info about its usage // Look at where this is called in componentDidUpdate for more info about its usage
const checkUnitToSequenceUnitRedirect = memoize( export const checkSectionToSequenceRedirect = memoize(
(courseStatus, courseId, sequenceStatus, sequenceMightBeUnit, sequenceId, section, routeUnitId, navigate) => { (courseStatus, courseId, sequenceStatus, section, unitId, navigate) => {
if (courseStatus === 'loaded' && sequenceStatus === 'failed' && !section && !routeUnitId) { if (courseStatus === 'loaded' && sequenceStatus === 'failed' && section && !unitId) {
if (sequenceMightBeUnit) { // If the section is non-empty, redirect to its first sequence.
// If the sequence failed to load as a sequence, but it is marked as a possible unit, then if (section.sequenceIds && section.sequenceIds[0]) {
// we need to look up the correct parent sequence for it, and redirect there. navigate(`/course/${courseId}/${section.sequenceIds[0]}`, { replace: true });
const unitId = sequenceId; // just for clarity during the rest of this method // Otherwise, just go to the course root, letting the resume redirect take care of things.
getSequenceForUnitDeprecated(courseId, unitId).then(
parentId => {
if (parentId) {
navigate(`/course/${courseId}/${parentId}/${unitId}`, { replace: true });
} else {
navigate(`/course/${courseId}`, { replace: true });
}
},
() => { // error case
navigate(`/course/${courseId}`, { replace: true });
},
);
} else { } else {
// Invalid sequence that isn't a unit either. Redirect up to main course.
navigate(`/course/${courseId}`, { replace: true }); navigate(`/course/${courseId}`, { replace: true });
} }
} }
@@ -82,41 +70,80 @@ const checkUnitToSequenceUnitRedirect = memoize(
); );
// Look at where this is called in componentDidUpdate for more info about its usage // Look at where this is called in componentDidUpdate for more info about its usage
const checkSequenceToSequenceUnitRedirect = memoize((courseId, sequenceStatus, sequence, unitId, navigate) => { export const checkUnitToSequenceUnitRedirect = memoize((
if (sequenceStatus === 'loaded' && sequence.id && !unitId) { courseStatus,
if (sequence.unitIds !== undefined && sequence.unitIds.length > 0) { courseId,
const nextUnitId = sequence.unitIds[sequence.activeUnitIndex]; sequenceStatus,
// This is a replace because we don't want this change saved in the browser's history. sequenceMightBeUnit,
navigate(`/course/${courseId}/${sequence.id}/${nextUnitId}`, { replace: true }); sequenceId,
section,
routeUnitId,
navigate,
isPreview,
) => {
if (courseStatus === 'loaded' && sequenceStatus === 'failed' && !section && !routeUnitId) {
if (sequenceMightBeUnit) {
// If the sequence failed to load as a sequence, but it is marked as a possible unit, then
// we need to look up the correct parent sequence for it, and redirect there.
const unitId = sequenceId; // just for clarity during the rest of this method
getSequenceForUnitDeprecated(courseId, unitId).then(
parentId => {
if (parentId) {
const baseUrl = `/course/${courseId}/${parentId}`;
const sequenceUrl = isPreview ? `/preview${baseUrl}` : baseUrl;
navigate(`${sequenceUrl}/${unitId}`, { replace: true });
} else {
navigate(`/course/${courseId}`, { replace: true });
}
},
() => { // error case
navigate(`/course/${courseId}`, { replace: true });
},
);
} else {
// Invalid sequence that isn't a unit either. Redirect up to main course.
navigate(`/course/${courseId}`, { replace: true });
} }
} }
}); });
// Look at where this is called in componentDidUpdate for more info about its usage // Look at where this is called in componentDidUpdate for more info about its usage
const checkSequenceUnitMarkerToSequenceUnitRedirect = memoize( export const checkSequenceToSequenceUnitRedirect = memoize(
(courseId, sequenceStatus, sequence, unitId, navigate) => { (courseId, sequenceStatus, sequence, unitId, navigate, isPreview) => {
if (sequenceStatus === 'loaded' && sequence.id && !unitId) {
if (sequence.unitIds !== undefined && sequence.unitIds.length > 0) {
const baseUrl = `/course/${courseId}/${sequence.id}`;
const sequenceUrl = isPreview ? `/preview${baseUrl}` : baseUrl;
const nextUnitId = sequence.unitIds[sequence.activeUnitIndex];
// This is a replace because we don't want this change saved in the browser's history.
navigate(`${sequenceUrl}/${nextUnitId}`, { replace: true });
}
}
},
);
// Look at where this is called in componentDidUpdate for more info about its usage
export const checkSequenceUnitMarkerToSequenceUnitRedirect = memoize(
(courseId, sequenceStatus, sequence, unitId, navigate, isPreview) => {
if (sequenceStatus !== 'loaded' || !sequence.id) { if (sequenceStatus !== 'loaded' || !sequence.id) {
return; return;
} }
const baseUrl = `/course/${courseId}/${sequence.id}`;
const hasUnits = sequence.unitIds?.length > 0; const hasUnits = sequence.unitIds?.length > 0;
if (unitId === 'first') { if (hasUnits) {
if (hasUnits) { const sequenceUrl = isPreview ? `/preview${baseUrl}` : baseUrl;
if (unitId === 'first') {
const firstUnitId = sequence.unitIds[0]; const firstUnitId = sequence.unitIds[0];
navigate(`/course/${courseId}/${sequence.id}/${firstUnitId}`, { replace: true }); navigate(`${sequenceUrl}/${firstUnitId}`, { replace: true });
} else { } else if (unitId === 'last') {
// No units... go to general sequence page
navigate(`/course/${courseId}/${sequence.id}`, { replace: true });
}
} else if (unitId === 'last') {
if (hasUnits) {
const lastUnitId = sequence.unitIds[sequence.unitIds.length - 1]; const lastUnitId = sequence.unitIds[sequence.unitIds.length - 1];
navigate(`/course/${courseId}/${sequence.id}/${lastUnitId}`, { replace: true }); navigate(`${sequenceUrl}/${lastUnitId}`, { replace: true });
} else {
// No units... go to general sequence page
navigate(`/course/${courseId}/${sequence.id}`, { replace: true });
} }
} else {
// No units... go to general sequence page
navigate(baseUrl, { replace: true });
} }
}, },
); );
@@ -141,7 +168,7 @@ class CoursewareContainer extends Component {
checkFetchSequence = memoize((sequenceId) => { checkFetchSequence = memoize((sequenceId) => {
if (sequenceId) { if (sequenceId) {
this.props.fetchSequence(sequenceId); this.props.fetchSequence(sequenceId, this.props.isPreview);
} }
}); });
@@ -169,6 +196,7 @@ class CoursewareContainer extends Component {
routeSequenceId, routeSequenceId,
routeUnitId, routeUnitId,
navigate, navigate,
isPreview,
} = this.props; } = this.props;
// Load data whenever the course or sequence ID changes. // Load data whenever the course or sequence ID changes.
@@ -197,7 +225,7 @@ class CoursewareContainer extends Component {
// Check resume redirect: // Check resume redirect:
// /course/:courseId -> /course/:courseId/:sequenceId/:unitId // /course/:courseId -> /course/:courseId/:sequenceId/:unitId
// based on sequence/unit where user was last active. // based on sequence/unit where user was last active.
checkResumeRedirect(courseStatus, courseId, sequenceId, firstSequenceId, navigate); checkResumeRedirect(courseStatus, courseId, sequenceId, firstSequenceId, navigate, isPreview);
// Check section-unit to unit redirect: // Check section-unit to unit redirect:
// /course/:courseId/:sectionId/:unitId -> /course/:courseId/:unitId // /course/:courseId/:sectionId/:unitId -> /course/:courseId/:unitId
@@ -210,33 +238,69 @@ class CoursewareContainer extends Component {
// otherwise, we could get stuck in a redirect loop, since a sequence that failed to load // otherwise, we could get stuck in a redirect loop, since a sequence that failed to load
// would endlessly redirect to itself through `checkSectionUnitToUnitRedirect` // would endlessly redirect to itself through `checkSectionUnitToUnitRedirect`
// and `checkUnitToSequenceUnitRedirect`. // and `checkUnitToSequenceUnitRedirect`.
checkSectionUnitToUnitRedirect(courseStatus, courseId, sequenceStatus, sectionViaSequenceId, routeUnitId, navigate); checkSectionUnitToUnitRedirect(
courseStatus,
courseId,
sequenceStatus,
sectionViaSequenceId,
routeUnitId,
navigate,
isPreview,
);
// Check section to sequence redirect: // Check section to sequence redirect:
// /course/:courseId/:sectionId -> /course/:courseId/:sequenceId // /course/:courseId/:sectionId -> /course/:courseId/:sequenceId
// by redirecting to the first sequence within the section. // by redirecting to the first sequence within the section.
checkSectionToSequenceRedirect(courseStatus, courseId, sequenceStatus, sectionViaSequenceId, routeUnitId, navigate); checkSectionToSequenceRedirect(
courseStatus,
courseId,
sequenceStatus,
sectionViaSequenceId,
routeUnitId,
navigate,
);
// Check unit to sequence-unit redirect: // Check unit to sequence-unit redirect:
// /course/:courseId/:unitId -> /course/:courseId/:sequenceId/:unitId // /course/:courseId/:unitId -> /course/:courseId/:sequenceId/:unitId
// by filling in the ID of the parent sequence of :unitId. // by filling in the ID of the parent sequence of :unitId.
checkUnitToSequenceUnitRedirect(( checkUnitToSequenceUnitRedirect(
courseStatus, courseId, sequenceStatus, sequenceMightBeUnit, courseStatus,
sequenceId, sectionViaSequenceId, routeUnitId, navigate courseId,
)); sequenceStatus,
sequenceMightBeUnit,
sequenceId,
sectionViaSequenceId,
routeUnitId,
navigate,
isPreview,
);
// Check sequence to sequence-unit redirect: // Check sequence to sequence-unit redirect:
// /course/:courseId/:sequenceId -> /course/:courseId/:sequenceId/:unitId // /course/:courseId/:sequenceId -> /course/:courseId/:sequenceId/:unitId
// by filling in the ID the most-recently-active unit in the sequence, OR // by filling in the ID the most-recently-active unit in the sequence, OR
// the ID of the first unit the sequence if none is active. // the ID of the first unit the sequence if none is active.
checkSequenceToSequenceUnitRedirect(courseId, sequenceStatus, sequence, routeUnitId, navigate); checkSequenceToSequenceUnitRedirect(
courseId,
sequenceStatus,
sequence,
routeUnitId,
navigate,
isPreview,
);
// Check sequence-unit marker to sequence-unit redirect: // Check sequence-unit marker to sequence-unit redirect:
// /course/:courseId/:sequenceId/first -> /course/:courseId/:sequenceId/:unitId // /course/:courseId/:sequenceId/first -> /course/:courseId/:sequenceId/:unitId
// /course/:courseId/:sequenceId/last -> /course/:courseId/:sequenceId/:unitId // /course/:courseId/:sequenceId/last -> /course/:courseId/:sequenceId/:unitId
// by filling in the ID the first or last unit in the sequence. // by filling in the ID the first or last unit in the sequence.
// "Sequence unit marker" is an invented term used only in this component. // "Sequence unit marker" is an invented term used only in this component.
checkSequenceUnitMarkerToSequenceUnitRedirect(courseId, sequenceStatus, sequence, routeUnitId, navigate); checkSequenceUnitMarkerToSequenceUnitRedirect(
courseId,
sequenceStatus,
sequence,
routeUnitId,
navigate,
isPreview,
);
} }
handleUnitNavigationClick = () => { handleUnitNavigationClick = () => {
@@ -334,6 +398,7 @@ CoursewareContainer.propTypes = {
fetchCourse: PropTypes.func.isRequired, fetchCourse: PropTypes.func.isRequired,
fetchSequence: PropTypes.func.isRequired, fetchSequence: PropTypes.func.isRequired,
navigate: PropTypes.func.isRequired, navigate: PropTypes.func.isRequired,
isPreview: PropTypes.bool.isRequired,
}; };
CoursewareContainer.defaultProps = { CoursewareContainer.defaultProps = {

View File

@@ -16,11 +16,19 @@ import tabMessages from '../tab-page/messages';
import { initializeMockApp, waitFor } from '../setupTest'; import { initializeMockApp, waitFor } from '../setupTest';
import { DECODE_ROUTES } from '../constants'; import { DECODE_ROUTES } from '../constants';
import CoursewareContainer from './CoursewareContainer'; import CoursewareContainer, {
checkResumeRedirect,
checkSectionToSequenceRedirect,
checkSectionUnitToUnitRedirect,
checkSequenceToSequenceUnitRedirect,
checkSequenceUnitMarkerToSequenceUnitRedirect,
checkUnitToSequenceUnitRedirect,
} from './CoursewareContainer';
import { buildSimpleCourseBlocks, buildBinaryCourseBlocks } from '../shared/data/__factories__/courseBlocks.factory'; import { buildSimpleCourseBlocks, buildBinaryCourseBlocks } from '../shared/data/__factories__/courseBlocks.factory';
import initializeStore from '../store'; import initializeStore from '../store';
import { appendBrowserTimezoneToUrl } from '../utils'; import { appendBrowserTimezoneToUrl } from '../utils';
import { buildOutlineFromBlocks } from './data/__factories__/learningSequencesOutline.factory'; import { buildOutlineFromBlocks } from './data/__factories__/learningSequencesOutline.factory';
import { getSequenceForUnitDeprecatedUrl } from './data/api';
// NOTE: Because the unit creates an iframe, we choose to mock it out as its rendering isn't // NOTE: Because the unit creates an iframe, we choose to mock it out as its rendering isn't
// pertinent to this test. Instead, we render a simple div that displays the properties we expect // pertinent to this test. Instead, we render a simple div that displays the properties we expect
@@ -525,3 +533,838 @@ describe('CoursewareContainer', () => {
}); });
}); });
}); });
describe('Course redirect functions', () => {
let navigate;
let axiosMock;
beforeEach(() => {
navigate = jest.fn();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
});
describe('isPreview equals true', () => {
describe('checkSequenceUnitMarkerToSequenceUnitRedirect', () => {
it('return when sequence is not loaded', () => {
checkSequenceUnitMarkerToSequenceUnitRedirect(
'courseId',
'loading',
{ id: 'sequence_1', unitIds: ['unit_1'] },
'first',
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('return when sequence id is null', () => {
checkSequenceUnitMarkerToSequenceUnitRedirect(
'courseId',
'loaded',
{ id: null, unitIds: ['unit_1'] },
'first',
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('calls navigate with first unit id', () => {
checkSequenceUnitMarkerToSequenceUnitRedirect(
'courseId',
'loaded',
{ id: 'sequence_1', unitIds: ['unit_1', 'unit_2'] },
'first',
navigate,
true,
);
const expectedUrl = '/preview/course/courseId/sequence_1/unit_1';
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
it('calls navigate with last unit id', () => {
checkSequenceUnitMarkerToSequenceUnitRedirect(
'courseId',
'loaded',
{ id: 'sequence_1', unitIds: ['unit_1', 'unit_2'] },
'last',
navigate,
true,
);
const expectedUrl = '/preview/course/courseId/sequence_1/unit_2';
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
describe('checkSequenceToSequenceUnitRedirect', () => {
it('calls navigate with next unit id', () => {
checkSequenceToSequenceUnitRedirect(
'courseId',
'loaded',
{ id: 'sequence_1', unitIds: ['unit_1', 'unit_2'], activeUnitIndex: 0 },
null,
navigate,
true,
);
const expectedUrl = '/preview/course/courseId/sequence_1/unit_1';
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
it('returns when sequence status is loading', () => {
checkSequenceToSequenceUnitRedirect(
'courseId',
'loading',
{ id: 'sequence_1', unitIds: ['unit_1', 'unit_2'], activeUnitIndex: 0 },
null,
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when sequence id is null', () => {
checkSequenceToSequenceUnitRedirect(
'courseId',
'loading',
{ unitIds: ['unit_1', 'unit_2'], activeUnitIndex: 0 },
null,
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when unit id is defined', () => {
checkSequenceToSequenceUnitRedirect(
'courseId',
'loaded',
{ id: 'sequence_1', unitIds: ['unit_1', 'unit_2'], activeUnitIndex: 0 },
'unit_2',
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when unit ids are undefiend', () => {
checkSequenceToSequenceUnitRedirect(
'courseId',
'loaded',
{ id: 'sequence_1', activeUnitIndex: 0 },
null,
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
});
describe('checkUnitToSequenceUnitRedirect', () => {
const { href: apiUrl } = getSequenceForUnitDeprecatedUrl('courseId');
it('calls navigate with parentId and sequenceId', () => {
const getSequenceForUnitDeprecated = jest.fn();
axiosMock.onGet(apiUrl).reply(200, {
blocks: [{
id: 'sequence_1',
type: 'sequential',
children: ['unit_1'],
}],
});
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'failed',
true,
'unit_1',
false,
null,
navigate,
true,
);
const expectedUrl = '/course/courseId/sequence_1';
waitFor(() => {
expect(getSequenceForUnitDeprecated).toHaveBeenCalled();
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
it('calls navigate to course page when getSequenceForUnitDeprecated errors', () => {
const getSequenceForUnitDeprecated = jest.fn();
axiosMock.onGet(apiUrl).reply(404);
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'failed',
true,
'unit_1',
false,
null,
navigate,
true,
);
const expectedUrl = '/course/courseId';
waitFor(() => {
expect(getSequenceForUnitDeprecated).toHaveBeenCalled();
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
it('calls navigate to course page when no parent id is returned', () => {
const getSequenceForUnitDeprecated = jest.fn();
axiosMock.onGet(apiUrl).reply(200, {
blocks: [{
id: 'sequence_1',
type: 'sequential',
children: ['block_1'],
}],
});
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'failed',
true,
'unit_1',
false,
null,
navigate,
true,
);
const expectedUrl = '/course/courseId';
waitFor(() => {
expect(getSequenceForUnitDeprecated).toHaveBeenCalled();
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
it('calls navigate to course page when sequnce is not unit', () => {
const getSequenceForUnitDeprecated = jest.fn();
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'failed',
false,
'unit_1',
false,
null,
navigate,
true,
);
const expectedUrl = '/course/courseId';
waitFor(() => {
expect(getSequenceForUnitDeprecated).not.toHaveBeenCalled();
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
it('returns when course status is loading', () => {
checkUnitToSequenceUnitRedirect(
'loading',
'courseId',
'failed',
true,
'unit_1',
false,
null,
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when sequence status is not failed', () => {
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'loaded',
true,
'unit_1',
false,
null,
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when section is defined', () => {
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'loaded',
true,
'unit_1',
true,
null,
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when routeUnitId is defined', () => {
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'loaded',
true,
'unit_1',
false,
'unit_1',
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
});
describe('checkSectionUnitToUnitRedirect', () => {
it('calls navigate with unitId', () => {
checkSectionUnitToUnitRedirect(
'loaded',
'courseId',
'failed',
true,
'unit_2',
navigate,
true,
);
const expectedUrl = '/preview/course/courseId/unit_2';
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
it('returns when course status is loading', () => {
checkSectionUnitToUnitRedirect(
'loading',
'courseId',
'loaded',
true,
'unit_2',
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when sequence status is loading', () => {
checkSectionUnitToUnitRedirect(
'loaded',
'courseId',
'loaded',
true,
'unit_2',
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when section is null', () => {
checkSectionUnitToUnitRedirect(
'loaded',
'courseId',
'loaded',
null,
'unit_2',
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when unitId is null', () => {
checkSectionUnitToUnitRedirect(
'loaded',
'courseId',
'loaded',
true,
null,
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
});
describe('checkResumeRedirect', () => {
it('calls navigate with unitId', () => {
axiosMock.onGet(
`${getConfig().LMS_BASE_URL}/api/courseware/resume/courseId`,
).reply(200, {
section_id: 'section_1',
unitId: 'unit_1',
});
checkResumeRedirect(
'loaded',
'courseId',
null,
'sequence_1',
navigate,
true,
);
const expectedUrl = '/preview/course/courseId/section_1/unit_1';
waitFor(() => {
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
it('calls navigate with firstSequenceId', () => {
axiosMock.onGet(
`${getConfig().LMS_BASE_URL}/api/courseware/resume/courseId`,
).reply(200, {
section_id: 'section_1',
first_sequence_id: 'sequence_1',
});
checkResumeRedirect(
'loaded',
'courseId',
null,
'sequence_1',
navigate,
true,
);
const expectedUrl = '/course/courseId/sequence_1';
waitFor(() => {
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
it('returns after calling getResumeBlock', () => {
const getResumeBlock = jest.fn();
axiosMock.onGet(
`${getConfig().LMS_BASE_URL}/api/courseware/resume/courseId`,
).reply(200, {
course_id: 'courseId',
});
checkResumeRedirect(
'loaded',
'courseId',
null,
'sequence_1',
navigate,
true,
);
waitFor(() => {
expect(getResumeBlock).toHaveBeenCalled();
expect(navigate).not.toHaveBeenCalled();
});
});
it('returns when course status is loading', () => {
checkResumeRedirect(
'loading',
'courseId',
null,
'sequence_1',
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when sequenceId is defined', () => {
checkResumeRedirect(
'loaded',
'courseId',
'sequence_3',
'sequence_1',
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when getResumeBlock throws error', () => {
const getResumeBlock = jest.fn();
axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/courseware/resume/courseId`).reply(404);
checkResumeRedirect(
'loaded',
'courseId',
null,
'sequence_1',
navigate,
true,
);
waitFor(() => {
expect(getResumeBlock).toHaveBeenCalled();
expect(navigate).not.toHaveBeenCalled();
});
});
});
});
describe('isPreview equals false', () => {
describe('checkSectionToSequenceRedirect', () => {
it('calls navigate with section based sequence id', () => {
checkSectionToSequenceRedirect(
'loaded',
'courseId',
'failed',
{ sequenceIds: ['sequence_1'] },
null,
navigate,
);
const expectedUrl = '/course/courseId/sequence_1';
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
it('calls navigate with course id only', () => {
checkSectionToSequenceRedirect(
'loaded',
'courseId',
'failed',
{ sequenceIds: [] },
null,
navigate,
);
const expectedUrl = '/course/courseId';
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
it('returns when course status is loading', () => {
checkSectionToSequenceRedirect(
'loading',
'courseId',
'failed',
{ sequenceIds: [] },
null,
navigate,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when sequence status is not failed', () => {
checkSectionToSequenceRedirect(
'loaded',
'courseId',
'loading',
{ sequenceIds: [] },
null,
navigate,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when section is not defined', () => {
checkSectionToSequenceRedirect(
'loaded',
'courseId',
'failed',
null,
null,
navigate,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when unitId is defined', () => {
checkSectionToSequenceRedirect(
'loaded',
'courseId',
'failed',
null,
'unit_1',
navigate,
);
expect(navigate).not.toHaveBeenCalled();
});
});
describe('checkResumeRedirect', () => {
it('calls navigate with unitId', () => {
axiosMock.onGet(
`${getConfig().LMS_BASE_URL}/api/courseware/resume/courseId`,
).reply(200, {
section_id: 'section_1',
unitId: 'unit_1',
});
checkResumeRedirect(
'loaded',
'courseId',
null,
'sequence_1',
navigate,
false,
);
const expectedUrl = '/preview/course/courseId/section_1/unit_1';
waitFor(() => {
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
it('calls navigate with firstSequenceId', () => {
axiosMock.onGet(
`${getConfig().LMS_BASE_URL}/api/courseware/resume/courseId`,
).reply(200, {
section_id: 'section_1',
first_sequence_id: 'sequence_1',
});
checkResumeRedirect(
'loaded',
'courseId',
null,
'sequence_1',
navigate,
false,
);
const expectedUrl = '/course/courseId/sequence_1';
waitFor(() => {
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
});
describe('checkSequenceUnitMarkerToSequenceUnitRedirect', () => {
it('calls navigate with first unit id', () => {
checkSequenceUnitMarkerToSequenceUnitRedirect(
'courseId',
'loaded',
{ id: 'sequence_1', unitIds: ['unit_1', 'unit_2'] },
'first',
navigate,
false,
);
const expectedUrl = '/course/courseId/sequence_1/unit_1';
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
it('calls navigate with base url when no unit id', () => {
checkSequenceUnitMarkerToSequenceUnitRedirect(
'courseId',
'loaded',
{ id: 'sequence_1', unitIds: [] },
'first',
navigate,
true,
);
const expectedUrl = '/course/courseId/sequence_1';
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
it('calls navigate with last unit id', () => {
checkSequenceUnitMarkerToSequenceUnitRedirect(
'courseId',
'loaded',
{ id: 'sequence_1', unitIds: ['unit_1', 'unit_2'] },
'last',
navigate,
false,
);
const expectedUrl = '/course/courseId/sequence_1/unit_2';
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
describe('checkSequenceToSequenceUnitRedirect', () => {
it('calls navigate with next unit id', () => {
checkSequenceToSequenceUnitRedirect(
'courseId',
'loaded',
{ id: 'sequence_1', unitIds: ['unit_1', 'unit_2'], activeUnitIndex: 0 },
null,
navigate,
false,
);
const expectedUrl = '/course/courseId/sequence_1/unit_1';
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
describe('checkUnitToSequenceUnitRedirect', () => {
const apiUrl = getSequenceForUnitDeprecatedUrl('courseId');
it('calls navigate with parentId and sequenceId', () => {
axiosMock.onGet(apiUrl).reply(200, {
parent: { id: 'sequence_1' },
});
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'failed',
true,
'unit_1',
false,
null,
navigate,
true,
);
const expectedUrl = '/course/courseId/sequence_1';
waitFor(() => {
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
it('calls navigate to course page when getSequenceForUnitDeprecated errors', () => {
const getSequenceForUnitDeprecated = jest.fn();
axiosMock.onGet(apiUrl).reply(404);
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'failed',
true,
'unit_1',
false,
null,
navigate,
true,
);
const expectedUrl = '/course/courseId';
waitFor(() => {
expect(getSequenceForUnitDeprecated).toHaveBeenCalled();
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
it('calls navigate to course page when no parent id is returned', () => {
const getSequenceForUnitDeprecated = jest.fn();
axiosMock.onGet(apiUrl).reply(200, {
parent: { children: ['block_1'] },
});
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'failed',
true,
'unit_1',
false,
null,
navigate,
true,
);
const expectedUrl = '/course/courseId';
waitFor(() => {
expect(getSequenceForUnitDeprecated).toHaveBeenCalled();
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
it('returns when course status is loading', () => {
checkUnitToSequenceUnitRedirect(
'loading',
'courseId',
'failed',
true,
'unit_1',
false,
null,
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when sequence status is not failed', () => {
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'loaded',
true,
'unit_1',
false,
null,
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when section is defined', () => {
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'loaded',
true,
'unit_1',
true,
null,
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
it('returns when routeUnitId is defined', () => {
checkUnitToSequenceUnitRedirect(
'loaded',
'courseId',
'loaded',
true,
'unit_1',
false,
'unit_1',
navigate,
true,
);
expect(navigate).not.toHaveBeenCalled();
});
});
describe('checkSectionUnitToUnitRedirect', () => {
it('calls navigate with unitId', () => {
checkSectionUnitToUnitRedirect(
'loaded',
'courseId',
'failed',
true,
'unit_2',
navigate,
false,
);
const expectedUrl = '/course/courseId/unit_2';
expect(navigate).toHaveBeenCalledWith(expectedUrl, { replace: true });
});
});
});
});

View File

@@ -3,21 +3,21 @@ import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { getConfig } from '@edx/frontend-platform'; import { getConfig } from '@edx/frontend-platform';
import { useLocation, useNavigate } from 'react-router-dom';
import { breakpoints, useWindowSize } from '@openedx/paragon'; import { breakpoints, useWindowSize } from '@openedx/paragon';
import { AlertList } from '@src/generic/user-messages'; import { AlertList } from '@src/generic/user-messages';
import { useModel } from '@src/generic/model-store'; import { useModel } from '@src/generic/model-store';
import { getCoursewareOutlineSidebarSettings } from '../data/selectors'; import { getCoursewareOutlineSidebarSettings } from '../data/selectors';
import { Trigger as CourseOutlineTrigger } from './sidebar/sidebars/course-outline';
import Chat from './chat/Chat'; import Chat from './chat/Chat';
import SidebarProvider from './sidebar/SidebarContextProvider'; import SidebarProvider from './sidebar/SidebarContextProvider';
import SidebarTriggers from './sidebar/SidebarTriggers';
import NewSidebarProvider from './new-sidebar/SidebarContextProvider'; import NewSidebarProvider from './new-sidebar/SidebarContextProvider';
import NewSidebarTriggers from './new-sidebar/SidebarTriggers'; import { NotificationsDiscussionsSidebarTriggerSlot } from '../../plugin-slots/NotificationsDiscussionsSidebarTriggerSlot';
import { CelebrationModal, shouldCelebrateOnSectionLoad, WeeklyGoalCelebrationModal } from './celebration'; import { CelebrationModal, shouldCelebrateOnSectionLoad, WeeklyGoalCelebrationModal } from './celebration';
import CourseBreadcrumbs from './CourseBreadcrumbs';
import ContentTools from './content-tools'; import ContentTools from './content-tools';
import Sequence from './sequence'; import Sequence from './sequence';
import { CourseOutlineMobileSidebarTriggerSlot } from '../../plugin-slots/CourseOutlineMobileSidebarTriggerSlot';
import { CourseBreadcrumbsSlot } from '../../plugin-slots/CourseBreadcrumbsSlot';
const Course = ({ const Course = ({
courseId, courseId,
@@ -33,11 +33,19 @@ const Course = ({
celebrations, celebrations,
isStaff, isStaff,
isNewDiscussionSidebarViewEnabled, isNewDiscussionSidebarViewEnabled,
originalUserIsStaff,
} = useModel('courseHomeMeta', courseId); } = useModel('courseHomeMeta', courseId);
const sequence = useModel('sequences', sequenceId); const sequence = useModel('sequences', sequenceId);
const section = useModel('sections', sequence ? sequence.sectionId : null); const section = useModel('sections', sequence ? sequence.sectionId : null);
const { enableNavigationSidebar } = useSelector(getCoursewareOutlineSidebarSettings); const { enableNavigationSidebar } = useSelector(getCoursewareOutlineSidebarSettings);
const navigationDisabled = enableNavigationSidebar || (sequence?.navigationDisabled ?? false); const navigationDisabled = enableNavigationSidebar || (sequence?.navigationDisabled ?? false);
const navigate = useNavigate();
const { pathname } = useLocation();
if (!originalUserIsStaff && pathname.startsWith('/preview')) {
const courseUrl = pathname.replace('/preview', '');
navigate(courseUrl, { replace: true });
}
const pageTitleBreadCrumbs = [ const pageTitleBreadCrumbs = [
sequence, sequence,
@@ -78,7 +86,7 @@ const Course = ({
<div className="position-relative d-flex align-items-xl-center mb-4 mt-1 flex-column flex-xl-row"> <div className="position-relative d-flex align-items-xl-center mb-4 mt-1 flex-column flex-xl-row">
{navigationDisabled || ( {navigationDisabled || (
<> <>
<CourseBreadcrumbs <CourseBreadcrumbsSlot
courseId={courseId} courseId={courseId}
sectionId={section ? section.id : null} sectionId={section ? section.id : null}
sequenceId={sequenceId} sequenceId={sequenceId}
@@ -100,8 +108,8 @@ const Course = ({
</> </>
)} )}
<div className="w-100 d-flex align-items-center"> <div className="w-100 d-flex align-items-center">
<CourseOutlineTrigger isMobileView /> <CourseOutlineMobileSidebarTriggerSlot />
{isNewDiscussionSidebarViewEnabled ? <NewSidebarTriggers /> : <SidebarTriggers /> } <NotificationsDiscussionsSidebarTriggerSlot courseId={courseId} />
</div> </div>
</div> </div>

View File

@@ -1,10 +1,9 @@
import React, { useCallback } from 'react'; import { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { StatefulButton } from '@openedx/paragon'; import { Icon, StatefulButton } from '@openedx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n'; import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import BookmarkOutlineIcon from './BookmarkOutlineIcon'; import { Bookmark, BookmarkBorder } from '@openedx/paragon/icons';
import BookmarkFilledIcon from './BookmarkFilledIcon';
import { removeBookmark, addBookmark } from './data/thunks'; import { removeBookmark, addBookmark } from './data/thunks';
const addBookmarkLabel = ( const addBookmarkLabel = (
@@ -42,10 +41,11 @@ const BookmarkButton = ({
return ( return (
<StatefulButton <StatefulButton
variant="link" variant="link"
className="px-1 ml-n1 btn-sm text-primary-500" className={`px-1 ml-n1 btn-sm text-primary-500 ${isProcessing && 'disabled'}`}
onClick={toggleBookmark} onClick={toggleBookmark}
state={state} state={state}
disabledStates={['defaultProcessing', 'bookmarkedProcessing']} aria-busy={isProcessing}
disabled={isProcessing}
labels={{ labels={{
default: addBookmarkLabel, default: addBookmarkLabel,
defaultProcessing: addBookmarkLabel, defaultProcessing: addBookmarkLabel,
@@ -53,10 +53,10 @@ const BookmarkButton = ({
bookmarkedProcessing: hasBookmarkLabel, bookmarkedProcessing: hasBookmarkLabel,
}} }}
icons={{ icons={{
default: <BookmarkOutlineIcon className="text-primary" />, default: <Icon src={BookmarkBorder} className="text-primary" />,
defaultProcessing: <BookmarkOutlineIcon className="text-primary" />, defaultProcessing: <Icon src={BookmarkBorder} className="text-primary" />,
bookmarked: <BookmarkFilledIcon className="text-primary" />, bookmarked: <Icon src={Bookmark} className="text-primary" />,
bookmarkedProcessing: <BookmarkFilledIcon className="text-primary" />, bookmarkedProcessing: <Icon src={Bookmark} className="text-primary" />,
}} }}
/> />
); );

View File

@@ -1,7 +0,0 @@
import React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBookmark } from '@fortawesome/free-solid-svg-icons';
const BookmarkFilledIcon = (props) => <FontAwesomeIcon icon={faBookmark} {...props} />;
export default BookmarkFilledIcon;

View File

@@ -1,7 +0,0 @@
import React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBookmark } from '@fortawesome/free-regular-svg-icons';
const BookmarkOutlineIcon = (props) => <FontAwesomeIcon icon={faBookmark} {...props} />;
export default BookmarkOutlineIcon;

View File

@@ -1,3 +1 @@
export { default as BookmarkButton } from './BookmarkButton'; export { default as BookmarkButton } from './BookmarkButton';
export { default as BookmarkFilledIcon } from './BookmarkFilledIcon';
export { default as BookmarkOutlineIcon } from './BookmarkFilledIcon';

View File

@@ -3,9 +3,10 @@ import { useSelector } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Xpert } from '@edx/frontend-lib-learning-assistant'; import { Xpert } from '@edx/frontend-lib-learning-assistant';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl } from '@edx/frontend-platform/i18n'; import { injectIntl } from '@edx/frontend-platform/i18n';
import { VERIFIED_MODES } from '@src/constants'; import { ALLOW_UPSELL_MODES, VERIFIED_MODES } from '@src/constants';
import { useModel } from '../../../generic/model-store'; import { useModel } from '../../../generic/model-store';
const Chat = ({ const Chat = ({
@@ -21,43 +22,48 @@ const Chat = ({
} = useSelector(state => state.specialExams); } = useSelector(state => state.specialExams);
const course = useModel('coursewareMeta', courseId); const course = useModel('coursewareMeta', courseId);
const hasVerifiedEnrollment = ( // If is disabled or taking an exam, we don't show the chat.
enrollmentMode !== null if (!enabled || activeAttempt?.attempt_id || exam?.id) { return null; }
&& enrollmentMode !== undefined
&& VERIFIED_MODES.includes(enrollmentMode) // If is not staff and doesn't have an enrollment, we don't show the chat.
if (!isStaff && !enrollmentMode) { return null; }
const verifiedMode = VERIFIED_MODES.includes(enrollmentMode); // Enrollment verified
const auditMode = (
!isStaff
&& !verifiedMode
&& ALLOW_UPSELL_MODES.includes(enrollmentMode) // Can upgrade course
&& getConfig().ENABLE_XPERT_AUDIT
); );
// If user has no access, we don't show the chat.
if (!isStaff && !(verifiedMode || auditMode)) { return null; }
const validDates = () => { // Date validation
const date = new Date(); const {
const utcDate = date.toISOString(); accessExpiration,
start,
end,
} = course;
const startDate = course.start || utcDate; const utcDate = (new Date()).toISOString();
const endDate = course.end || utcDate; const expiration = accessExpiration?.expirationDate || utcDate;
const validDate = (
return ( (start ? start <= utcDate : true)
startDate <= utcDate && (end ? end >= utcDate : true)
&& utcDate <= endDate && (auditMode ? expiration >= utcDate : true)
);
};
const shouldDisplayChat = (
enabled
&& (hasVerifiedEnrollment || isStaff) // display only to verified learners or staff
&& validDates()
// it is necessary to check both whether the user is in an exam, and whether or not they are viewing an exam
// this will prevent the learner from interacting with the tool at any point of the exam flow, even at the
// entrance interstitial.
&& !(activeAttempt?.attempt_id || exam?.id)
); );
// If date is invalid, we don't show the chat.
if (!validDate) { return null; }
return ( // Use a portal to ensure that component overlay does not compete with learning MFE styles.
<> return createPortal(
{/* Use a portal to ensure that component overlay does not compete with learning MFE styles. */} <Xpert
{shouldDisplayChat && (createPortal( courseId={courseId}
<Xpert courseId={courseId} contentToolsEnabled={contentToolsEnabled} unitId={unitId} />, contentToolsEnabled={contentToolsEnabled}
document.body, unitId={unitId}
))} isUpgradeEligible={auditMode}
</> />,
document.body,
); );
}; };

View File

@@ -2,6 +2,8 @@ import { BrowserRouter } from 'react-router-dom';
import React from 'react'; import React from 'react';
import { Factory } from 'rosie'; import { Factory } from 'rosie';
import { getConfig } from '@edx/frontend-platform';
import { import {
initializeMockApp, initializeMockApp,
initializeTestStore, initializeTestStore,
@@ -28,6 +30,10 @@ jest.mock('@edx/frontend-lib-learning-assistant', () => {
}; };
}); });
jest.mock('@edx/frontend-platform', () => ({
getConfig: jest.fn().mockReturnValue({ ENABLE_XPERT_AUDIT: false }),
}));
initializeMockApp(); initializeMockApp();
const courseId = 'course-v1:edX+DemoX+Demo_Course'; const courseId = 'course-v1:edX+DemoX+Demo_Course';
@@ -225,4 +231,56 @@ describe('Chat', () => {
const chat = screen.queryByTestId(mockXpertTestId); const chat = screen.queryByTestId(mockXpertTestId);
expect(chat).toBeInTheDocument(); expect(chat).toBeInTheDocument();
}); });
it('displays component for audit learner if explicitly enabled', async () => {
getConfig.mockImplementation(() => ({ ENABLE_XPERT_AUDIT: true }));
store = await initializeTestStore({
courseMetadata: Factory.build('courseMetadata', {
access_expiration: { expiration_date: '' },
}),
});
render(
<BrowserRouter>
<Chat
enrollmentMode="audit"
isStaff={false}
enabled
courseId={courseId}
contentToolsEnabled={false}
/>
</BrowserRouter>,
{ store },
);
const chat = screen.queryByTestId(mockXpertTestId);
expect(chat).toBeInTheDocument();
});
it('does not display component for audit learner if access deadline has passed', async () => {
getConfig.mockImplementation(() => ({ ENABLE_XPERT_AUDIT: true }));
store = await initializeTestStore({
courseMetadata: Factory.build('courseMetadata', {
access_expiration: { expiration_date: '2014-02-03T05:00:00Z' },
}),
});
render(
<BrowserRouter>
<Chat
enrollmentMode="audit"
isStaff={false}
enabled
courseId={courseId}
contentToolsEnabled={false}
/>
</BrowserRouter>,
{ store },
);
const chat = screen.queryByTestId(mockXpertTestId);
expect(chat).not.toBeInTheDocument();
});
}); });

View File

@@ -32,7 +32,7 @@ import UpgradeFootnote from './UpgradeFootnote';
import SocialIcons from '../../social-share/SocialIcons'; import SocialIcons from '../../social-share/SocialIcons';
import { logClick, logVisit } from './utils'; import { logClick, logVisit } from './utils';
import { DashboardLink, IdVerificationSupportLink, ProfileLink } from '../../../shared/links'; import { DashboardLink, IdVerificationSupportLink, ProfileLink } from '../../../shared/links';
import CourseRecommendations from './CourseRecommendations'; import CourseRecommendationsSlot from '../../../plugin-slots/CourseRecommendationsSlot';
const LINKEDIN_BLUE = '#2867B2'; const LINKEDIN_BLUE = '#2867B2';
@@ -357,7 +357,7 @@ const CourseCelebration = ({ intl }) => {
/> />
))} ))}
{footnote} {footnote}
<CourseRecommendations variant={visitEvent} /> <CourseRecommendationsSlot variant={visitEvent} />
</div> </div>
</div> </div>
</> </>

View File

@@ -134,6 +134,9 @@ const CourseLicense = ({
</span> </span>
))} ))}
{intl.formatMessage(messages['learn.course.license.creativeCommons.text'])} {intl.formatMessage(messages['learn.course.license.creativeCommons.text'])}
<span className="sr-only">
{intl.formatMessage(messages['learn.course.license.creativeCommons.externalSite.screenreaderOnly.message'])}
</span>
</a> </a>
); );

View File

@@ -42,6 +42,11 @@ const messages = defineMessages({
defaultMessage: 'Some Rights Reserved', defaultMessage: 'Some Rights Reserved',
description: 'License text shown when using all Creative Commons license types.', description: 'License text shown when using all Creative Commons license types.',
}, },
'learn.course.license.creativeCommons.externalSite.screenreaderOnly.message': {
id: 'learn.course.license.creativeCommons.externalSite.screenreaderOnly.message',
defaultMessage: 'external site, in a new tab',
description: 'Screenreader only text for external site link',
},
}); });
export default messages; export default messages;

View File

@@ -25,8 +25,9 @@ const SidebarProvider: React.FC<Props> = ({
}) => { }) => {
const { verifiedMode } = useModel('courseHomeMeta', courseId); const { verifiedMode } = useModel('courseHomeMeta', courseId);
const topic = useModel('discussionTopics', unitId); const topic = useModel('discussionTopics', unitId);
const shouldDisplayFullScreen = useWindowSize().width < breakpoints.large.minWidth; const windowWidth = useWindowSize().width ?? window.innerWidth;
const shouldDisplaySidebarOpen = useWindowSize().width > breakpoints.medium.minWidth; const shouldDisplayFullScreen = windowWidth < breakpoints.large.minWidth;
const shouldDisplaySidebarOpen = windowWidth > breakpoints.medium.minWidth;
const query = new URLSearchParams(window.location.search); const query = new URLSearchParams(window.location.search);
const isInitiallySidebarOpen = shouldDisplaySidebarOpen || query.get('sidebar') === 'true'; const isInitiallySidebarOpen = shouldDisplaySidebarOpen || query.get('sidebar') === 'true';
const sidebarKey = `sidebar.${courseId}`; const sidebarKey = `sidebar.${courseId}`;

View File

@@ -36,6 +36,8 @@ const DiscussionsNotificationsTrigger = ({ onClick }) => {
[tabs], [tabs],
); );
const sidebarIcon = currentSidebar === ID ? RightSidebarFilled : RightSidebarOutlined;
useEffect(() => { useEffect(() => {
if (baseUrl && edxProvider) { if (baseUrl && edxProvider) {
dispatch(getCourseDiscussionTopics(courseId)); dispatch(getCourseDiscussionTopics(courseId));
@@ -81,7 +83,7 @@ const DiscussionsNotificationsTrigger = ({ onClick }) => {
return ( return (
<IconButton <IconButton
src={currentSidebar ? RightSidebarFilled : RightSidebarOutlined} src={sidebarIcon}
iconAs={Icon} iconAs={Icon}
onClick={handleClick} onClick={handleClick}
alt={intl.formatMessage(messages.openSidebarTrigger)} alt={intl.formatMessage(messages.openSidebarTrigger)}

View File

@@ -9,7 +9,6 @@ import {
import { useIntl } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import SequenceExamWrapper from '@edx/frontend-lib-special-exams'; import SequenceExamWrapper from '@edx/frontend-lib-special-exams';
import { useToggle } from '@openedx/paragon';
import PageLoading from '@src/generic/PageLoading'; import PageLoading from '@src/generic/PageLoading';
import { useModel } from '@src/generic/model-store'; import { useModel } from '@src/generic/model-store';
@@ -18,16 +17,13 @@ import SequenceContainerSlot from '../../../plugin-slots/SequenceContainerSlot';
import { getCoursewareOutlineSidebarSettings } from '../../data/selectors'; import { getCoursewareOutlineSidebarSettings } from '../../data/selectors';
import CourseLicense from '../course-license'; import CourseLicense from '../course-license';
import Sidebar from '../sidebar/Sidebar'; import { NotificationsDiscussionsSidebarSlot } from '../../../plugin-slots/NotificationsDiscussionsSidebarSlot';
import NewSidebar from '../new-sidebar/Sidebar';
import {
Trigger as CourseOutlineTrigger,
Sidebar as CourseOutlineTray,
} from '../sidebar/sidebars/course-outline';
import messages from './messages'; import messages from './messages';
import HiddenAfterDue from './hidden-after-due'; import HiddenAfterDue from './hidden-after-due';
import { SequenceNavigation, UnitNavigation } from './sequence-navigation'; import { SequenceNavigation, UnitNavigation } from './sequence-navigation';
import SequenceContent from './SequenceContent'; import SequenceContent from './SequenceContent';
import { CourseOutlineSidebarSlot } from '../../../plugin-slots/CourseOutlineSidebarSlot';
import { CourseOutlineSidebarTriggerSlot } from '../../../plugin-slots/CourseOutlineSidebarTriggerSlot';
const Sequence = ({ const Sequence = ({
unitId, unitId,
@@ -38,7 +34,6 @@ const Sequence = ({
previousSequenceHandler, previousSequenceHandler,
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const [isOpen, open, close] = useToggle();
const { const {
canAccessProctoredExams, canAccessProctoredExams,
license, license,
@@ -46,9 +41,9 @@ const Sequence = ({
const { const {
isStaff, isStaff,
originalUserIsStaff, originalUserIsStaff,
isNewDiscussionSidebarViewEnabled,
} = useModel('courseHomeMeta', courseId); } = useModel('courseHomeMeta', courseId);
const sequence = useModel('sequences', sequenceId); const sequence = useModel('sequences', sequenceId);
const section = useModel('sections', sequence ? sequence.sectionId : null);
const unit = useModel('units', unitId); const unit = useModel('units', unitId);
const sequenceStatus = useSelector(state => state.courseware.sequenceStatus); const sequenceStatus = useSelector(state => state.courseware.sequenceStatus);
const sequenceMightBeUnit = useSelector(state => state.courseware.sequenceMightBeUnit); const sequenceMightBeUnit = useSelector(state => state.courseware.sequenceMightBeUnit);
@@ -149,6 +144,7 @@ const Sequence = ({
const renderUnitNavigation = (isAtTop) => ( const renderUnitNavigation = (isAtTop) => (
<UnitNavigation <UnitNavigation
courseId={courseId}
sequenceId={sequenceId} sequenceId={sequenceId}
unitId={unitId} unitId={unitId}
isAtTop={isAtTop} isAtTop={isAtTop}
@@ -166,8 +162,13 @@ const Sequence = ({
const defaultContent = ( const defaultContent = (
<> <>
<div className="sequence-container d-inline-flex flex-row w-100"> <div className="sequence-container d-inline-flex flex-row w-100">
<CourseOutlineTrigger /> <CourseOutlineSidebarTriggerSlot
<CourseOutlineTray /> sectionId={section ? section.id : null}
sequenceId={sequenceId}
isStaff={isStaff}
unitId={unitId}
/>
<CourseOutlineSidebarSlot />
<div className="sequence w-100"> <div className="sequence w-100">
{!isEnabledOutlineSidebar && ( {!isEnabledOutlineSidebar && (
<div className="sequence-navigation-container"> <div className="sequence-navigation-container">
@@ -189,9 +190,6 @@ const Sequence = ({
{...{ {...{
nextSequenceHandler, nextSequenceHandler,
handleNavigate, handleNavigate,
isOpen,
open,
close,
}} }}
/> />
</div> </div>
@@ -204,11 +202,14 @@ const Sequence = ({
sequenceId={sequenceId} sequenceId={sequenceId}
unitId={unitId} unitId={unitId}
unitLoadedHandler={handleUnitLoaded} unitLoadedHandler={handleUnitLoaded}
isOriginalUserStaff={originalUserIsStaff}
isEnabledOutlineSidebar={isEnabledOutlineSidebar}
renderUnitNavigation={renderUnitNavigation}
/> />
{unitHasLoaded && renderUnitNavigation(false)} {unitHasLoaded && renderUnitNavigation(false)}
</div> </div>
</div> </div>
{isNewDiscussionSidebarViewEnabled ? <NewSidebar /> : <Sidebar />} <NotificationsDiscussionsSidebarSlot courseId={courseId} />
</div> </div>
<SequenceContainerSlot courseId={courseId} unitId={unitId} /> <SequenceContainerSlot courseId={courseId} unitId={unitId} />
</> </>
@@ -224,7 +225,6 @@ const Sequence = ({
originalUserIsStaff={originalUserIsStaff} originalUserIsStaff={originalUserIsStaff}
canAccessProctoredExams={canAccessProctoredExams} canAccessProctoredExams={canAccessProctoredExams}
> >
{isEnabledOutlineSidebar && renderUnitNavigation(true)}
{defaultContent} {defaultContent}
</SequenceExamWrapper> </SequenceExamWrapper>
<CourseLicense license={license || undefined} /> <CourseLicense license={license || undefined} />

View File

@@ -1,6 +1,6 @@
import React, { Suspense, useEffect } from 'react'; import React, { Suspense, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import PageLoading from '../../../generic/PageLoading'; import PageLoading from '../../../generic/PageLoading';
import { useModel } from '../../../generic/model-store'; import { useModel } from '../../../generic/model-store';
@@ -11,12 +11,15 @@ const ContentLock = React.lazy(() => import('./content-lock'));
const SequenceContent = ({ const SequenceContent = ({
gated, gated,
intl,
courseId, courseId,
sequenceId, sequenceId,
unitId, unitId,
unitLoadedHandler, unitLoadedHandler,
isOriginalUserStaff,
isEnabledOutlineSidebar,
renderUnitNavigation,
}) => { }) => {
const intl = useIntl();
const sequence = useModel('sequences', sequenceId); const sequence = useModel('sequences', sequenceId);
// Go back to the top of the page whenever the unit or sequence changes. // Go back to the top of the page whenever the unit or sequence changes.
@@ -59,6 +62,9 @@ const SequenceContent = ({
key={unitId} key={unitId}
id={unitId} id={unitId}
onLoaded={unitLoadedHandler} onLoaded={unitLoadedHandler}
isOriginalUserStaff={isOriginalUserStaff}
isEnabledOutlineSidebar={isEnabledOutlineSidebar}
renderUnitNavigation={renderUnitNavigation}
/> />
); );
}; };
@@ -69,11 +75,13 @@ SequenceContent.propTypes = {
sequenceId: PropTypes.string.isRequired, sequenceId: PropTypes.string.isRequired,
unitId: PropTypes.string, unitId: PropTypes.string,
unitLoadedHandler: PropTypes.func.isRequired, unitLoadedHandler: PropTypes.func.isRequired,
intl: intlShape.isRequired, isOriginalUserStaff: PropTypes.bool.isRequired,
isEnabledOutlineSidebar: PropTypes.bool.isRequired,
renderUnitNavigation: PropTypes.func.isRequired,
}; };
SequenceContent.defaultProps = { SequenceContent.defaultProps = {
unitId: null, unitId: null,
}; };
export default injectIntl(SequenceContent); export default SequenceContent;

View File

@@ -20,7 +20,7 @@ import * as hooks from './hooks';
* Changes to it should be vetted by them (security@edx.org). * Changes to it should be vetted by them (security@edx.org).
*/ */
export const IFRAME_FEATURE_POLICY = ( export const IFRAME_FEATURE_POLICY = (
'microphone *; camera *; midi *; geolocation *; encrypted-media *, clipboard-write *' 'microphone *; camera *; midi *; geolocation *; encrypted-media *; clipboard-write *; autoplay *'
); );
export const testIDs = StrictDict({ export const testIDs = StrictDict({

View File

@@ -1,61 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Unit component output BookmarkButton props bookmarked, bookmark update pending snapshot 1`] = `
<BookmarkButton
isBookmarked={true}
isProcessing={false}
unitId="unit-id"
/>
`;
exports[`Unit component output BookmarkButton props not bookmarked, bookmark update loading snapshot 1`] = `
<BookmarkButton
isBookmarked={false}
isProcessing={true}
unitId="unit-id"
/>
`;
exports[`Unit component output snapshot: not bookmarked, do not show content 1`] = `
<div
className="unit"
>
<div
className="mb-0"
>
<h3
className="h3"
>
unit-title
</h3>
<UnitTitleSlot
courseId="test-course-id"
unitId="test-props-id"
unitTitle="unit-title"
/>
</div>
<h2
className="sr-only"
>
Level 2 headings may be created by course providers in the future.
</h2>
<BookmarkButton
isBookmarked={false}
isProcessing={false}
unitId="unit-id"
/>
<UnitSuspense
courseId="test-course-id"
id="test-props-id"
/>
<ContentIFrame
courseId="test-course-id"
elementId="unit-iframe"
id="test-props-id"
loadingMessage="Loading learning sequence..."
onLoaded={[MockFunction props.onLoaded]}
shouldShowContent={true}
title="unit-title"
/>
</div>
`;

View File

@@ -18,9 +18,10 @@ const useExamAccess = ({
const fetchExamAccessToken = useFetchExamAccessToken(); const fetchExamAccessToken = useFetchExamAccessToken();
// NOTE: We cannot use this hook in the useEffect hook below to grab the updated exam access token in the finally // NOTE: We cannot use this hook in the useEffect hook below to grab the updated exam access token in the finally
// block, due to the rules of hooks. Instead, we get the value of the exam access token from a call to the hook. // block, due to the rules of hooks. Instead, we get the value of the exam access token from a call to
// the hook below.
// When the fetchExamAccessToken call completes, the useExamAccess hook will re-run // When the fetchExamAccessToken call completes, the useExamAccess hook will re-run
// (due to a change to the Redux store, and, thus, a change to the the context), at which point the updated // (due to a change to the Redux store, and, thus, a change to the context), at which point the updated
// exam access token will be fetched via the useExamAccessToken hook call below. // exam access token will be fetched via the useExamAccessToken hook call below.
// The important detail is that there should never be a return value (false, ''). // The important detail is that there should never be a return value (false, '').
const examAccessToken = useExamAccessToken(); const examAccessToken = useExamAccessToken();
@@ -35,7 +36,7 @@ const useExamAccess = ({
logError(error); logError(error);
}); });
} }
}, [id]); }, [id, isExam]);
return { return {
blockAccess, blockAccess,

View File

@@ -1,5 +1,6 @@
import { useState } from 'react';
import { logError } from '@edx/frontend-platform/logging'; import { logError } from '@edx/frontend-platform/logging';
import { renderHook } from '@testing-library/react-hooks'; import { act, renderHook } from '@testing-library/react-hooks';
import { useExamAccessToken, useFetchExamAccessToken, useIsExam } from '@edx/frontend-lib-special-exams'; import { useExamAccessToken, useFetchExamAccessToken, useIsExam } from '@edx/frontend-lib-special-exams';
import { initializeMockApp } from '../../../../../setupTest'; import { initializeMockApp } from '../../../../../setupTest';
@@ -16,10 +17,27 @@ jest.mock('@edx/frontend-lib-special-exams', () => ({
const id = 'test-id'; const id = 'test-id';
// This object allows us to manipulate the value of the accessToken.
const testAccessToken = { curr: '' };
const mockFetchExamAccessToken = jest.fn().mockImplementation(() => Promise.resolve()); const mockFetchExamAccessToken = jest.fn().mockImplementation(() => Promise.resolve());
useFetchExamAccessToken.mockReturnValue(mockFetchExamAccessToken); useFetchExamAccessToken.mockReturnValue(mockFetchExamAccessToken);
const testAccessToken = 'test-access-token'; const mockUseIsExam = (initialState = false) => {
const [isExam, setIsExam] = useState(initialState);
// This setTimeout block is intended to replicate the case where a unit is an exam, but
// the call to fetch exam metadata has not yet completed. That is the value of isExam starts
// as false and transitions to true once the call resolves.
if (!initialState) {
setTimeout(
() => setIsExam(true),
500,
);
}
return isExam;
};
describe('useExamAccess hook', () => { describe('useExamAccess hook', () => {
beforeAll(async () => { beforeAll(async () => {
@@ -28,13 +46,13 @@ describe('useExamAccess hook', () => {
}); });
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
jest.useFakeTimers();
// Mock implementations from previous test runs may not have been "consumed", so reset mock implementations.
useExamAccessToken.mockReset(); useExamAccessToken.mockReset();
useExamAccessToken.mockReturnValueOnce('');
useExamAccessToken.mockReturnValueOnce(testAccessToken); useIsExam.mockImplementation(() => mockUseIsExam());
useExamAccessToken.mockImplementation(() => testAccessToken.curr);
}); });
describe.only('behavior', () => { describe('behavior', () => {
it('returns accessToken and blockAccess and doesn\'t call token API if not an exam', () => { it('returns accessToken and blockAccess and doesn\'t call token API if not an exam', () => {
const { result } = renderHook(() => useExamAccess({ id })); const { result } = renderHook(() => useExamAccess({ id }));
const { accessToken, blockAccess } = result.current; const { accessToken, blockAccess } = result.current;
@@ -43,18 +61,23 @@ describe('useExamAccess hook', () => {
expect(blockAccess).toBe(false); expect(blockAccess).toBe(false);
expect(mockFetchExamAccessToken).not.toHaveBeenCalled(); expect(mockFetchExamAccessToken).not.toHaveBeenCalled();
}); });
it('returns true for blockAccess if an exam but accessToken not yet fetched', () => { it('returns true for blockAccess if an exam but accessToken not yet fetched', async () => {
useIsExam.mockImplementation(() => (true)); useIsExam.mockImplementation(() => mockUseIsExam(true));
const { result } = renderHook(() => useExamAccess({ id })); const { result, waitForNextUpdate } = renderHook(() => useExamAccess({ id }));
const { accessToken, blockAccess } = result.current; const { accessToken, blockAccess } = result.current;
expect(accessToken).toEqual(''); expect(accessToken).toEqual('');
expect(blockAccess).toBe(true); expect(blockAccess).toBe(true);
expect(mockFetchExamAccessToken).toHaveBeenCalled(); expect(mockFetchExamAccessToken).toHaveBeenCalled();
// This is to get rid of the act(...) warning.
await act(async () => {
await waitForNextUpdate();
});
}); });
it('returns false for blockAccess if an exam and accessToken fetch succeeds', async () => { it('returns false for blockAccess if an exam and accessToken fetch succeeds', async () => {
useIsExam.mockImplementation(() => (true)); useIsExam.mockImplementation(() => mockUseIsExam(true));
const { result, waitForNextUpdate } = renderHook(() => useExamAccess({ id })); const { result, waitForNextUpdate } = renderHook(() => useExamAccess({ id }));
// We wait for the promise to resolve and for updates to state to complete so that blockAccess is updated. // We wait for the promise to resolve and for updates to state to complete so that blockAccess is updated.
@@ -62,12 +85,36 @@ describe('useExamAccess hook', () => {
const { accessToken, blockAccess } = result.current; const { accessToken, blockAccess } = result.current;
expect(accessToken).toEqual(testAccessToken); expect(accessToken).toEqual(testAccessToken.curr);
expect(blockAccess).toBe(false);
expect(mockFetchExamAccessToken).toHaveBeenCalled();
});
it('in progress', async () => {
const { result, waitForNextUpdate } = renderHook(() => useExamAccess({ id }));
let { accessToken, blockAccess } = result.current;
expect(accessToken).toEqual('');
expect(blockAccess).toBe(false);
expect(mockFetchExamAccessToken).not.toHaveBeenCalled();
testAccessToken.curr = 'test-access-token';
// The runAllTimers will update the value of isExam, and the waitForNextUpdate will
// wait for call to setBlockAccess in the finally clause of useEffect hook.
await act(async () => {
jest.runAllTimers();
await waitForNextUpdate();
});
({ accessToken, blockAccess } = result.current);
expect(accessToken).toEqual('test-access-token');
expect(blockAccess).toBe(false); expect(blockAccess).toBe(false);
expect(mockFetchExamAccessToken).toHaveBeenCalled(); expect(mockFetchExamAccessToken).toHaveBeenCalled();
}); });
it('returns false for blockAccess if an exam and accessToken fetch fails', async () => { it('returns false for blockAccess if an exam and accessToken fetch fails', async () => {
useIsExam.mockImplementation(() => (true)); useIsExam.mockImplementation(() => mockUseIsExam(true));
const testError = 'test-error'; const testError = 'test-error';
mockFetchExamAccessToken.mockImplementationOnce(() => Promise.reject(testError)); mockFetchExamAccessToken.mockImplementationOnce(() => Promise.reject(testError));
@@ -79,7 +126,7 @@ describe('useExamAccess hook', () => {
const { accessToken, blockAccess } = result.current; const { accessToken, blockAccess } = result.current;
expect(accessToken).toEqual(testAccessToken); expect(accessToken).toEqual(testAccessToken.curr);
expect(blockAccess).toBe(false); expect(blockAccess).toBe(false);
expect(mockFetchExamAccessToken).toHaveBeenCalled(); expect(mockFetchExamAccessToken).toHaveBeenCalled();
expect(logError).toHaveBeenCalledWith(testError); expect(logError).toHaveBeenCalledWith(testError);

View File

@@ -1,6 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams, useLocation } from 'react-router-dom';
import { AppContext } from '@edx/frontend-platform/react'; import { AppContext } from '@edx/frontend-platform/react';
import { useIntl } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
@@ -8,7 +8,6 @@ import { useIntl } from '@edx/frontend-platform/i18n';
import { useModel } from '@src/generic/model-store'; import { useModel } from '@src/generic/model-store';
import { usePluginsCallback } from '@src/generic/plugin-store'; import { usePluginsCallback } from '@src/generic/plugin-store';
import BookmarkButton from '../../bookmark/BookmarkButton';
import messages from '../messages'; import messages from '../messages';
import ContentIFrame from './ContentIFrame'; import ContentIFrame from './ContentIFrame';
import UnitSuspense from './UnitSuspense'; import UnitSuspense from './UnitSuspense';
@@ -22,15 +21,19 @@ const Unit = ({
format, format,
onLoaded, onLoaded,
id, id,
isOriginalUserStaff,
isEnabledOutlineSidebar,
renderUnitNavigation,
}) => { }) => {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const { pathname } = useLocation();
const { authenticatedUser } = React.useContext(AppContext); const { authenticatedUser } = React.useContext(AppContext);
const examAccess = useExamAccess({ id }); const examAccess = useExamAccess({ id });
const shouldDisplayHonorCode = useShouldDisplayHonorCode({ courseId, id }); const shouldDisplayHonorCode = useShouldDisplayHonorCode({ courseId, id });
const unit = useModel(modelKeys.units, id); const unit = useModel(modelKeys.units, id);
const isProcessing = unit.bookmarkedUpdateState === 'loading';
const view = authenticatedUser ? views.student : views.public; const view = authenticatedUser ? views.student : views.public;
const shouldDisplayUnitPreview = pathname.startsWith('/preview') && isOriginalUserStaff;
const getUrl = usePluginsCallback('getIFrameUrl', () => getIFrameUrl({ const getUrl = usePluginsCallback('getIFrameUrl', () => getIFrameUrl({
id, id,
@@ -38,22 +41,14 @@ const Unit = ({
format, format,
examAccess, examAccess,
jumpToId: searchParams.get('jumpToId'), jumpToId: searchParams.get('jumpToId'),
preview: shouldDisplayUnitPreview ? '1' : '0',
})); }));
const iframeUrl = getUrl(); const iframeUrl = getUrl();
return ( return (
<div className="unit"> <div className="unit">
<div className="mb-0"> <UnitTitleSlot unitId={id} {...{ unit, isEnabledOutlineSidebar, renderUnitNavigation }} />
<h3 className="h3">{unit.title}</h3>
<UnitTitleSlot courseId={courseId} unitId={id} unitTitle={unit.title} />
</div>
<h2 className="sr-only">{formatMessage(messages.headerPlaceholder)}</h2>
<BookmarkButton
unitId={unit.id}
isBookmarked={unit.bookmarked}
isProcessing={isProcessing}
/>
<UnitSuspense {...{ courseId, id }} /> <UnitSuspense {...{ courseId, id }} />
<ContentIFrame <ContentIFrame
elementId="unit-iframe" elementId="unit-iframe"
@@ -74,6 +69,9 @@ Unit.propTypes = {
format: PropTypes.string, format: PropTypes.string,
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
onLoaded: PropTypes.func, onLoaded: PropTypes.func,
isOriginalUserStaff: PropTypes.bool.isRequired,
isEnabledOutlineSidebar: PropTypes.bool.isRequired,
renderUnitNavigation: PropTypes.func.isRequired,
}; };
Unit.defaultProps = { Unit.defaultProps = {

View File

@@ -1,205 +1,122 @@
import React from 'react'; import { MemoryRouter } from 'react-router';
import { when } from 'jest-when'; import { Factory } from 'rosie';
import { formatMessage, shallow } from '@edx/react-unit-test-utils/dist';
import { useSearchParams } from 'react-router-dom';
import { useModel } from '@src/generic/model-store'; import {
initializeMockApp, initializeTestStore, render, screen,
import BookmarkButton from '../../bookmark/BookmarkButton'; } from '../../../../setupTest';
import UnitSuspense from './UnitSuspense';
import ContentIFrame from './ContentIFrame';
import Unit from '.';
import messages from '../messages';
import { getIFrameUrl } from './urls'; import { getIFrameUrl } from './urls';
import { modelKeys, views } from './constants'; import { views } from './constants';
import * as hooks from './hooks'; import Unit from '.';
jest.mock('./hooks', () => ({ useUnitData: jest.fn() })); const defaultProps = {
jest.mock('react-router-dom');
jest.mock('@edx/frontend-platform/i18n', () => {
const utils = jest.requireActual('@edx/react-unit-test-utils/dist');
return {
useIntl: () => ({ formatMessage: utils.formatMessage }),
defineMessages: m => m,
};
});
jest.mock('@src/generic/PageLoading', () => 'PageLoading');
jest.mock('../../bookmark/BookmarkButton', () => 'BookmarkButton');
jest.mock('./ContentIFrame', () => 'ContentIFrame');
jest.mock('./UnitSuspense', () => 'UnitSuspense');
jest.mock('../honor-code', () => 'HonorCode');
jest.mock('../lock-paywall', () => 'LockPaywall');
jest.mock('@src/generic/model-store', () => ({
useModel: jest.fn(),
}));
jest.mock('react', () => ({
...jest.requireActual('react'),
useContext: jest.fn(v => v),
}));
jest.mock('./hooks', () => ({
useExamAccess: jest.fn(),
useShouldDisplayHonorCode: jest.fn(),
}));
jest.mock('./urls', () => ({
getIFrameUrl: jest.fn(),
}));
const props = {
courseId: 'test-course-id', courseId: 'test-course-id',
format: 'test-format', format: 'test-format',
onLoaded: jest.fn().mockName('props.onLoaded'), onLoaded: jest.fn().mockName('props.onLoaded'),
id: 'test-props-id', id: 'unit-id',
isOriginalUserStaff: false,
isEnabledOutlineSidebar: false,
renderUnitNavigation: jest.fn(enabled => enabled && 'UnitNaviagtion'),
}; };
const context = { authenticatedUser: { test: 'user' } };
React.useContext.mockReturnValue(context);
const examAccess = {
accessToken: 'test-token',
blockAccess: false,
};
hooks.useExamAccess.mockReturnValue(examAccess);
hooks.useShouldDisplayHonorCode.mockReturnValue(false);
const unit = { const unit = {
id: 'unit-id', id: 'unit-id',
title: 'unit-title', title: 'unit-title',
bookmarked: false, bookmarked: false,
bookmarkedUpdateState: 'pending', bookmarkedUpdateState: 'pending',
}; };
const mockCoursewareMetaFn = jest.fn(() => ({ wholeCourseTranslationEnabled: false }));
const mockUnitsFn = jest.fn(() => unit);
when(useModel) let store;
.calledWith('courseHomeMeta', props.courseId)
.mockImplementation(mockCoursewareMetaFn)
.calledWith(modelKeys.units, props.id)
.mockImplementation(mockUnitsFn);
let el; const renderComponent = (props) => {
describe('Unit component', () => { render(
const searchParams = { get: (prop) => prop }; <MemoryRouter initialEntries={[{ pathname: `/course/${props.courseID}` }]}>
const setSearchParams = jest.fn(); <Unit {...props} />
</MemoryRouter>,
{ store, wrapWithRouter: false },
);
};
beforeEach(() => { initializeMockApp();
useSearchParams.mockImplementation(() => [searchParams, setSearchParams]);
jest.clearAllMocks(); async function setupStoreState() {
el = shallow(<Unit {...props} />); const courseMetadata = Factory.build('courseMetadata');
const unitBlocks = [Factory.build(
'block',
{ type: 'vertical', ...unit },
{ courseId: courseMetadata.id },
)];
store = await initializeTestStore({ courseMetadata, unitBlocks });
}
describe('<Unit />', () => {
beforeEach(async () => {
await setupStoreState();
}); });
describe('behavior', () => {
it('initializes hooks', () => { describe('unit title', () => {
expect(hooks.useShouldDisplayHonorCode).toHaveBeenCalledWith({ it('has two children', () => {
courseId: props.courseId, renderComponent(defaultProps);
id: props.id, const unitTitleWrapper = screen.getByTestId('unit_title_slot').children[0];
});
expect(unitTitleWrapper.children).toHaveLength(3);
});
it('renders bookmark button', () => {
renderComponent(defaultProps);
expect(screen.getByText('Bookmark this page')).toBeInTheDocument();
});
it('does not render unit navigation buttons', () => {
renderComponent(defaultProps);
const nextButton = screen.queryByText('UnitNaviagtion');
expect(nextButton).toBeNull();
});
it('renders unit navigation buttons when isEnabledOutlineSidebar is true', () => {
const props = { ...defaultProps, isEnabledOutlineSidebar: true };
renderComponent(props);
const nextButton = screen.getByText('UnitNaviagtion');
expect(nextButton).toBeVisible();
}); });
}); });
describe('output', () => {
let component; describe('UnitSuspense', () => {
test('snapshot: not bookmarked, do not show content', () => { it('renders loading message', () => {
el = shallow(<Unit {...props} />); renderComponent(defaultProps);
expect(el.snapshot).toMatchSnapshot();
expect(screen.getByText('Loading', { exact: false })).toBeInTheDocument();
}); });
describe('BookmarkButton props', () => { });
const renderComponent = () => {
el = shallow(<Unit {...props} />); describe('ContentIFrame', () => {
[component] = el.instance.findByType(BookmarkButton); let iframe;
}; beforeEach(() => {
describe('not bookmarked, bookmark update loading', () => { renderComponent(defaultProps);
beforeEach(() => { iframe = screen.getByTestId('content-iframe-test-id');
useModel.mockReturnValueOnce({ ...unit, bookmarkedUpdateState: 'loading' });
renderComponent();
});
test('snapshot', () => {
expect(component.snapshot).toMatchSnapshot();
});
test('props', () => {
expect(component.props.isBookmarked).toEqual(false);
expect(component.props.isProcessing).toEqual(true);
expect(component.props.unitId).toEqual(unit.id);
});
});
describe('bookmarked, bookmark update pending', () => {
beforeEach(() => {
mockUnitsFn.mockReturnValueOnce({ ...unit, bookmarked: true });
renderComponent();
});
test('snapshot', () => {
expect(component.snapshot).toMatchSnapshot();
});
test('props', () => {
expect(component.props.isBookmarked).toEqual(true);
expect(component.props.isProcessing).toEqual(false);
expect(component.props.unitId).toEqual(unit.id);
});
});
}); });
test('UnitSuspense props', () => {
el = shallow(<Unit {...props} />); it('renders content iframe', () => {
[component] = el.instance.findByType(UnitSuspense); expect(iframe).toBeVisible();
expect(component.props.courseId).toEqual(props.courseId);
expect(component.props.id).toEqual(props.id);
}); });
describe('ContentIFrame props', () => {
const testComponentProps = () => { it('generates correct iframeUrl', () => {
expect(component.props.elementId).toEqual('unit-iframe'); expect(iframe.getAttribute('src')).toEqual(getIFrameUrl({
expect(component.props.id).toEqual(props.id); id: defaultProps.id,
expect(component.props.loadingMessage).toEqual(formatMessage(messages.loadingSequence)); view: views.student,
expect(component.props.onLoaded).toEqual(props.onLoaded); format: defaultProps.format,
expect(component.props.title).toEqual(unit.title); examAccess: {
}; accessToken: '',
const loadComponent = () => { blockAccess: false,
el = shallow(<Unit {...props} />); },
[component] = el.instance.findByType(ContentIFrame); jumpToId: null,
}; preview: 0,
describe('shouldShowContent', () => { }));
test('do not show content if displaying honor code', () => {
hooks.useShouldDisplayHonorCode.mockReturnValueOnce(true);
loadComponent();
testComponentProps();
expect(component.props.shouldShowContent).toEqual(false);
});
test('do not show content if examAccess is blocked', () => {
hooks.useExamAccess.mockReturnValueOnce({ ...examAccess, blockAccess: true });
loadComponent();
testComponentProps();
expect(component.props.shouldShowContent).toEqual(false);
});
test('show content if not displaying honor code or blocked by exam access', () => {
loadComponent();
testComponentProps();
expect(component.props.shouldShowContent).toEqual(true);
});
});
describe('iframeUrl', () => {
test('loads iframe url with student view if authenticated user', () => {
loadComponent();
testComponentProps();
expect(component.props.iframeUrl).toEqual(getIFrameUrl({
id: props.id,
view: views.student,
format: props.format,
examAccess,
}));
});
test('loads iframe url with public view if no authenticated user', () => {
React.useContext.mockReturnValueOnce({});
loadComponent();
testComponentProps();
expect(component.props.iframeUrl).toEqual(getIFrameUrl({
id: props.id,
view: views.public,
format: props.format,
examAccess,
}));
});
});
}); });
}); });
}); });

View File

@@ -13,6 +13,7 @@ export const getIFrameUrl = ({
format, format,
examAccess, examAccess,
jumpToId, jumpToId,
preview,
}) => { }) => {
const xblockUrl = `${getConfig().LMS_BASE_URL}/xblock/${id}`; const xblockUrl = `${getConfig().LMS_BASE_URL}/xblock/${id}`;
return stringifyUrl({ return stringifyUrl({
@@ -20,6 +21,7 @@ export const getIFrameUrl = ({
query: { query: {
...iframeParams, ...iframeParams,
view, view,
preview,
...(format && { format }), ...(format && { format }),
...(!examAccess.blockAccess && { exam_access: examAccess.accessToken }), ...(!examAccess.blockAccess && { exam_access: examAccess.accessToken }),
jumpToId, // Pass jumpToId as query param as fragmentIdentifier is not passed to server. jumpToId, // Pass jumpToId as query param as fragmentIdentifier is not passed to server.

View File

@@ -17,6 +17,7 @@ const props = {
view: 'test-view', view: 'test-view',
format: 'test-format', format: 'test-format',
examAccess: { blockAccess: false, accessToken: 'test-access-token' }, examAccess: { blockAccess: false, accessToken: 'test-access-token' },
preview: false,
}; };
describe('urls module getIFrameUrl', () => { describe('urls module getIFrameUrl', () => {
@@ -28,6 +29,7 @@ describe('urls module getIFrameUrl', () => {
view: props.view, view: props.view,
format: props.format, format: props.format,
exam_access: props.examAccess.accessToken, exam_access: props.examAccess.accessToken,
preview: props.preview,
}, },
}); });
expect(getIFrameUrl(props)).toEqual(url); expect(getIFrameUrl(props)).toEqual(url);
@@ -35,11 +37,12 @@ describe('urls module getIFrameUrl', () => {
test('no format provided, exam access blocked', () => { test('no format provided, exam access blocked', () => {
const url = stringifyUrl({ const url = stringifyUrl({
url: `${config.LMS_BASE_URL}/xblock/${props.id}`, url: `${config.LMS_BASE_URL}/xblock/${props.id}`,
query: { ...iframeParams, view: props.view }, query: { ...iframeParams, view: props.view, preview: props.preview },
}); });
expect(getIFrameUrl({ expect(getIFrameUrl({
id: props.id, id: props.id,
view: props.view, view: props.view,
preview: props.preview,
examAccess: { blockAccess: true }, examAccess: { blockAccess: true },
})).toEqual(url); })).toEqual(url);
}); });
@@ -50,6 +53,7 @@ describe('urls module getIFrameUrl', () => {
...iframeParams, ...iframeParams,
view: props.view, view: props.view,
format: props.format, format: props.format,
preview: props.preview,
exam_access: props.examAccess.accessToken, exam_access: props.examAccess.accessToken,
jumpToId: 'some-xblock-id', jumpToId: 'some-xblock-id',
}, },
@@ -60,4 +64,20 @@ describe('urls module getIFrameUrl', () => {
jumpToId: 'some-xblock-id', jumpToId: 'some-xblock-id',
})).toEqual(url); })).toEqual(url);
}); });
test('preview is true and url param equals 1', () => {
const url = stringifyUrl({
url: `${config.LMS_BASE_URL}/xblock/${props.id}`,
query: {
...iframeParams,
view: props.view,
format: props.format,
preview: true,
exam_access: props.examAccess.accessToken,
},
});
expect(getIFrameUrl({
...props,
preview: true,
})).toEqual(url);
});
}); });

View File

@@ -1,16 +1,7 @@
import React from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { breakpoints, Button, useWindowSize } from '@openedx/paragon'; import { breakpoints, useWindowSize } from '@openedx/paragon';
import { ChevronLeft, ChevronRight } from '@openedx/paragon/icons';
import classNames from 'classnames'; import classNames from 'classnames';
import { import { useIntl } from '@edx/frontend-platform/i18n';
injectIntl,
intlShape,
isRtl,
getLocale,
} from '@edx/frontend-platform/i18n';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { LOADED } from '@src/constants'; import { LOADED } from '@src/constants';
@@ -21,21 +12,18 @@ import { useSequenceNavigationMetadata } from './hooks';
import { useModel } from '../../../../generic/model-store'; import { useModel } from '../../../../generic/model-store';
import messages from './messages'; import messages from './messages';
import PreviousButton from './generic/PreviousButton';
import { NextUnitTopNavTriggerSlot } from '../../../../plugin-slots/NextUnitTopNavTriggerSlot';
const SequenceNavigation = ({ const SequenceNavigation = ({
intl,
unitId, unitId,
sequenceId, sequenceId,
className, className,
onNavigate, onNavigate,
nextHandler, nextHandler,
previousHandler, previousHandler,
nextSequenceHandler,
handleNavigate,
isOpen,
open,
close,
}) => { }) => {
const intl = useIntl();
const sequence = useModel('sequences', sequenceId); const sequence = useModel('sequences', sequenceId);
const { const {
isFirstUnit, isFirstUnit,
@@ -76,62 +64,39 @@ const SequenceNavigation = ({
); );
}; };
const renderPreviousButton = () => { const renderPreviousButton = () => navigationDisabledPrevSequence || (
const disabled = isFirstUnit; <PreviousButton
const prevArrow = isRtl(getLocale()) ? ChevronRight : ChevronLeft; variant="link"
return navigationDisabledPrevSequence || ( buttonStyle="previous-btn"
<Button onClick={previousHandler}
variant="link" previousLink={previousLink}
className="previous-btn" isFirstUnit={isFirstUnit}
onClick={previousHandler} buttonLabel={shouldDisplayNotificationTriggerInSequence ? null : intl.formatMessage(messages.previousButton)}
disabled={disabled} />
iconBefore={prevArrow} );
as={disabled ? undefined : Link}
to={disabled ? undefined : previousLink}
>
{shouldDisplayNotificationTriggerInSequence ? null : intl.formatMessage(messages.previousButton)}
</Button>
);
};
const renderNextButton = () => { const renderNextButton = () => {
let buttonText;
const { exitActive, exitText } = GetCourseExitNavigation(courseId, intl); const { exitActive, exitText } = GetCourseExitNavigation(courseId, intl);
const buttonText = (isLastUnit && exitText) ? exitText : intl.formatMessage(messages.nextButton);
const disabled = isLastUnit && !exitActive; const disabled = isLastUnit && !exitActive;
const nextArrow = isRtl(getLocale()) ? ChevronLeft : ChevronRight;
if (isLastUnit && exitText) {
buttonText = exitText;
} else if (!shouldDisplayNotificationTriggerInSequence) {
buttonText = intl.formatMessage(messages.nextButton);
}
return navigationDisabledNextSequence || ( return navigationDisabledNextSequence || (
<PluginSlot <NextUnitTopNavTriggerSlot
id="next_button_slot" {...{
pluginProps={{
courseId,
disabled, disabled,
buttonText, buttonText,
nextArrow,
nextLink, nextLink,
shouldDisplayNotificationTriggerInSequence,
sequenceId, sequenceId,
unitId, onClickHandler: nextHandler,
nextSequenceHandler, variant: 'link',
handleNavigate, buttonStyle: 'next-btn',
isOpen,
open,
close,
linkComponent: Link,
}} }}
> />
<Button
variant="link"
className="next-btn"
onClick={nextHandler}
disabled={disabled}
iconAfter={nextArrow}
as={disabled ? undefined : Link}
to={disabled ? undefined : nextLink}
>
{shouldDisplayNotificationTriggerInSequence ? null : buttonText}
</Button>
</PluginSlot>
); );
}; };
@@ -145,28 +110,17 @@ const SequenceNavigation = ({
}; };
SequenceNavigation.propTypes = { SequenceNavigation.propTypes = {
intl: intlShape.isRequired,
sequenceId: PropTypes.string.isRequired, sequenceId: PropTypes.string.isRequired,
unitId: PropTypes.string, unitId: PropTypes.string,
className: PropTypes.string, className: PropTypes.string,
onNavigate: PropTypes.func.isRequired, onNavigate: PropTypes.func.isRequired,
nextHandler: PropTypes.func.isRequired, nextHandler: PropTypes.func.isRequired,
previousHandler: PropTypes.func.isRequired, previousHandler: PropTypes.func.isRequired,
close: PropTypes.func,
open: PropTypes.func,
isOpen: PropTypes.bool,
handleNavigate: PropTypes.func,
nextSequenceHandler: PropTypes.func,
}; };
SequenceNavigation.defaultProps = { SequenceNavigation.defaultProps = {
className: null, className: null,
unitId: null, unitId: null,
close: null,
open: null,
isOpen: false,
handleNavigate: null,
nextSequenceHandler: null,
}; };
export default injectIntl(SequenceNavigation); export default SequenceNavigation;

View File

@@ -1,13 +1,13 @@
import React, { useCallback } from 'react'; import { useCallback } from 'react';
import { Link } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect, useSelector } from 'react-redux'; import { connect, useSelector } from 'react-redux';
import classNames from 'classnames'; import classNames from 'classnames';
import { Button } from '@openedx/paragon'; import { Button, Icon } from '@openedx/paragon';
import { Bookmark } from '@openedx/paragon/icons';
import UnitIcon from './UnitIcon'; import UnitIcon from './UnitIcon';
import CompleteIcon from './CompleteIcon'; import CompleteIcon from './CompleteIcon';
import BookmarkFilledIcon from '../../bookmark/BookmarkFilledIcon';
const UnitButton = ({ const UnitButton = ({
onClick, onClick,
@@ -22,6 +22,9 @@ const UnitButton = ({
showTitle, showTitle,
}) => { }) => {
const { courseId, sequenceId } = useSelector(state => state.courseware); const { courseId, sequenceId } = useSelector(state => state.courseware);
const { pathname } = useLocation();
const basePath = `/course/${courseId}/${sequenceId}/${unitId}`;
const unitPath = pathname.startsWith('/preview') ? `/preview${basePath}` : basePath;
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
onClick(unitId); onClick(unitId);
@@ -37,13 +40,15 @@ const UnitButton = ({
onClick={handleClick} onClick={handleClick}
title={title} title={title}
as={Link} as={Link}
to={`/course/${courseId}/${sequenceId}/${unitId}`} to={unitPath}
> >
<UnitIcon type={contentType} /> <UnitIcon type={contentType} />
{showTitle && <span className="unit-title">{title}</span>} {showTitle && <span className="unit-title">{title}</span>}
{showCompletion && complete ? <CompleteIcon size="sm" className="text-success ml-2" /> : null} {showCompletion && complete ? <CompleteIcon size="sm" className="text-success ml-2" /> : null}
{bookmarked ? ( {bookmarked ? (
<BookmarkFilledIcon <Icon
data-testid="bookmark-icon"
src={Bookmark}
className="text-primary small position-absolute" className="text-primary small position-absolute"
style={{ top: '-3px', right: '5px' }} style={{ top: '-3px', right: '5px' }}
/> />

View File

@@ -63,17 +63,17 @@ describe('Unit Button', () => {
}); });
it('does not show bookmark', () => { it('does not show bookmark', () => {
const { container } = render(<UnitButton {...mockData} />); const { queryByTestId } = render(<UnitButton {...mockData} />);
container.querySelectorAll('svg').forEach(icon => { expect(queryByTestId('bookmark-icon')).toBeNull();
expect(icon).not.toHaveClass('fa-bookmark');
});
}); });
it('shows bookmark', () => { it('shows bookmark', () => {
const { container } = render(<UnitButton {...mockData} unitId={bookmarkedUnit.id} />, { wrapWithRouter: true }); const { container } = render(<UnitButton {...mockData} unitId={bookmarkedUnit.id} />, { wrapWithRouter: true });
const buttonIcons = container.querySelectorAll('svg'); const buttonIcons = container.querySelectorAll('svg');
expect(buttonIcons).toHaveLength(3); expect(buttonIcons).toHaveLength(3);
expect(buttonIcons[2]).toHaveClass('fa-bookmark');
const bookmarkIcon = buttonIcons[2].closest('span');
expect(bookmarkIcon.getAttribute('data-testid')).toBe('bookmark-icon');
}); });
it('handles the click', () => { it('handles the click', () => {

View File

@@ -1,48 +1,40 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Button } from '@openedx/paragon'; import { useIntl } from '@edx/frontend-platform/i18n';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons';
import {
injectIntl, intlShape, isRtl, getLocale,
} from '@edx/frontend-platform/i18n';
import { useSelector } from 'react-redux';
import { GetCourseExitNavigation } from '../../course-exit'; import { GetCourseExitNavigation } from '../../course-exit';
import UnitNavigationEffortEstimate from './UnitNavigationEffortEstimate';
import { useSequenceNavigationMetadata } from './hooks'; import { useSequenceNavigationMetadata } from './hooks';
import messages from './messages'; import messages from './messages';
import PreviousButton from './generic/PreviousButton';
import NextButton from './generic/NextButton';
import { NextUnitTopNavTriggerSlot } from '../../../../plugin-slots/NextUnitTopNavTriggerSlot';
const UnitNavigation = ({ const UnitNavigation = ({
intl,
sequenceId, sequenceId,
unitId, unitId,
onClickPrevious, onClickPrevious,
onClickNext, onClickNext,
isAtTop, isAtTop,
courseId,
}) => { }) => {
const intl = useIntl();
const { const {
isFirstUnit, isLastUnit, nextLink, previousLink, isFirstUnit, isLastUnit, nextLink, previousLink,
} = useSequenceNavigationMetadata(sequenceId, unitId); } = useSequenceNavigationMetadata(sequenceId, unitId);
const { courseId } = useSelector(state => state.courseware);
const renderPreviousButton = () => { const renderPreviousButton = () => {
const disabled = isFirstUnit; const buttonStyle = `previous-button ${isAtTop ? 'text-dark mr-3' : 'justify-content-center'}`;
const prevArrow = isRtl(getLocale()) ? faChevronRight : faChevronLeft;
return ( return (
<Button <PreviousButton
isFirstUnit={isFirstUnit}
variant="outline-secondary" variant="outline-secondary"
className="previous-button mr-sm-2 d-flex align-items-center justify-content-center" buttonLabel={intl.formatMessage(messages.previousButton)}
disabled={disabled} buttonStyle={buttonStyle}
onClick={onClickPrevious} onClick={onClickPrevious}
as={disabled ? undefined : Link} previousLink={previousLink}
to={disabled ? undefined : previousLink} isAtTop={isAtTop}
> />
<FontAwesomeIcon icon={prevArrow} className="mr-2" size="sm" />
{intl.formatMessage(messages.previousButton)}
</Button>
); );
}; };
@@ -50,26 +42,45 @@ const UnitNavigation = ({
const { exitActive, exitText } = GetCourseExitNavigation(courseId, intl); const { exitActive, exitText } = GetCourseExitNavigation(courseId, intl);
const buttonText = (isLastUnit && exitText) ? exitText : intl.formatMessage(messages.nextButton); const buttonText = (isLastUnit && exitText) ? exitText : intl.formatMessage(messages.nextButton);
const disabled = isLastUnit && !exitActive; const disabled = isLastUnit && !exitActive;
const nextArrow = isRtl(getLocale()) ? faChevronLeft : faChevronRight; const variant = 'outline-primary';
const buttonStyle = `next-button ${isAtTop ? 'text-dark' : 'justify-content-center'}`;
if (isAtTop) {
return (
<NextUnitTopNavTriggerSlot
{...{
variant,
buttonStyle,
buttonText,
disabled,
sequenceId,
nextLink,
onClickHandler: onClickNext,
isAtTop,
}}
/>
);
}
return ( return (
<Button <NextButton
variant="outline-primary" variant={variant}
className="next-button d-flex align-items-center justify-content-center" buttonStyle={buttonStyle}
onClick={onClickNext} onClickHandler={onClickNext}
disabled={disabled} disabled={disabled}
as={disabled ? undefined : Link} buttonText={buttonText}
to={disabled ? undefined : nextLink} nextLink={nextLink}
> hasEffortEstimate
<UnitNavigationEffortEstimate sequenceId={sequenceId} unitId={unitId}> />
{buttonText}
</UnitNavigationEffortEstimate>
<FontAwesomeIcon icon={nextArrow} className="ml-2" size="sm" />
</Button>
); );
}; };
return ( return (
<div className={classNames('unit-navigation d-flex', { 'top-unit-navigation mb-3 w-100': isAtTop })}> <div className={classNames('d-flex', {
'unit-navigation': !isAtTop,
'top-unit-navigation': isAtTop,
})}
>
{renderPreviousButton()} {renderPreviousButton()}
{renderNextButton()} {renderNextButton()}
</div> </div>
@@ -77,7 +88,7 @@ const UnitNavigation = ({
}; };
UnitNavigation.propTypes = { UnitNavigation.propTypes = {
intl: intlShape.isRequired, courseId: PropTypes.string.isRequired,
sequenceId: PropTypes.string.isRequired, sequenceId: PropTypes.string.isRequired,
unitId: PropTypes.string, unitId: PropTypes.string,
onClickPrevious: PropTypes.func.isRequired, onClickPrevious: PropTypes.func.isRequired,
@@ -90,4 +101,4 @@ UnitNavigation.defaultProps = {
isAtTop: false, isAtTop: false,
}; };
export default injectIntl(UnitNavigation); export default UnitNavigation;

View File

@@ -5,6 +5,13 @@ import {
} from '../../../../setupTest'; } from '../../../../setupTest';
import UnitNavigation from './UnitNavigation'; import UnitNavigation from './UnitNavigation';
const mockNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: () => mockNavigate,
}));
describe('Unit Navigation', () => { describe('Unit Navigation', () => {
let mockData; let mockData;
const courseMetadata = Factory.build('courseMetadata'); const courseMetadata = Factory.build('courseMetadata');
@@ -18,6 +25,7 @@ describe('Unit Navigation', () => {
const store = await initializeTestStore({ courseMetadata, unitBlocks }); const store = await initializeTestStore({ courseMetadata, unitBlocks });
const { courseware } = store.getState(); const { courseware } = store.getState();
mockData = { mockData = {
courseId: courseware.courseId,
unitId: unitBlocks[1].id, unitId: unitBlocks[1].id,
sequenceId: courseware.sequenceId, sequenceId: courseware.sequenceId,
onClickPrevious: () => {}, onClickPrevious: () => {},
@@ -55,6 +63,26 @@ describe('Unit Navigation', () => {
expect(onClickNext).toHaveBeenCalledTimes(1); expect(onClickNext).toHaveBeenCalledTimes(1);
}); });
it('when clicked it calls navigate when is at the top', () => {
const onClickPrevious = jest.fn();
const onClickNext = jest.fn();
render(<UnitNavigation
{...mockData}
onClickPrevious={onClickPrevious}
onClickNext={onClickNext}
isAtTop
/>, { wrapWithRouter: true });
fireEvent.click(screen.getByRole('button', { name: /previous/i }));
expect(onClickPrevious).toHaveBeenCalledTimes(1);
expect(mockNavigate).toHaveBeenCalledTimes(1);
fireEvent.click(screen.getByRole('button', { name: /next/i }));
expect(onClickNext).toHaveBeenCalledTimes(1);
expect(mockNavigate).toHaveBeenCalledTimes(2);
});
it('has the navigation buttons enabled for the non-corner unit in the sequence', () => { it('has the navigation buttons enabled for the non-corner unit in the sequence', () => {
render(<UnitNavigation {...mockData} />, { wrapWithRouter: true }); render(<UnitNavigation {...mockData} />, { wrapWithRouter: true });

View File

@@ -0,0 +1,91 @@
import PropTypes from 'prop-types';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import { Button, IconButton, Icon } from '@openedx/paragon';
import {
ArrowBack,
ArrowForward,
ChevronLeft,
ChevronRight,
} from '@openedx/paragon/icons';
import { isRtl, getLocale } from '@edx/frontend-platform/i18n';
import UnitNavigationEffortEstimate from '../UnitNavigationEffortEstimate';
const NextButton = ({
onClickHandler,
buttonText,
nextLink,
variant,
buttonStyle,
disabled,
hasEffortEstimate,
isAtTop,
}) => {
const navigate = useNavigate();
const { pathname } = useLocation();
const navLink = pathname.startsWith('/preview') ? `/preview${nextLink}` : nextLink;
const buttonContent = hasEffortEstimate ? (
<UnitNavigationEffortEstimate>
{buttonText}
</UnitNavigationEffortEstimate>
) : buttonText;
const getNextArrow = () => {
if (isAtTop) {
return isRtl(getLocale()) ? ArrowBack : ArrowForward;
}
return isRtl(getLocale()) ? ChevronLeft : ChevronRight;
};
const nextArrow = getNextArrow();
const onClick = () => {
navigate(navLink);
onClickHandler();
};
if (isAtTop) {
return (
<IconButton
variant="light"
className={buttonStyle}
onClick={onClick}
src={nextArrow}
disabled={disabled}
iconAs={Icon}
alt={buttonText}
/>
);
}
return (
<Button
variant={variant}
className={buttonStyle}
disabled={disabled}
onClick={onClickHandler}
as={disabled ? undefined : Link}
to={disabled ? undefined : navLink}
iconAfter={nextArrow}
>
{buttonContent}
</Button>
);
};
NextButton.defaultProps = {
hasEffortEstimate: false,
};
NextButton.propTypes = {
onClickHandler: PropTypes.func.isRequired,
buttonText: PropTypes.string.isRequired,
nextLink: PropTypes.string.isRequired,
variant: PropTypes.string.isRequired,
buttonStyle: PropTypes.string.isRequired,
disabled: PropTypes.bool.isRequired,
hasEffortEstimate: PropTypes.bool,
isAtTop: PropTypes.bool.isRequired,
};
export default NextButton;

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