Compare commits

...

1300 Commits

Author SHA1 Message Date
Brayan Cerón
6841e94ab0 fix: render proper visibility message on self-paced course type (backport) (#1713) 2025-05-14 10:25:59 -07:00
Rômulo Penido
4f04698a60 perf: add staleTime to queryClient to avoid excessive calls (#1740) 2025-03-13 18:24:02 -07:00
Rômulo Penido
82a76b0cad fix: excessive calls to the clipboard API endpoint [sumac] (#1723) 2025-03-12 14:20:52 -07:00
Jillian
62445a18f3 fix: bugs with ExpandableTextArea toolbars & modals in problem editor (#1646) (#1673)
Backports the bugfixes from https://github.com/openedx/frontend-app-authoring/pull/1646 to Sumac.
2025-02-27 13:17:44 -05:00
Jillian
91ee5004a4 [sumac] fix: allow user provided value if can auto-create orgs [FC-0076] (#1678)
Backports #1582 + #1689 to Sumac.
2025-02-26 17:05:17 -05:00
Brayan Cerón
e0ec87c969 fix: find proper courses when searching (backport) (#1496) (#1497)
When active/archived filters were on or there was selected any order filter, the search skipped these values and it was just returned the courses list without the respective filters. Additionally, when a search keyword was applied and a filter was selected, the keyword stayed stuck and the search list returned were not the appropriate
2024-12-09 14:33:48 -08:00
Chris Chávez
4835f72f2c fix: Update error messages when adding user to library (backport) (#1543) (#1550)
Updates the message error when the user doesn't exist when adding a new team member to a library.
2024-12-09 14:23:09 -08:00
Daniel Valenzuela
3ab329d373 fix: avoid changing url when removing filters (#1530) (#1551)
* Makes the Active Tab Key independent from the URL, except for the initial load, where the active tab is set from the url.
*Avoids unnecessarily changing SearchParams: Due to a limitation of the useSearchParams react hook, which uses a memoized value for the URL that becomes stale after selecting a tab, it unexpectedly changes the URL value. Unfortunately there's no way to completely avoid this, so if there's a usageKey url param, the hook setter function will be called and the URL will revert to the stale memoized url.
2024-12-09 11:20:52 -05:00
Rômulo Penido
7c97ffecb5 fix: show/hide "new library" button based on separate v1/v2 permissions (backport) (#1549) 2024-12-06 12:12:11 -08:00
Chris Chávez
90727590dd fix: Show published OLX in Library Content Picker (backport) (#1534) (#1546) 2024-12-06 11:48:52 -08:00
Rômulo Penido
1c82a67364 fix: editor flicker after creating xblock (#1529) 2024-11-26 14:41:14 -08:00
Navin Karkera
d08ef83659 fix: remove unnecessary toast notification on adding component (#1490) (#1528)
(cherry picked from commit 033acc45f1)
2024-11-22 11:15:01 -08:00
Chris Chávez
13bce7e034 fix: Show published count component in library content picker (#1481) (#1521)
When using the library component picker, show the correct number on component count (published components) in collection cards.
2024-11-21 15:01:48 -08:00
Chris Chávez
54888d03bc fix: TinyMce aux modal issues in text editors (#1500) (#1520)
The following bugs were found with the TinyMCE aux modal (used in emoticons, formulas and embed iframe):

* The TinyMCE aux modal and the Editor modal close when clicking on any content in the aux modal.
* When the user opens the Edit Source Code modal, this adds data-focus-on-hidden to the TinyMce aux modal, making it unusable (not clickable).
* Since they are two separate modals, the focus remains on the editor modal, making it impossible to use scrolling or inputs from the modal aux.

Solution: Move the aux modal inside the editor modal.

One discarded solution: Block the modal editor from closing when interacting with the modal aux. The modal editor still retained focus.
2024-11-19 15:32:47 -08:00
Daniel Valenzuela
e6d9f3a50d fix: simplify Library Home Page (v2) (#1443) (#1495) 2024-11-18 14:46:25 -08:00
Navin Karkera
74b455287e feat: show info banner in component picker (#1498) (#1501)
Displays a infor banner if only published content is visible in component picker.

(cherry picked from commit efd2b3d27d)
2024-11-14 12:00:02 -05:00
Jillian
e2adb45493 fix: show a more detailed error on Bad Request (#1468) (#1478)
Show a detailed error when 400 Bad Request received while adding a component to a library, either a new or pasted component. The most likely error from the backend here is "library can only have {max} components", and since this error is translated already, we can just report it through.

(cherry picked from commit f1bdc6200f)
2024-11-06 22:42:31 -05:00
Rômulo Penido
d4e9a6bec2 fix: add spacing to searchbar and simplify render conditions (#1476)
Adds padding between the search bar and the library list.

Also, the render method was refactored to be a bit simpler.

Backport of #1461
2024-11-06 22:05:57 -05:00
Navin Karkera
e6741496dc fix: add component to collection on paste [FC-0062] (#1450) (#1472)
Link component to collection if pasted in a collection page.
Fixes: https://github.com/openedx/frontend-app-authoring/issues/1435

(cherry picked from commit 549dbaa0fa)
2024-11-06 21:56:44 -05:00
Navin Karkera
9304a83bef chore: hide transcripts in video preview for library (#1459) (#1474)
Fixes: #1453
(cherry picked from commit e118eb5971)
2024-11-06 21:47:05 -05:00
Navin Karkera
3173f41e63 feat: handle unsaved changes in text & problem editors (#1444) (#1471)
The text & problem xblock editors will display a confirmation box before
cancelling only if user has changed something else it will directly go
back.

(cherry picked from commit df8a65dc4e)
2024-11-06 11:26:12 -05:00
Jillian
866dd9bd31 fix: Hide / error on Libraries v2 pages if !librariesV2Enabled (#1449) (#1473)
Show an error message if the user tries to view a v2 Library while Libraries V2 are disabled in the platform.

(cherry picked from commit d7bbd40de1)
2024-11-06 11:25:30 -05:00
Rômulo Penido
f10ad9f525 fix: enable publish button on library after component edit [sumac] [FC-0062] (#1447)
This PR fixes the following bug: After publishing a library then editing a component, the "Publish" button in Library Info doesn't become enabled until you refresh
Fixes: https://github.com/openedx/frontend-app-authoring/issues/1455
Backport: https://github.com/openedx/frontend-app-authoring/pull/1446
2024-11-04 11:56:20 -05:00
Chris Chávez
81d78b9613 fix: Library Preview Expand button covers dropdown (#1438) (#1442) 2024-10-30 09:03:00 -05:00
Rômulo Penido
4886df7d6f [sumac] fix: empty state for library selection on component picker [FC-0062] (#1441)
This PR fixes the empty state text for adding library content if the user can't access any library.
2024-10-28 18:49:58 -05:00
Jillian
62dfb75169 fix: use absolute URL for Export Tags menu item
Use absolute URL for Export Tags menu item so that the menu item works no matter where in the course it's used. Fix this issue: https://github.com/openedx/frontend-app-authoring/issues/1380

(cherry picked from commit 774728a9c0)
2024-10-25 21:50:04 -05:00
Braden MacDonald
3d8d248599 feat: arbitrary asset upload/deletion for Library Components [FC-0062] (#1430)
Allow users to upload and delete assets associated with Content Library
components via the sidebar panel, under the "Advanced Details" section
of the "Details" tab. This is intended as a debug tool and power-user
feature, similar to the OLX editor provided there. It's also serving as
our interim image-upload solution, because it was easier to implement
than the full modal that integrates with TinyMCE.

---------

Co-authored-by: XnpioChV <xnpiochv@gmail.com>
2024-10-24 09:46:27 -04:00
Cristhian Garcia
e1ce3eb484 fix: display image preview in libraries editor (#1403)
Prior to this commit, the TinyMCE editor image preview only worked in
courses, and did not work for content libraries.
2024-10-24 09:39:37 -04:00
Rômulo Penido
a8aa495542 feat: add existing content to a collection [FC-0062] (#1416)
Allows library components to be added to a collection using the add-content
sidebar. For the Libraries Relaunch Beta.

For: https://github.com/openedx/frontend-app-authoring/issues/1173
2024-10-23 11:49:46 -04:00
Navin Karkera
c0c74dec83 feat: show confirmation dialog before discarding library changes [FC-0062] (#1428)
* feat: show confirmation dialog before discarding library changes
2024-10-23 10:47:23 -05:00
Navin Karkera
f67c3ffc4c feat: direct link to single block in library [FC-0062] (#1392)
* feat: direct link to single block in library

Adds support for displaying single xblock in a library when passed a
query param: usageKey. This is required for directing users to a
specific block from course.

* feat: show alert while editing library block from course
2024-10-23 10:25:54 -05:00
Rômulo Penido
11470f256d feat: library component picker now supports multi-select (#1417) 2024-10-22 16:41:49 -07:00
Brian Smith
fe37d119f2 feat(deps): update header to 5.6.0 (#1424) 2024-10-22 19:18:50 -04:00
Braden MacDonald
8c8d9119d4 fix: don't revert to advanced editor if problem contains fields like url_name (#1421) 2024-10-22 18:53:22 +00:00
Chris Chávez
21cbf80f23 feat: Show published components on content picker (#1420)
* feat: Show published components on content picker

---------

Co-authored-by: Braden MacDonald <braden@opencraft.com>
2024-10-22 13:47:07 -05:00
Daniel Valenzuela
966e1c3d91 feat: publish single library component (#1407) 2024-10-22 17:31:17 +00:00
Braden MacDonald
57e7baf59e fix: this repo has been renamed to frontend-app-authoring (#1419) 2024-10-22 10:19:24 -07:00
Navin Karkera
675e02fcbd feat: "add to collection" menu item functionality (#1413) 2024-10-22 09:49:51 -07:00
Braden MacDonald
841aede8cd perf: don't load advanced info details (library components) before they're needed (#1409) 2024-10-21 14:07:28 -07:00
Braden MacDonald
6ae68bd122 feat: Menu option to delete a component + small fixes (#1408)
* feat: menu option to delete a component
* feat: close component sidebar if it's open when that component id deleted
* feat: hide unsupported block types from the "Add Content" menu
* fix: expand and internationalize the "component usage" text
2024-10-21 14:04:45 -07:00
Navin Karkera
d49fc85163 refactor: remove parentLocator and next button from lib component picker (#1412) 2024-10-21 10:05:04 -07:00
Navin Karkera
56e025a4f0 refactor: lib component picker modal to only post message with block info (#1401) 2024-10-19 11:55:25 -07:00
Max Sokolski
a94df2fdf0 fix: set original value for TypeaheadDropdown component 2024-10-18 16:35:53 -03:00
Jillian
cfe19894d1 feat: Let Studio Home REST API determine if libraries v1 and/or v2 are enabled (#1329) 2024-10-18 12:03:26 -07:00
Braden MacDonald
40a6ee9ca5 feat: View for comparing published version of library block to previous (#1393) 2024-10-18 11:27:10 -07:00
Chris Chávez
4facf1cf5d feat: add tags to collections [FC-0062] (#1379)
* feat: Add ContentTagsDrawer to collection

* test: Add test to show ContentTagsDrawer on CollectionInfo
2024-10-16 21:50:17 -05:00
Rômulo Penido
b81f611a0e feat: add library component picker (#1356) 2024-10-16 10:18:12 -07:00
Maria Grimaldi
8a4d1f4810 refactor!: turn on homepage course API V2 consumption by default (#1307)
* refactor!: turn on homepage course API V2 consumption by default

Turn on getting courses from the HomePageCourses API
which allows pagination, filtering and ordering. See https://github.com/openedx/edx-platform/pull/34173
for more details on the API implementation.

* fix: home page initial a-z course sort

---------

Co-authored-by: Diana Olarte <diana.olarte@edunext.co>
2024-10-16 12:29:20 -04:00
David Ormsbee
1bdea093b0 fix: disable static asset mangling for v2 Content Libraries
The static asset substitution used to make images show up properly when
in the TinyMCE editor doesn't work for Content Libraries. Unfortunately,
this will cause the static asset references in XBlock content to get
mangled and saved incorrectly. So until we can handle it correctly,
we're just going to disable it entirely if the LearningContext is a v2
Content Library.

This means that static assets won't display properly in the editor
itself, but it should at least get written/preserved correctly, so that
those assets will show up properly in XBlock previews.
2024-10-16 11:54:19 -04:00
David Ormsbee
a1181f3d49 refactor: switch Content Library XBlock preview to Studio
edx-platform commit 7316111 (PR #35598) moved the XBlock embed view so
that it can be rendered on either LMS or Studio. This commit moves the
frontend to actually call the Studio endpoint. This will make Content
Library static asset display easier, because that view will only be made
available through Studio and not the LMS.
2024-10-16 11:54:19 -04:00
Stanislav
ba8e3d448e fix: Calendar icon over datepicker modal (#1365) 2024-10-16 10:20:27 -04:00
Jillian
1ee3229104 feat: UI to manage users/permissions for the content libraries (#1362) 2024-10-15 17:24:00 +00:00
Navin Karkera
84487602cc feat: manage collections in component sidebar [FC-0062] (#1373)
* feat: add to collection in sidebar

* feat: manage collections

* test: add tests for manage collections

* feat: remove from collection menu option
2024-10-15 10:20:23 -05:00
Navin Karkera
7fb460019e feat: add an allowlist of for supported blocks in library [FC-0062] (#1378)
* feat: show error msg from server on paste

* feat: add an allowlist of for supported blocks in library

Libraries v2 currently don't support editing blocks other than problem,
text and videos. This commit adds a configuration variable called
`LIBRARY_SUPPORTED_BLOCKS` to setup allowed list of block types users
can paste into libraries. By default it is set to support
'problem,text,video,html`.

* feat: enable add button for blocks based on setting


---------

Co-authored-by: Rômulo Penido <romulo@opencraft.com>
2024-10-15 09:52:35 -05:00
Braden MacDonald
66b14a5b16 docs: update the README with an easy way to run the MFE on your host (#1364) 2024-10-10 14:02:42 -07:00
ABBOUD Moncef
3696836de6 feat: save discussion alert dismissal (#1245) 2024-10-09 16:23:12 -04:00
Navin Karkera
434fea3a95 feat: delete collection [FC-0062] (#1333)
* feat: delete collection

* feat: update button status on delete

* test: add tests for collection delete
2024-10-08 16:59:06 +00:00
Braden MacDonald
75f937e11a feat: Libraries v2: Advanced Component Info & OLX Editor (#1346) 2024-10-08 09:41:21 -07:00
Rômulo Penido
85b5730114 fix: change collection details component slots (#1363) 2024-10-07 21:34:48 -05:00
Braden MacDonald
8c125df9aa feat: Open Editors in a Modal (library components only) [FC-0062] (#1357)
* feat: allow opening editors in modals

* refactor: add an EditorContext

* test: update tests accordingly

* test: make testUtils call clearAllMocks() automatically :)
2024-10-07 21:04:49 -05:00
edX requirements bot
83322e2052 chore: update browserslist DB (#1367)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-10-07 10:15:40 -07:00
dependabot[bot]
b6eeec8e60 build(deps): bump actions/checkout from 3 to 4 (#1371) 2024-10-07 10:12:16 -07:00
Jillian
b957f3b4e3 Use block type label instead of Library block_types REST API [FC-0062] (#1361)
* style: avoid using reserved word "type" as variable name

use componentType or blockType instead.

* refactor: let BlockTypeLabel handle displaying the component label

including the child count, if one is provided.

This change removes hooks for the block_types REST API

* test: add tests for BlockTypeLabel

---------

Co-authored-by: Chris Chávez <xnpiochv@gmail.com>
2024-10-04 13:04:23 -05:00
Jillian
9c1fd5a68c fix: Show spinner while loading library components (#1331) 2024-10-03 21:02:32 -07:00
Braden MacDonald
652af9f6a5 refactor: Improve LibraryContext, convert tests to testUtils (#1345) 2024-10-03 19:35:43 -07:00
Jillian
dc6ede4d80 fix: use "other" component type in decide Card header background (#1359)
if no other background color is found
2024-10-03 15:26:12 -07:00
Jesper Hodge
8c6bbb895f fix: update express (#1351)
Just a package update
2024-10-03 15:21:37 -04:00
Braden MacDonald
4d0f92e265 fix: upload codecov report as a separate workflow step (#1355) 2024-10-02 10:49:38 -07:00
Rômulo Penido
0349188c42 feat: allow full width content in library authoring [FC-0062] (#1258)
* feat: allow full width content in library authoring

* chore: update header and footer versions

---------

Co-authored-by: Jillian <jill@opencraft.com>
2024-10-02 06:16:25 -05:00
Rômulo Penido
b1772383f4 fix: component preview modal overflow (#1348) 2024-10-01 16:56:58 -07:00
Kristin Aoki
b71f2148c9 feat: update ora settings to only be flexible peer grading (#1332) 2024-10-01 13:11:52 -04:00
edX requirements bot
e9c10c7f9e chore: update browserslist DB (#1347)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-09-29 17:35:01 -07:00
Rômulo Penido
4d67e8bda9 feat: improve collection sidebar (#1320)
* feat: improve collection sidebar

* feat: add comments to splice blockTypesArray code

Co-authored-by: Jillian <jill@opencraft.com>
---------

Co-authored-by: Jillian <jill@opencraft.com>
Co-authored-by: Chris Chávez <xnpiochv@gmail.com>
2024-09-27 21:24:12 -05:00
Dmytro
c80483c053 fix: Create button remains deactivated until pick a new org (#1279)
Co-authored-by: Dima Alipov <dimaalipov@192.168.1.101>
2024-09-27 11:37:32 -04:00
Chris Chávez
2cd77ce455 feat: Add tags to manage sidebar of library components (#1299) 2024-09-26 23:33:36 -07:00
Braden MacDonald
95c17537c1 fix: don't revert to advanced problem editor when max_attempts is set (#1326) 2024-09-26 09:50:28 -07:00
Braden MacDonald
3662fadad4 feat!: Remove support for the (deprecated) library authoring MFE (#1327) 2024-09-26 08:38:16 -07:00
Braden MacDonald
ccce44a1c8 fix: library metadata times are actually displayed in local time (#1309) 2024-09-25 17:26:36 -07:00
Rômulo Penido
ff67c9a952 feat: add component Details sidebar [FC-0062] (#1303)
* feat: add ComponentDetails component

---------

Co-authored-by: Jillian <jill@opencraft.com>
2024-09-25 14:33:45 -05:00
renovate[bot]
c13ab00344 chore(deps): update dependency @openedx/frontend-build to v14.1.4 (#1308)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-25 10:17:08 -07:00
Rômulo Penido
b6ec5e1e3a fix: remove preview overlay from library component sidebar (#1323) 2024-09-25 09:32:49 -07:00
Braden MacDonald
5f41db83c2 feat: Enable the Video editor in content libraries [FC-0062] (#1319)
* feat: enable video editor in libraries

* fix: a11y issue in video editor - URL and ID fields were combined

* test: tests for video editor
2024-09-24 21:11:06 -05:00
Braden MacDonald
95521d3b8d Cleanups for the video editor [FC-0062] (#1318)
* refactor: cleanups to video editor code

* test: ignore coverage of blank default data
2024-09-24 20:55:15 -05:00
Braden MacDonald
64d718d198 fix: Use soft nav when clicking a library from studio home (#1306) 2024-09-24 09:32:56 -07:00
edX requirements bot
353ef508df chore: update browserslist DB (#1312)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-09-23 13:22:37 -07:00
renovate[bot]
8b50449c1f fix(deps): update dependency npm to v10.8.3 (#1313)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 09:38:30 -07:00
Navin Karkera
b7ae82bde2 feat: Collections page (in libraries) (#1281) 2024-09-20 10:15:25 -07:00
renovate[bot]
0d472ae66f chore(deps): update dependency eslint-import-resolver-webpack to v0.13.9 (#1284)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-19 13:52:05 -07:00
Zachary Hancock
4e609e02e5 feat: improve error message for proctored exam settings (#1300) 2024-09-19 16:03:34 -04:00
Braden MacDonald
8d49f2ed4e feat: display library title in browser tab (#1305) 2024-09-19 12:34:48 -07:00
Stanislav
f3274e70a6 fix: Fix content overflow in the Pages & Resources modal windows (#1301) 2024-09-19 15:16:53 -04:00
Navin Karkera
9d3a05f1bd feat: show children count in collection card (#1298) 2024-09-19 09:50:38 -07:00
Chris Chávez
053a9b1074 fix: inconsistency with the select all functionality in problem capa type filter (#1294) 2024-09-18 11:35:56 -07:00
Rômulo Penido
fc4b700624 fix: responsiveness on library authoring sidebar (#1293)
* fix: responsiveness on library authoring sidebar

* fix: adjust margin to prevent height change

* fix: prevent button stretch
2024-09-18 11:19:56 -07:00
Braden MacDonald
314dfa60e2 feat: Enable capa problem editor for components in libraries (#1290)
* feat: enable the problem editor for library components

* fix: don't try to load "advanced settings" when editing problem in library

* fix: don't fetch images when editing problem in library

* docs: add a note about plans for the editor modal

* fix: choosing a problem type then cancelling resulted in an error

* chore: remove unused mockApi, clean up problematic 'module' self import

* test: update workflow test to test problem editor

* feat: show capa content summary on cards in library search results

* docs: fix comment typos found in code review

* refactor: add 'key-utils' to consolidate opaque key logic
2024-09-18 17:45:41 +00:00
Braden MacDonald
b01090902a fix: propTypes warnings in Problem Editor, refactor some code to TS (#1280)
* fix: a11y - missing 'alt' text for Problem Editor IconButton
* fix: warning in <ProblemTypeSelect> component - missing key prop in list
* fix: warning: `problemType` required in `ProblemEditor`, but is `null`
* fix: warning: The prop `onClose` marked as required in `SelectTypeModal`
* fix: warning: prop `name` is marked as required in `ForwardRef(_c)`
* fix: warning: props `alt`, `id`, and `key` are required
* test: improve test coverage of SelectTypeModal, refactor some code
* test: improve test coverage
2024-09-18 17:01:56 +00:00
Stanislav
82a3b7c986 fix: Fix content overflow in the Overwrite Files modal window (#1291) 2024-09-17 10:28:18 -04:00
renovate[bot]
fb3533ad49 fix(deps): update dependency frontend-components-tinymce-advanced-plugins to v1.0.4 (#1285)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-16 19:34:58 +00:00
Rômulo Penido
dd7e4d4297 feat: add component sidebar manage tab [FC-0062] (#1275) 2024-09-16 14:13:41 -05:00
Kristin Aoki
902853d649 fix: isInitialized selector depends on unitUrl for course blocks (#1288) 2024-09-16 14:34:56 -04:00
Kyle McCormick
6eed6438cb docs: update README based on rename (#1289) 2024-09-16 13:05:12 -04:00
edX requirements bot
644f1706a2 chore: update browserslist DB (#1283)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-09-16 09:49:11 -07:00
Brayan Cerón
80e3592669 refactor: remove thumbnail for non-edX videos & allow removing fallback URLs (#1241)
* refactor: remove thumbnail from non-edx videos

* fix: when deleting a fallback URL the app crashed

* refactor: simplify conditional rendering
2024-09-16 11:53:05 -04:00
Rômulo Penido
121ced42ec feat: preview components (xblocks) on library authoring pages (#1242) 2024-09-14 10:03:49 -07:00
Chris Chávez
a37a1b1ef8 feat: Create collection Modal [FC-0062] (#1259)
* feat: Enable Collection button on Create Component in Library

* feat: CreateCollectionModal added

* test: For CreateCollectionModal

* refactor: Migrate FormikControl to TypeScript

* test: Add tests for EmptyStates
2024-09-13 21:07:02 -05:00
Braden MacDonald
fd48fef299 feat: edit Text components within content libraries [FC-0062] (#1240) 2024-09-12 19:39:42 -07:00
Navin Karkera
9b61037311 feat: collections tab [FC-0062] (#1257)
* feat: add collections query to search results

* feat: collections tab with basic cards

* feat: add collection card also fix inifinite scroll for collections

* feat: collection empty states

* test: add test for collections card
2024-09-12 18:55:34 -05:00
renovate[bot]
4035931cbb fix(deps): update dependency @openedx/paragon to v22.8.1 (#1268)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-12 19:26:42 +00:00
renovate[bot]
e2e3104474 fix(deps): update dependency @edx/frontend-component-footer to v14.0.10 (#1244)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-12 19:25:27 +00:00
edX requirements bot
88a038c0ea chore: enable github action auto update in dependabot.yml (#1256) 2024-09-12 15:12:17 -04:00
Demid
45c68d6ca4 Hide "Advanced Settings" settings item [BB-9081] (#1252)
* refactor: convert header utils to hooks

* feat: hide advanced settings button if a user doesn't have access
2024-09-12 14:55:01 -04:00
Dmytro
56728310f4 fix: create course button inactive after using org drop-down (#1276)
Co-authored-by: Dima Alipov <dimaalipov@192.168.1.101>
2024-09-12 12:46:04 -04:00
renovate[bot]
9bbbf610b7 fix(deps): update dependency @edx/openedx-atlas to v0.6.2 (#1228) 2024-09-11 11:30:51 -07:00
Braden MacDonald
6255768c97 test: refactor and fix flakiness of LibraryAuthoringTest (#1263) 2024-09-10 13:34:22 -07:00
Dmytro
513309c160 fix: no validation for combined length of org, number, run (#1262)
Co-authored-by: Dima Alipov <dimaalipov@192.168.1.101>
2024-09-10 10:24:11 -04:00
edX requirements bot
bbe15afbe9 chore: update browserslist DB (#1175)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-09-09 14:41:08 -07:00
Braden MacDonald
f9c11f8129 feat: Add type stubs for frontend-platform/i18n (#1251) 2024-09-09 09:30:14 -07:00
Braden MacDonald
376f31ebae chore: clean up dependencies (#1255) 2024-09-09 09:27:15 -07:00
renovate[bot]
849471bfed chore(deps): update dependency @openedx/frontend-build to v14.1.2 (#1213) 2024-09-06 13:27:43 -07:00
Kaustav Banerjee
6b10fa7401 feat: remove new library button if user does not have create access for v1 libraries (#1216) 2024-09-06 10:49:38 -07:00
Kyr
3a61e84c50 feat: fixed height for prerequisite course dropdown list (#1154)
Co-authored-by: Kyrylo Hudym-Levkovych <kyr.hudym@kyrs-MacBook-Pro.local>
2024-09-06 10:28:55 -04:00
Kristin Aoki
dcf05cde07 fix: tinymce render outside of editors (#1254) 2024-09-05 13:18:26 -07:00
Chris Chávez
34f0bf5253 fix: UX nits on Library Info Sidebar (#1253) 2024-09-04 10:25:36 -07:00
Rômulo Penido
c4d00017e0 fix: FilterItem key warning (#1246) 2024-09-04 08:51:13 -05:00
Braden MacDonald
735d978894 refactor: Merge frontend-lib-content-components into this repository 2024-08-29 10:19:17 -07:00
Braden MacDonald
9d0898cdfe chore: update with master 2024-08-29 10:03:55 -07:00
Bilal Qamar
1e7e3e7036 build: Upgrade to Node 20 (#1209)
* feat: updated node to v20

* refactor: updated package-lock along with ci & lockfile version workflows

* refactor: updated lockfile version workflow
2024-08-29 12:08:30 -04:00
Rômulo Penido
48e0ec1f70 feat: add library component sidebar [FC-0062] (#1217) 2024-08-29 07:22:13 -05:00
Braden MacDonald
af2b4dd3cb fix: remove another accidental import of test code from the build 2024-08-28 10:51:36 -07:00
Chris Chávez
64ffaddf3c feat: Capa problem types submenu [FC-0059] (#1207) 2024-08-28 08:16:02 -05:00
Braden MacDonald
7d7394521b chore: update with latest master 2024-08-27 13:45:23 -07:00
Ihor Romaniuk
f36b2183d6 fix: reset active answers for single answer problem type (#426) 2024-08-27 13:44:11 -07:00
Braden MacDonald
83fda560c1 fix: remove accidental import of test code from the build 2024-08-27 13:41:55 -07:00
Chris Chávez
d99a09efba fix: Library v2 info sidebar UI fixes (#1226) 2024-08-26 13:33:54 -07:00
Jillian
259a50c468 UI fixes for Sort Library Component [FC-0059] (#1222)
fix: use "Recently Modified" as the default sort option

When search keyword(s) are entered, use "Most Relevant" as default.

Also

* Hides "Most Relevant" option if no keyword is entered.
* Re-orders the sort menu options
* Ensures the default sort option is not stored in the query string
* Shows the selected sort option on the drop-down toggle button
* Shows "Sort By" as a header inside the drop-down menu
2024-08-23 19:11:53 -05:00
Bilal Qamar
3e0f7b5758 test: Add Node 20 to CI matrix (#1224) 2024-08-22 14:37:36 -04:00
Rômulo Penido
21c9e30207 refactor: change toast component (#1211) 2024-08-22 08:50:43 -05:00
Muhammad Anas
8ae9dfbd88 feat: customize the certificate link in header (#1223)
* feat: customize the certificate link in header

* fix: lint issues

* fix: tests
2024-08-21 10:36:14 -04:00
Navin Karkera
3089d0b993 fix: discard button [FC-0062] (#1214)
* fix: discard changes

* refactor: disable discard btn for new libs

Enable it if components are added.

* refactor: invalidate library related content queries

* chore: add comment about content search query invalidation
2024-08-20 21:12:06 -05:00
renovate[bot]
47cec6e4c9 fix(deps): update dependency @edx/frontend-lib-content-components to v2.6.8 (#1218)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-20 15:21:54 -04:00
Rômulo Penido
f370b565c2 fix: dropdown checkbox click area (#1215) 2024-08-17 16:43:27 -07:00
Braden MacDonald
155a710aa8 chore: work around type error with <SelectableBox> 2024-08-17 16:41:12 -07:00
Braden MacDonald
591a02e8a7 chore: update with master 2024-08-17 16:27:00 -07:00
Braden MacDonald
f90bbb2de7 chore: update to frontend-lib-content-components 2.6.8 2024-08-17 16:23:19 -07:00
Braden MacDonald
28e1956708 chore: update imports, fix lint issues 2024-08-17 16:12:58 -07:00
Ihor Romaniuk
b55e5c9f8f fix: answer range validation in Numerical input (#482) 2024-08-16 12:19:11 -04:00
Kyr
a9e8bd5558 fix: license widget checkbox and link (#486)
* fix: share alike after save, license link for creative common

* test: update snapshot

---------

Co-authored-by: Kyrylo Hudym-Levkovych <kyr.hudym@kyrs-MacBook-Pro.local>
2024-08-16 10:19:13 -04:00
Yusuf Musleh
95ac0983a3 feat: Add "Paste from Clipboard" to lib v2 sidebar (#1187) 2024-08-15 10:03:39 -07:00
renovate[bot]
7c59b4a210 fix(deps): update dependency @openedx/paragon to v22.7.0 (#1180)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-13 16:39:37 -04:00
renovate[bot]
de3befec08 fix(deps): update dependency @edx/frontend-component-header to v5.3.4 (#1179)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-13 18:08:37 +00:00
renovate[bot]
6ff3847c6c fix(deps): update dependency @edx/openedx-atlas to v0.6.1 (#1123)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-13 13:53:33 -04:00
renovate[bot]
ea90e7e93c fix(deps): update dependency @edx/frontend-lib-content-components to v2.6.6 (#1210)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-13 17:50:42 +00:00
renovate[bot]
48ffa0f970 fix(deps): update dependency @edx/frontend-component-footer to v14.0.8 (#1161)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-13 17:49:42 +00:00
Chris Chávez
4f5346ed31 feat: Library info sidebar - allows lib rename+publish (#1138) 2024-08-13 10:37:34 -07:00
Ihor Romaniuk
940482dd9a fix: add validation to problem number fields (#425) 2024-08-12 11:04:28 -04:00
renovate[bot]
8285f8ec5a fix(deps): update dependency @edx/frontend-lib-content-components to v2.6.5 (#1206)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-12 09:43:34 -04:00
Braden MacDonald
afa2317131 feat: 'frontend-lib-content-components' into this repo 2024-08-09 11:48:49 -07:00
Braden MacDonald
d3d5fe0e1b Removed unneeded files from lib-content-components 2024-08-09 11:47:35 -07:00
Kristin Aoki
b088a8fe3d fix: asset name parsing in static converter (#501) 2024-08-09 13:02:34 -04:00
Rômulo Penido
bb88101255 feat: add "copy to clipboard" feature to library authoring UI (#1197) 2024-08-08 09:32:04 -07:00
Chris Chávez
a7645afd22 fix: UI fixes for read-only libraries etc. (#1198)
* fix: Hide open Create content buttons without permissions

* feat: Read only badge on library Home

* refactor: library authoring to get canEditLibrary from useContentLibrary

* style: Typo on the code
2024-08-07 18:26:32 -07:00
Kristin Aoki
7379e734a0 feat: replace progress bar with loading spinner (#1192) 2024-08-07 12:07:22 -04:00
Kristin Aoki
3d82d37943 Revert "fix(deps): update dependency @edx/frontend-lib-content-components to …" (#1205)
This reverts commit 6f13164998.
2024-08-06 12:50:21 -04:00
Brandon Bodine
553acd8fcc chore: remove unused ai-translations env vars (#1204) 2024-08-06 07:17:08 -06:00
renovate[bot]
6f13164998 fix(deps): update dependency @edx/frontend-lib-content-components to v2.6.4 (#1184)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-05 09:31:12 -04:00
Jorg Are
9efb583cdc feat: replace ai-translations component with a plugin slot (#1186)
* feat: replace ai-translations component with a plugin slot

* feat: move ai-translations enabled check to plugin
2024-08-05 13:46:50 +01:00
Kristin Aoki
b68257e176 fix: remove imports breaking build (#500) 2024-08-02 15:04:49 -04:00
Rômulo Penido
680b5ff160 refactor: convert a couple files to TS and improve typings/tests (#1181)
* refactor: convert files to ts and improve typings/tests

* fix: set return type to unkown for future fix
2024-08-02 09:26:10 -07:00
Kristin Aoki
beb4813c53 fix: setAssetToStaticUrl regex matcher (#497) 2024-08-01 15:56:32 -04:00
Ihor Romaniuk
ce8703799b fix: add rtl support to editor (#424) 2024-08-01 13:21:01 -04:00
Adolfo R. Brandes
5825dd36d3 Merge pull request #487 from open-craft/braden/fix-extra-scss
fix: don't accidentally bundle paragon CSS x2
2024-08-01 09:55:35 -03:00
Jillian
cba85ab96d test: fix flaky library-authoring test (#1193) 2024-07-30 13:33:24 -07:00
Kristin Aoki
cc3bbfd9af fix: package.json out of sync with package-lock.json and upgrade (#1194) 2024-07-30 20:24:37 +00:00
Jillian
4f88948844 feat: adds sort widget to search manager and library component page (#1147) 2024-07-30 09:41:10 -07:00
Yusuf Musleh
699cbeadb3 feat: Add cancel create library button (#1182) 2024-07-26 09:39:24 -07:00
Rômulo Penido
6382898213 chore: add ts* files to lint --fix script 2024-07-26 09:06:54 -07:00
Kristin Aoki
3dfc579745 feat: add conditional for new parser beta testing (#496) 2024-07-26 10:59:40 -04:00
renovate[bot]
649863d094 fix(deps): update dependency @edx/frontend-lib-content-components to v2.5.2 (#1178)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-25 11:21:40 -04:00
renovate[bot]
1be693b826 fix(deps): update dependency npm to v10.8.2 2024-07-24 15:02:19 -07:00
renovate[bot]
0933bae314 chore(deps): update actions/checkout action to v4 2024-07-24 14:43:33 -07:00
Rômulo Penido
f159b2b31c chore: update frontend-build (#1155) 2024-07-24 21:42:03 +00:00
Rômulo Penido
25ab1fffa1 feat: adds filter by tags and contentType to library home (#1141)
Refactor to add search-manager feature.

Co-authored-by: Yusuf Musleh <yusuf@opencraft.com>
2024-07-24 12:13:24 -07:00
Milad Emami
ebab15f046 fix: correct typo in Alert component prop (#494)
Corrected the typo in the prop name of the Alert component from 'varaint' to 'variant'. This change ensures the proper functioning of the alert in informational variant.
2024-07-24 13:27:16 -04:00
Chris Chávez
77135cde1d feat: Library v2 components tab (#1109) 2024-07-22 18:04:57 -07:00
edX requirements bot
3a14141a4e chore: update browserslist DB (#1156)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-07-17 10:23:54 +02:00
Chris Chávez
3d24741062 feat: "Add content" sidebar on each library home page (#1065) 2024-07-17 10:15:40 +02:00
Rômulo Penido
e087001905 feat: create library (v2) form (#1116) 2024-07-12 05:54:37 -07:00
Braden MacDonald
cc41a2fda1 fix: source map warning seen during build (#1150) 2024-07-11 15:15:02 -04:00
renovate[bot]
5dee203401 fix(deps): update dependency @edx/frontend-lib-content-components to v2.5.1 (#1153)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 09:55:51 -04:00
Kristin Aoki
9bce0a34e3 fix: remove deprecated feedback link (#493) 2024-07-10 15:58:19 -04:00
renovate[bot]
085069abb0 fix(deps): update dependency @edx/frontend-lib-content-components to v2.5.0 (#1143)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-10 19:04:39 +00:00
Kyr
a22a260e27 feat: remove offset when stuio header exists (#491)
Co-authored-by: Kyrylo Hudym-Levkovych <kyr.hudym@kyrs-MacBook-Pro.local>
2024-07-10 14:35:25 -04:00
Kristin Aoki
b6ff6230e7 fix: text editor opening blank with no images (#492)
* fix: pasting and images only insert at beginning

* fix: add image click not showing gallery

* chore: increase code coverage

* fix: empty string when no srcs need updates

* fix: assest to static in raw editor
2024-07-10 13:53:40 -04:00
Braden MacDonald
ab9d57345b chore: remove unused fontawesome dependencies (#1149) 2024-07-10 14:05:02 +00:00
Rômulo Penido
71fcf9f168 fix: only show course blocks in the search modal (#1148) 2024-07-10 05:29:55 -07:00
Rômulo Penido
f60ddb579e feat: library home page ("bare bones") (#1076) 2024-07-10 05:20:00 -07:00
Braden MacDonald
117b4f10e7 chore: remove core-js and regenerator-runtime (#1032) 2024-07-10 05:07:29 -07:00
edX requirements bot
09822c2937 chore: update browserslist DB (#443) 2024-07-10 01:24:44 -07:00
Hunia Fatima
01d4b85205 perf: lockfile version check workflow file updated (#1107)
Co-authored-by: Hunia Fatima <hunia.fatima@A006-01315.local>
2024-07-10 11:05:43 +05:00
Yusuf Musleh
83489b0983 feat: Add filters/sorting for the libraries v2 tab on studio home (#1117) 2024-07-08 14:35:43 +00:00
Braden MacDonald
8cf26e1a75 Version bump for Paragon to 22.6.1, with stricter typing (#1146)
* fix(deps): update dependency @openedx/paragon to v22.6.1

* fix: lint errors from stricter types in new paragon version

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-08 10:10:08 -04:00
renovate[bot]
9528bfde62 chore(deps): update dependency meilisearch to ^0.41.0 (#1136) 2024-07-08 06:49:44 -07:00
Raymond Zhou
292663a5e3 Revert "fix(deps): update dependency @edx/frontend-lib-content-components to …" (#1142)
This reverts commit cdc9af2ed4.
2024-07-02 12:19:19 -04:00
renovate[bot]
9f0be768aa fix(deps): update dependency @edx/frontend-component-footer to v14.0.5 (#1121)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-01 17:11:05 +00:00
Kristin Aoki
efd73f9c0b fix: progress bar display for uploads (#1135) 2024-07-01 12:52:08 -04:00
renovate[bot]
cdc9af2ed4 fix(deps): update dependency @edx/frontend-lib-content-components to v2.4.3 (#1140)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-01 16:51:39 +00:00
Kristin Aoki
fc3cd9a9ce fix: image and paste insert (#490)
* fix: pasting and images only insert at beginning

* fix: add image click not showing gallery

* chore: increase code coverage
2024-07-01 12:29:05 -04:00
Kristin Aoki
eb3e6faba4 fix: update mapToStateProps to match changes in TinyMceWidget (#1133)
* fix: update mapToStateProps to match changes in TinyMceWidget

* feat: bump frontend-lib-content-components
2024-06-27 14:44:12 -04:00
Kristin Aoki
267823414e fix: simple editor without solution not loading (#489)
* fix: update initialize to only call required functions

* feat: update asset urls without asset object

* feat: add pagination to select image modal

* fix: lint errors

* chore: update tests

* fix: asset pattern regex match

* feat: update pagination to be button to prevent page skipping

* fix: e.target.error for feedback fields

* fix: failing snapshots

* fix: new simple problem load error
2024-06-27 12:54:59 -04:00
Braden MacDonald
a4859d2686 chore: convert all 'search-modal' code to TypeScript (#1129)
* chore: convert all 'search-modal' code to TypeScript

* fix: lint should check .ts[x] files too

* fix: remove unused dependency meilisearch-instantsearch
2024-06-27 12:54:01 -04:00
Kristin Aoki
22ea32cf01 feat: video upload progress modal (#1131)
* feat: add upload progress modal

* fix: increase code coverage

* fix: fix code to be more readable

* fix: delete empty file

* fix: failing test and lint

* fix: progress bar not updating

* feat: add missing abort controller on POST to edxVal
2024-06-26 18:00:07 -04:00
renovate[bot]
8b759bc867 fix(deps): update dependency @edx/frontend-lib-content-components to v2.4.1 (#1132)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-26 13:54:56 -04:00
bszabo
0e913739d4 Merge pull request #488 from openedx/revert-484-KristinAoki/improve-asset-loading
Revert "feat: improve asset loading"
2024-06-26 10:08:55 -04:00
bszabo
ba8141ea6a Revert "feat: improve asset loading (#484)"
This reverts commit f3ae225d64.
2024-06-26 10:02:59 -04:00
Kristin Aoki
9317b87564 Revert "feat: add upload progress modal (#1113)" (#1128)
This reverts commit 8ef804bd58.
2024-06-24 12:19:58 -04:00
Kristin Aoki
8ef804bd58 feat: add upload progress modal (#1113)
* feat: add upload progress modal

* fix: increase code coverage

* fix: fix code to be more readable

* fix: delete empty file
2024-06-24 10:53:49 -04:00
renovate[bot]
641419656f fix(deps): update dependency @edx/frontend-lib-content-components to v2.4.0 (#1118)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-24 09:02:05 -04:00
Jillian
6b6d3aaa7a Upgrade frontend-build to v14 (#1052)
* fix: warnings about Duplicate message id
* fix: paragon's Hyperlink no longer accepts a 'content' attribute
* test: ensure all act() calls are async
* test: Removed "async" from "describe"
* fix: DiscussionsSettings tests
* Don't nest userAction.click in act() -- nested act() statements have
  indeterminent behaviour.
* Use getBy* instead of findBy* with userAction to avoid nested act() statements
* Always await userEvent.click
* Use fireEvent.click when the onClick handlers need to be called
* Use queryBy* instead of getBy* when using .toBeInTheDocument or 
* fix: typo in data-testid
* test: Use useLocation to test route changes
* Don't nest userAction.click in act() -- nested act() statements have
* chore: fix lint:fix and lint errors
* remove "indent" setting from .eslintrc.js
* add @typescript-eslint/ prefix to eslint-disable-line statements where flagged by linter
* changed stylelint setting import-notation to "string"
* test: fix failing tests after upgrade
* fix: css error "target selector was not found"
* chore: upgrades dependency frontend-lib-content-components@2.3.0
* chore: bumps @edx/frontend-component-ai-translations to ^2.1.0

---------

Co-authored-by: Yusuf Musleh <yusuf@opencraft.com>
2024-06-22 00:14:46 +05:30
Braden MacDonald
28c7b32bd5 fix: don't accidentally bundle paragon CSS x2 2024-06-20 15:23:08 -07:00
Marcos Rigoli
3936737b48 feat: Include org filter when requesting LTI providers (#1114)
* feat: Include org filter when requesting LTI providers

* chore: Created silent version for CI testing to avoid log flooding
2024-06-20 11:17:08 -03:00
Yusuf Musleh
088a01d716 feat: Add lib v2/legacy tabs in studio home (#1050)
This PR adds a new configuration flag that shows/hides tabs in studio home along with some new functionality around to V1 and V2 Libraries.

When the new LIBRARY_MODE flag is set to "mixed" (default in dev) it will show "Libraries" and "Legacy Libraries" tabs that correspond to v1 and v2 tabs respectively.

When the new LIBRARY_MODE flag is set to "v1 only" (default in production) or "v2 only", only one tab "Libraries" is shown and only the respective libraries are fetched when the tab is clicked.

In addition to the above changes, the URL/route now updates when clicking on the tabs, and navigating to it directly would open up that tab as well as a new placeholder page that you will be redirected to when clicking on a v2 library if the library authoring MFE is not enabled.
2024-06-20 17:30:57 +05:30
vladislavkeblysh
c84e3229f6 feat: Validation for Start time and Stop time fields (#419)
* feat: fixed fields onblur

* feat: fixed fields onblur

* feat: added new tests
2024-06-18 16:51:32 -04:00
Dmytro
d2ddc9099f fix: Not used Number of attempts field (#473)
Co-authored-by: Dima Alipov <dimaalipov@MacBook-Pro-Dima.local>
2024-06-18 16:44:51 -04:00
Ihor Romaniuk
6ac0a6e562 fix: form group controls alignment (#423) 2024-06-18 16:40:49 -04:00
Navin Karkera
e2ed3bc7a7 refactor: show generic message on studio server error (#1112) 2024-06-18 16:07:46 -04:00
Adolfo R. Brandes
6a58779ffe Merge pull request #485 from arbrandes/update-codecov
build: Update codecov and use token
2024-06-17 11:57:12 -03:00
Kristin Aoki
f3ae225d64 feat: improve asset loading (#484)
* fix: update initialize to only call required functions

* feat: update asset urls without asset object

* feat: add pagination to select image modal

* fix: lint errors

* chore: update tests

* fix: asset pattern regex match

* feat: update pagination to be button to prevent page skipping

* fix: e.target.error for feedback fields

* fix: failing snapshots
2024-06-17 09:52:49 -04:00
Adolfo R. Brandes
74776b3663 build: Update codecov and use token
Update codecov to the latest version and start using the org-wide token for uploads.

See https://github.com/openedx/wg-frontend/issues/179
2024-06-14 11:44:01 -03:00
Jesper Hodge
db1250ee95 Revert "feat: flcc to 2.2.0 (#1106)" (#1111)
This reverts commit e22cce9fa6.
Reverts #1106

The reason is that the pipeline to deploy to stage broke. This commit is probably the reason.

The revert is temporary until the pipeline problem is solved.
2024-06-14 14:17:41 +00:00
Jillian
f20e5311a9 Fix some test warnings (#1062)
* fix: paragon's Hyperlink no longer accepts a 'content' attribute
* test: ensure all act() calls are async
* test: Removed "async" from "describe"
Returning a Promise from "describe" is not supported.
* fix: DiscussionsSettings tests
Previous commit revealed several issues with these tests
* Don't nest userAction.click in act() -- nested act() statements have indeterminent behaviour.
* Use getBy* instead of findBy* with userAction to avoid nested act() statements
* Use fireEvent.click when the onClick handlers need to be called
* Use queryBy* instead of getBy* when using .toBeInTheDocument or waitForElementToBeRemoved
  queryBy* return null when the element is not found.
* fix: typo in data-testid
Warning: React does not recognize the `data-testId` prop on a DOM
element. If you intentionally want it to appear in the DOM as a custom
attribute, spell it as lowercase `data-testid` instead.
* test: Use useLocation to test route changes
---------

Co-authored-by: Yusuf Musleh <yusuf@opencraft.com>
2024-06-13 10:37:26 +05:30
Raymond Zhou
e22cce9fa6 feat: flcc to 2.2.0 (#1106) 2024-06-12 12:42:00 -04:00
Bilal Qamar
252ad6a6b9 feat: updated frontend-build & frontend-platform major versions (#475) 2024-06-12 05:29:17 -04:00
Jillian
6760b75774 fix: warnings about Duplicate message id (#1061)
Fixes warnings about "duplicate message IDs", which seem to have been made by copy-paste errors.
2024-06-11 18:01:25 +05:30
Raymond Zhou
7f5e82a844 fix: handle null displayname (#1074) 2024-06-07 13:40:59 -04:00
Kristin Aoki
7aa2baaa8a fix: bump frontend-lib-content-components package (#1071) 2024-06-05 14:19:53 -04:00
Kristin Aoki
e543ccc2e1 fix: parser not saving unlimited attempts (#483)
* fix: default settings not loading for new problems

* fix: unlimited attempts not saving
2024-06-04 16:41:58 -04:00
Yusuf Musleh
8cde43eb5b fix: Search result redirect to unit lib component (#1027)
This change fixes redirection to the library component in the unit when selecting the search result. It also fixes an issue with navigating to the library MFE when selecting a library component.
2024-06-03 17:55:37 +05:30
Chris Chávez
460de7014e [FC-0042] Fix: Bug: Unusable "Languages" taxonomy appears in tagging drawer (#1031)
Hide language taxonomy when is empty
New message on search result when taxonomy is empty
Empty taxonomies message added in drawer
2024-06-03 17:36:54 +05:30
Kristin Aoki
9b4eb10342 fix: allow grace period minutes only (#1064)
* fix: allow grace period minutes only

* fix: zero minutes error
2024-05-31 15:30:00 -04:00
Kristin Aoki
a340320e8f fix: upgrade frontend -lib-content-componets package (#1060) 2024-05-31 09:56:03 -04:00
Kristin Aoki
a959c0543c fix: removal of content after problem type tags (#479)
* fix: removal of content after problem type tags

* fix: readability and error handling
2024-05-30 12:33:43 -04:00
Kristin Aoki
a585a13e97 fix: wrong lock status update message (#1053) 2024-05-28 11:52:39 -04:00
Kristin Aoki
435af2c36f fix: update date using utc timezone instead of local (#1043)
* fix: update date using utc timezone instead of local

* fix: lint error
2024-05-24 13:07:10 -04:00
Rodrigo Martin
732b7ed86c feat(AU-2035): Add disclaimer to AppSettingsModalBase (#1024) 2024-05-24 12:13:42 -03:00
Yusuf Musleh
d0b3328f26 feat: Import new taxonomy dialog flow (#1017)
This PR updates the existing import tags wizard to also handle  importing new taxonomies.
2024-05-24 19:19:26 +05:30
Chris Chávez
c3df0b0692 feat: Show toast when exporting course tags (#995)
Show in  in-progress toast when exporting course tags
2024-05-24 15:54:59 +05:30
Kristin Aoki
7247cc2d71 feat: bump frontend-lib-content-components 2.1.9 (#1028) 2024-05-22 15:40:04 -04:00
Kristin Aoki
3f987f9958 feat: improve error messaging and empty updates (#1025)
* feat: improve error messaging and empty updates

* chore: improve code coverage

* fix: update error messages

* fix: message title for saving handouts
2024-05-22 14:28:53 -04:00
Kristin Aoki
6c743f858d fix: video selection gallery scroll (#477)
* fix: video selection gallery scroll

* fix: lint errors
2024-05-22 12:02:25 -04:00
Peter Kulko
3647bcbbf9 fix: fixed rerun link (#1023)
* fix: fixed rerun link

* refactor: code refactoring

* refactor: updated tests

* refactor: after review
2024-05-21 12:58:53 -04:00
bszabo
54003af07c fix: issue-1018 remove reference to edX user\nIn configure wiki opera… (#1021)
* fix: issue-1018 remove reference to edX user\nIn configure wiki operation

* fix: issue-1018 keep to original they wording
2024-05-21 11:24:21 -04:00
renovate[bot]
f34157e11c fix(deps): update dependency @edx/frontend-lib-content-components to v2.1.8 (#997)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-16 14:26:42 +00:00
renovate[bot]
8a491cc365 fix(deps): update dependency @openedx/paragon to v22.4.0 (#963)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-16 14:15:18 +00:00
Leangseu Kim
7d9dd7535b chore: lock @edx/react-unit-test-utils 2024-05-16 10:02:50 -04:00
Ihor Romaniuk
7c539f346b fix: info icon shrinking on advanced settings page (#984) 2024-05-15 13:02:05 -03:00
ABBOUD Moncef
3315205d15 fix: video status badge translation (#438) 2024-05-13 15:26:07 -04:00
Navin Karkera
d882f2f856 feat: discussion setting and release & due date setting (#976)
* fix: hide release and due dates config in self paced courses

* feat: discussion enable setting for unit in outline

* refactor: message text

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>

* fix: modal dialog overflow

---------

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>
2024-05-13 11:21:18 -04:00
Leangseu Kim
65132eead2 fix: allow page grid to take plugin slot id instead (#994)
* fix: allow page grid to take plugin slot id instead

* chore: add config check

* chore: linting

* chore: variable more readable
2024-05-13 10:39:11 -04:00
Peter Kulko
a88066a2c5 fix: fixed course rerun route (#993) 2024-05-09 13:51:22 -03:00
Chris Chávez
e0fb41d8f5 [FC-0049] feat: Other Tags section added on tags drawer (#987)
Adds the new "Other tags" Section to tags drawer that contains the taxonomies/tags that the user doesn't have permission to see/edit. It allow to delete those tags.
2024-05-09 21:34:22 +05:30
Peter Kulko
55adcfe90d feat: Added errors handling 4xx, 5xx (#992)
* feat: [AXIMST-538] Add errors handling 4xx, 5xx

* fix: resolve discussions

* fix: second round of review discussions

refactor: fixing tests for Textbooks page

Co-authored-by: ruzniaievdm <ruzniaievdm@gmail.com>
2024-05-09 11:10:28 -03:00
ruzniaievdm
c884ff2882 fix: Group configurations - Add a counter for usage groups (#991) 2024-05-09 08:59:44 -03:00
Navin Karkera
5c1df3e16e feat: better api error handling (#972)
Improve API error handling.
2024-05-09 11:18:00 +05:30
Yusuf Musleh
8aea28c6e0 feat: search filters refinement (#980)
Sort blocktypes on hierarchy then alphabet.
2024-05-09 11:10:21 +05:30
Braden MacDonald
14245bc6ad feat: Enable taxonomy/tagging feature in MFE by default (#989)
* feat: enable tagging/taxonomy feature by default

* test: improve coverage

* chore: fix lint issue
2024-05-08 18:12:56 -03:00
Chris Chávez
dd9202fafe feat: prevent losing work when users click outside tag drawer after making edits [FC-0036] 2024-05-08 15:56:32 -04:00
Jillian
23fb68f2c3 feat: show "No tags added yet. [Add tags]" on the tag drawer (#988)
when we've expanded a taxonomy in read mode with no content tags added.
2024-05-08 16:47:39 -03:00
Chris Chávez
92b7ae1b77 feat: Add In progress message on import taxonomy (#953)
Message added as a toast
2024-05-08 16:44:21 -03:00
Navin Karkera
087c82c60c refactor: handle relative proctoring link (#974) 2024-05-07 12:01:41 +05:30
Chris Chávez
de408b5a3a [FC-0036] refactor: Further refinements to tag drawer (#970)
* refactor: Further refinements to tag drawer
* Padding to top and left tagging drawer
* Changes in headings in the tagging drawer
2024-05-06 14:46:46 +05:30
Bryann Valderrama
2f5d4f71ec feat: add ENABLE_GRADING_METHOD_IN_PROBLEMS feature flag (#932) 2024-05-03 10:54:52 -04:00
Chris Chávez
64be7e3b37 feat: Send messages after update tags (#975)
Updates the code of Tag Drawer to send two messages to parent (if the drawer is a iframe) when the tags are updated:

- One message with updated content tags.
- One message with the content tags count.
2024-05-03 20:04:42 +05:30
Navin Karkera
a63c808300 refactor: change expand-collapse arrows in outline (#973)
Set arrow down for expanded section/subsection and right arrow for
collapsed items same as legacy
2024-05-03 18:36:51 +05:30
Yusuf Musleh
6d9a8a1eac feat: search modal refinements (#959)
* feat: More spacing between search bar and selectmenu
* feat: Autofocus search field when modal opens
* feat: Fix issues with scroll to search result
This includes the following:
  - The target search element is aligned to the top of the page when scrolling to it
  - Makes sure the section/subsection is expanded in order to scroll to result
* fix: Match focus border radius with button's
* fix: Only expand (sub)section with search result
2024-05-03 15:02:22 +05:30
vladislavkeblysh
65f45f72f0 feat: [FC-0044] Textbooks Page (#890)
Implement Textbooks page.

---------

Co-authored-by: Glib Glugovskiy <glib.glugovskiy@raccoongang.com>
2024-04-30 18:38:43 -03:00
Peter Kulko
a9a73efbb6 feat: [FC-0044] Course unit - Drag and drop for xblocks (#908)
Implements drag and drop for xblocks in the unit page.
2024-04-30 11:21:51 -03:00
Rômulo Penido
e24fb7889e feat: redirect to unit page if the hit or its parent is a unit (#957)
This change adds the feature to redirect from a search result to a Unit, in case the hit parent is a Unit, or the hit is a unit itself.
2024-04-29 19:50:12 +05:30
XnpioChV
9327948b61 feat: [FC-0036] Refined tag drawer
It contains different changes to achieve the reading and editing mode of the drawer tag:

* Manage tags drawer footer with buttons added.
* Creation of ContentTagsDrawerContext.
* Creation of global state and global removed state to allow edit mode.
* Update API client to match with openedx-learning 0.9.1: Save tags of multiple taxonomies; to save all tags added/removed on edit mode
* Extract TagsTree and use it on the Tags Drawer.
* Update TagsTree to allow edit mode.
* Add a Toast on Tags Drawer; show the toast afert save.
* Scrolling + sticky footer on tags drawer
2024-04-25 15:29:13 -04:00
Yusuf Musleh
4146fa6c6e feat: Remove taxonomy export id prompt (#955) 2024-04-26 00:42:32 +05:30
Felipe Montoya
be71668b8d fix: removing ENABLE_NEW_EDITOR_PAGES flag (#951) 2024-04-25 08:35:45 -03:00
Peter Kulko
5686dee43b feat: [FC-0044] Course unit - Copy/paste functionality (#884)
Implement copy/paste.

Co-authored-by: monteri <36768631+monteri@users.noreply.github.com>
Co-authored-by: ihor-romaniuk <ihor.romaniuk@raccoongang.com>
2024-04-24 15:27:29 -03:00
Raymond Zhou
bef6796da4 fix: errors for positive time zones (#961) 2024-04-24 09:07:15 -07:00
Leangseu Kim
e55f031c39 chore: add slot to allow additional course app plugin (#941)
* chore: add @openedx/frontend-plugin-framework

chore: move plugin page setting button to a props

chore: split out app setting modal for reusability

chore: add implementation of WTC plugin

chore: update app setting form

chore: implement the plugin form with mock

chore: follow the UI design

chore: remove translation plugin and move it into frontend-plugin instead

* chore: add eslint ignore for env.config.jsx

* chore: update package-lock.json
2024-04-24 11:55:46 -04:00
connorhaugh
98138181f7 feat: add browser dialog to prevent navigation (#962)
Switching from undefined to "" tells the browser to put in a modal which requires users to confirm thier navigation away. this will prevent continued annoyances from upload failures: https://2u-internal.atlassian.net/browse/TNL-11587
2024-04-24 11:04:12 -04:00
Braden MacDonald
c32462e21e feat: Allow filtering by multiple tags [FC-0040] (#945)
As of #918 , the content search only allows filtering the results by one tag at a time, which is a limitation of Instantsearch.

So with this change, usage of Instantsearch + instant-meilisearch has been replaced with direct usage of Meilisearch. Not only does this simplify the code and make our MFE bundle size smaller, but it allows us much more control over how the tags filtering works, so that we can implement searching by multiple tags.

Trying to modify Instantsearch to do that was too difficult, given the complexity of its codebase.

Related ticket: openedx/modular-learning#201
2024-04-24 09:15:17 +05:30
Kristin Aoki
34104495c5 fix: adding files count in toast (#960)
* fix: adding files count in toast

* fix: toast to use plural function
2024-04-23 16:05:13 -04:00
ruzniaievdm
907ce50071 feat: [FC-0044] group configurations MFE page (#929)
* feat: group configurations - index page

* feat: [AXIMST-63] Index group configurations page

* fix: resolve discussions

* fix: resolve second round discussions

* feat: group configurations - content group actions

* feat: [AXIMST-75, AXIMST-69, AXIMST-81] Content group actions

* fix: resolve conversations

* feat: group configurations - sidebar

* feat: [AXIMST-87] group-configuration page sidebar

* refactor: [AXIMST-87] add changes after review

* refactor: [AXIMST-87] add changes after review

* refactor: [AXIMST-87] add changes ater review

---------

Co-authored-by: Kyrylo Hudym-Levkovych <kyr.hudym@kyrs-MacBook-Pro.local>

* fix: group configurations - the page reloads after the user saves changes

* feat: group configurations - experiment groups

* feat: [AXIMST-93, 99, 105] Group configuration - Experiment Groups

* fix: [AXIMST-518, 537] Group configuration - resolve bugs

* fix: review discussions

* fix: revert classname case

* fix: group configurations - resolve discussions

fix: [AXIMST-714] icon is aligned with text (#210)

* fix: add hook tests

* fix: add thunk tests

* fix: add slice tests

* chore: group configurations - messages

* fix: group configurations - remove delete in edit mode

---------

Co-authored-by: Kyr <40792129+khudym@users.noreply.github.com>
Co-authored-by: Kyrylo Hudym-Levkovych <kyr.hudym@kyrs-MacBook-Pro.local>
Co-authored-by: monteri <lansevermore>
2024-04-23 11:53:49 -04:00
Rômulo Penido
7f668a6ca4 refactor: remove old taxonomy page route (#954) 2024-04-23 14:52:17 +05:30
Ihor Romaniuk
6ec44b5f41 feat: [FC-0044] Unit page - Manage access modal (unit & xblocks) (#901)
* feat: [FC-0044] Unit page - Manage access modal (unit & xblocks)

* fix: add message description
2024-04-22 11:13:16 -04:00
sundasnoreen12
1834655399 fix: fixed scroll issue of provider and settings (#958) 2024-04-22 15:04:52 +05:00
Raymond Zhou
bfcac5c0dd feat: bump flcc (#956) 2024-04-18 15:24:54 -04:00
Raymond Zhou
e0b70f2b17 fix: HTML screen should expand with content (#474) 2024-04-18 14:48:10 -04:00
Chris Chávez
422a5db6f9 [FC-0036] feat: Sort taxonomies (#949)
Taxonomies are now sorted by tag count for those with applied tags, and by name for the rest.
2024-04-18 09:15:33 +05:30
Chris Chávez
08140226c3 [FC-0036] Refined tag drawer UI style (#947)
* feat: Remove 'x' close btn from tags drawer

* feat: Change style for taxonomy tags count

* feat: Update heading styles in tags drawer

* feat: Move dropdown arrows to left of taxonomy

---------

Co-authored-by: Yusuf Musleh <yusuf@opencraft.com>
2024-04-17 19:16:25 +05:30
Rômulo Penido
612d1d8c63 feat: improve search modal results [FC-0040] (#946)
This change makes improvements to the `SearchResult` component which shows the search results in a number of ways:

- It improves the information messages that show up before you start search, and if the results are empty. 
- It adds more context to the search results, by displaying the location of the content
- It adds a link to directly navigate to the relevant content item. 
- It adds an animated highlight to a unit right after you navigate to it. 

---------

Co-authored-by: Jillian <jill@opencraft.com>
Co-authored-by: Braden MacDonald <mail@bradenm.com>
2024-04-17 13:43:56 +05:30
Feanil Patel
b119671ee2 build: Update codecov and start using repo tokens.
Update codecove to the latest version and start using the per repo
tokens which will soon be required to get more reliable coverage builds.

This change also has a corresponding addition of a codecov repo upload
token to the repository secrets for this repo.
2024-04-16 11:55:55 -04:00
Jesper Hodge
0f440c6b3a Fix video upload failures (#952) 2024-04-15 16:49:07 -04:00
Braden MacDonald
2fda48fa5f Bump Paragon to v22.2.1, fix some bugs that turned up (#933)
* chore: bump paragon version

* chore: update snapshot test - alt moved from sr-only to aria-label
2024-04-12 14:05:56 -04:00
Kristin Aoki
63e220ee3e fix: change dropdowns to menus to fix off-screen render (#948) 2024-04-12 13:52:51 -04:00
Jhon Vente
2641aecc8a feat: home studio search filters (#846)
* feat: pagination studio home for courses

* chore: addressing some comments

* refactor: addressing pr comments

* test: adding test for studio home slice

* feat: search input and filters for course home

* fix: using open edx paragon

* feat: usedebounce hook for searching courses

* fix: filters params for searching coruses

* feat: adding coursekey when course name is empty

* chore: remove edit in studio button

* fix: message changed when courses were  not found

* refactor: support courses tab filters and pagination

* test: more cases for course filters component

* refactor: coverage for onsubmit search field

* test: unit test for courses tab component

* feat: loading for search input and layout of course tab

* fix: linter problems

* test: adding more tests for courses tab

* refactor: don't ignore empty string as a case for searching

* refactor: manage empty search bar as special case for searching

* fix: remove expected dispatch mock for clear button

---------

Co-authored-by: Maria Grimaldi <maria.grimaldi@edunext.co>
2024-04-11 16:25:19 -04:00
Braden MacDonald
fc3e38f63b feat: Content Search Modal: Filters [FC-0040] (#918)
Implementation of openedx/modular-learning#201

Implements a modal for searching course content with filters for searching in current or all courses, filtering by content type, content tags and text.
2024-04-11 10:01:06 +05:30
Raymond Zhou
aaf4989610 fix: react datepicker workaround for local time (#944) 2024-04-10 15:57:46 -04:00
Kristin Aoki
fd6b9ae3a6 fix: referencing activeStatus for undefined (#943) 2024-04-10 14:50:40 -04:00
Kristin Aoki
fdcda9833f feat: include usage locations in delete modal (#938) 2024-04-10 15:34:06 +00:00
Jesper Hodge
74eaaa1f9e chore: change github workflow file to not fail pipeline for codecov (#942) 2024-04-10 11:25:08 -04:00
Rômulo Penido
2adff6e51d feat: add content search modal [FC-0040] (#928)
* feat: Prototype search UI using Instantsearch + Meilisearch
---------

Co-authored-by: Braden MacDonald <braden@opencraft.com>
2024-04-08 21:39:15 +05:30
Dmytro
5634e9e507 fix: incorrect redirect link to Pages&Resources from Custom Pages (#916)
Co-authored-by: Dima Alipov <dimaalipov@MacBook-Pro-Dima.local>
2024-04-08 10:03:01 -04:00
Braden MacDonald
ced2c0e891 Fix the TaxonomyMenu test, which wasn't getting run at all (#930)
* test: fix the TaxonomyMenu test, which wasn't getting run at all
Co-authored-by: Rômulo Penido <romulo@opencraft.com>
2024-04-08 13:04:40 +05:30
Rômulo Penido
99a144a869 fix: message case (#924) 2024-04-06 09:00:27 +05:30
Yusuf Musleh
50d2577353 feat: Put Taxonomies tab behind flag (#937) 2024-04-05 12:01:52 -04:00
renovate[bot]
7f3164bbd7 fix(deps): update dependency @edx/frontend-lib-content-components to v2.1.6 (#935)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-04 20:24:38 +00:00
dependabot[bot]
3c64eb75aa chore(deps): bump axios from 0.27.2 to 0.28.1 (#936)
Bumps [axios](https://github.com/axios/axios) from 0.27.2 to 0.28.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.28.1/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.2...v0.28.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 20:12:36 +00:00
dependabot[bot]
3c74cd23b2 chore(deps-dev): bump axios from 0.27.2 to 0.28.0 (#848)
Bumps [axios](https://github.com/axios/axios) from 0.27.2 to 0.28.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.28.0/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.2...v0.28.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 16:00:55 -04:00
Kyr
e306b62dd1 feat: [FC-0044] Certificates page (#872)
* feat: [FC-0044]  Certificates page

* feat: add descriptions for details, signatories, sidebar i18n messages

---------

Co-authored-by: Kyrylo Hudym-Levkovych <kyr.hudym@kyrs-MacBook-Pro.local>
2024-04-04 13:28:04 -04:00
Jesper Hodge
dedcb14386 fix: trigger pipeline release for dependency fixes (#472) 2024-04-04 13:26:46 -04:00
dependabot[bot]
cf46e6c6c9 build(deps-dev): bump follow-redirects from 1.15.5 to 1.15.6 (#471)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 13:04:17 -04:00
dependabot[bot]
77cdf2614e build(deps): bump express and @openedx/frontend-build (#470)
Bumps [express](https://github.com/expressjs/express) to 4.19.2 and updates ancestor dependency [@openedx/frontend-build](https://github.com/openedx/frontend-build). These dependencies need to be updated together.


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

Updates `@openedx/frontend-build` from 13.0.27 to 13.1.0
- [Release notes](https://github.com/openedx/frontend-build/releases)
- [Commits](https://github.com/openedx/frontend-build/compare/v13.0.27...v13.1.0)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
- dependency-name: "@openedx/frontend-build"
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 13:03:56 -04:00
dependabot[bot]
fd3c871585 build(deps): bump follow-redirects from 1.15.5 to 1.15.6 in /www (#469)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 13:02:58 -04:00
dependabot[bot]
2bf04b8be6 build(deps-dev): bump webpack-dev-middleware from 5.3.3 to 5.3.4 (#468)
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 12:56:01 -04:00
dependabot[bot]
b61cb5c7cd chore(deps): bump express and @openedx/frontend-build (#931)
Bumps [express](https://github.com/expressjs/express) to 4.19.2 and updates ancestor dependency [@openedx/frontend-build](https://github.com/openedx/frontend-build). These dependencies need to be updated together.


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

Updates `@openedx/frontend-build` from 13.0.27 to 13.1.0
- [Release notes](https://github.com/openedx/frontend-build/releases)
- [Commits](https://github.com/openedx/frontend-build/compare/v13.0.27...v13.1.0)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
- dependency-name: "@openedx/frontend-build"
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 12:55:10 -04:00
dependabot[bot]
4ba62d7ee4 build(deps): bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /www (#467)
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 12:52:44 -04:00
dependabot[bot]
0b1505975b chore(deps): bump follow-redirects from 1.15.5 to 1.15.6 (#902)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 12:50:41 -04:00
dependabot[bot]
a9d33f612c chore(deps): bump webpack-dev-middleware from 5.3.3 to 5.3.4 (#917)
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 12:42:54 -04:00
Jesper Hodge
57d2fea5fd fix: set feature flag to default to false for pagination (#934) 2024-04-04 13:53:17 +00:00
Ihor Romaniuk
ffa0f14693 feat: [FC-0044] Unit page - display component support label (#913)
* feat: [FC-0044] Unit page - display component support label

* fix: add message description for tooltip of module support
2024-04-04 09:28:39 -04:00
Yusuf Musleh
806591f1cc feat: Add taxonomies tab in home page (#923) 2024-04-04 17:34:13 +05:30
Jhon Vente
fde3872e2e feat: pagination studio home for courses (#825)
This PR adds pagination for the studio home view and makes minor changes to each course card.

NOTE: This needs to be activated by the environment variable ENABLE_HOME_PAGE_COURSE_API_V2 otherwise, it will continue using the old course list

enable this feature flag
new_studio_mfe.use_new_home_page

* feat: pagination studio home for courses

* chore: addressing some comments

* refactor: addressing pr comments

* test: adding test for studio home slice

* chore: deleting unnecessary blank line

* feat: adding feature for pagination

* refactor: change customParams to requestParams

* fix: linter problems

* fix: course home number of 0 courses

* chore: update feature name for pagination

* fix: pagination enabled request and test for tab section added again

* chore: removing cms link in course card items

* chore: addresing some comments

* fix: array dependency for pagination
2024-04-03 11:38:05 -04:00
Ihor Romaniuk
5247ec5022 feat: [FC-0044] Unit page - make xblock edit functional (#912) 2024-04-02 16:15:09 -04:00
Kristin Aoki
dd13ed49aa feat: upgrade frontend-lib-content-components (#927) 2024-04-02 09:35:29 -04:00
Rômulo Penido
0a50bbc9ef fix: content tags drawer width (#910) 2024-04-02 12:40:14 +05:30
Raymond Zhou
d44edb84a0 feat: send content-length in video (#925) 2024-03-28 15:05:08 -04:00
Kristin Aoki
905bea0d59 fix: default setting not updating with updated default settings (#466) 2024-03-28 15:00:16 -04:00
Rômulo Penido
f57d40ea34 feat: tag sections, subsections, and the whole course (FC-0053) (#879)
* feat: tag sections, subsections, and the whole course
* docs: add comments to useContentTagsCount
2024-03-28 17:44:29 +05:30
Kristin Aoki
80bf86992d fix: transcript and thumbnail uploads (#914)
* fix: transcript and thumbnail uploads

* chore: add missing tests
2024-03-25 10:02:09 -04:00
Yusuf Musleh
1dde30a0a2 [FC-0036] feat: Make tags widget keyboard accessible (#900)
Adds the ability to navigate the new "Add Tags" widget using the keyboard, making it fully accessible through the keyboard.
2024-03-21 17:26:22 +05:30
Braden MacDonald
9a6e12bd3b Clean up Taxonomy API files/hooks/queries [FC-0036] (#850)
* chore: rename apiHooks.jsx to apihooks.js

* refactor: consolidate taxonomy API code

* fix: was not invalidating tags after import

* fix: UI was freezing while computing plan for large import files
2024-03-20 09:31:10 +05:30
Kristin Aoki
784a811ff8 Revert "feat: show alerts related files included via paste unit (#847)" (#909)
This reverts commit 071aee5b02.
2024-03-19 13:34:33 -04:00
Navin Karkera
071aee5b02 feat: show alerts related files included via paste unit (#847)
* fix: block highlight and status for unscheduled course

* feat: show alerts related to files after pasting unit

* refactor: rename paste notices and add view files option to alert

* refactor: remove additional visibility state check

* refactor: page alert message
2024-03-19 10:27:18 -04:00
Navin Karkera
da68fb8e9d feat: allow dragging blocks across parents in outline (#859) 2024-03-19 11:25:02 -03:00
Kristin Aoki
972a7f324c feat: upgrade frontend-lib-content-components (#906) 2024-03-18 16:44:07 -04:00
Kristin Aoki
4fa169556e fix: remove useEffect that fires before search params (#465)
* fix: remove useEffect that fires before search params

* fix: remove disable lint lines
2024-03-18 15:31:00 -04:00
Kristin Aoki
c809dfb2e4 feat: add pr template (#905) 2024-03-18 15:02:08 -04:00
Samir Sabri
1b2c65fae6 feat!: remove Transifex calls for OEP-58 (#627) 2024-03-18 15:01:20 -04:00
connorhaugh
b09729b55e fix: add new videos to the front of the videos list (#904) 2024-03-18 12:58:59 -04:00
connorhaugh
60917c6ab5 Feat: refactor upload thunk, no progress bar pt 2 (#899)
Attempts to fix: https://2u-internal.atlassian.net/browse/TNL-1141
2024-03-18 11:07:13 -04:00
Chris Chávez
d57ecc6779 [FC-0036] Tags Sidebar (#852)
* refactor: Unit sidebar to create the TagsSidebar

* feat: Structure of TagsSidebar and TagsTree

* feat: Adding styles to the TagsTree

* feat: TagsSidebarHeader created

* feat: Add count on TagsSidebarHeader

* test: Tests for new components added

* style: Update tags count with opacity when the count is zero

* refactor: Extract tag count component as generic

* refactor: Transform Sidebar to a wrapper component

---------

Co-authored-by: Rômulo Penido <romulo@opencraft.com>
2024-03-15 21:59:28 +05:30
connorhaugh
6ae9cdac00 Revert "feat: refactor upload thunk, no progress bar (#894)" (#895)
This reverts commit a88a88e9af.
2024-03-13 14:08:18 -04:00
Kristin Aoki
9740974bbd feat: add duplicate file validation for asset upload (#885)
* feat: add duplicate file validation for asset upload

* fix: modal only appearing once

* feat: add tests for overwrite modal

* fix: input not allowing second upload of same file

* fix: default pageSize for asset details
2024-03-13 12:09:00 -04:00
connorhaugh
a88a88e9af feat: refactor upload thunk, no progress bar (#894)
* feat: add progress bar for video uploads and refactor

---------

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>
2024-03-13 11:50:23 -04:00
connorhaugh
6baec5b6a3 Revert "feat: add progress bar for video uploads and refactor (#860)" (#893)
This reverts commit d76aaa73a4.
2024-03-13 10:18:05 -04:00
Peter Kulko
8acd27d7bf fix: replaced the LMS endpoint for navigating the course unit page
fix: [AXIMST-424] Course unit - Fixed network connection behavior (#138)

* fix: [AXIMST-424] fixed network connetcion behavior

* fix: added placeholder for unsuccessful loading for the page

* refactor: code refactoring
2024-03-13 10:56:10 -03:00
connorhaugh
d76aaa73a4 feat: add progress bar for video uploads and refactor (#860)
* feat: add progress bar for video uploads and refactor

---------

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>
2024-03-13 09:37:55 -04:00
Yusuf Musleh
4e70813fa9 [FC-0036] feat: New "Add Tags" widget (#834)
* feat: Use react-select for tags selector

Replace existing component with react-select component, by passing in
our custom component.
This retained the existing search functionality.

* fix: Fix missing deps causing constant rerender

This bug appeared after removing the react-query call to the backend
when selecting/unselecting a tag in the dropdown. Since it no longer
gets the updated state from the backend, it doesnt mask the bug.

The bug is essentially the `ContentTagsCollapsibleHelper` rerendering
causing the states to reset overriding the selected (not commited) tags.
This is due to missing dependancies in the useCallback.

* feat: Add stagedContentTags state in react-select

This adds a state and callbacks in the toplevel component of the content
tags drawer to be able to add/remove staged content tags and have them
showup in the react-select as selected chips.

* feat: Split up applied & staged content tags trees

Now content tags have seperate tree states for applied ones and staged
ones. They are updated seperately and both are used when updating the
selectable box UI. This allows for more flexibility with actions that
can be performed on the staged content tags with impacting the applied
ones.

* feat: Change style of implicit checkbox to checks

This overrides the indeterminate input checkbox style to match the
checked checkbox style, using variables defined in paragon.

* feat: Add bottom buttons in tags dropdown selector

* refactor: Remove cloneDeep + simplify code

* feat: Update placeholder/button texts

* feat: Implement cancel button + add proptypes

* feat: Implement commit/cancel staged tags

This implements the commit functionality for staged tags, taking account
for implicit tags. This also handles the case for removing applied tags
by clicking on the "x" in the TagBubble.

* feat: Keep all staged tags only commit explicit

* feat: Change style of add/cancel/load more buttons

* feat: Add inline "Add" button to commit tags

In the react-select component, an inline "Add" button showsup when some
tags are staged, if they are clicked they are commited/applied.

* fix: Keep applied tag checked when only staged child unchecked

* feat: Style add tags widget + staged tags

Also clear search term whenever tags are staged/cancelled

* feat: Fixed some typing errors

* test: Update tests to fix existing broken cases

* test: Add new functionality tests

* chore: add types to ContentTagsCollapsible

* chore: add types for useContentTagsCollapsibleHelper

* fix: Small bug with useIntl

* chore: Fix more linter issues

* refactor: Separate stagedTags and stagedTagsTree state updates

This refactor removed the warning that was caused because the state of a
parent component (ContentTagsDrawer) was being updated in the middle of
a state update in (ContentTagsCollapsible). This seperated the two state
updates to avoid this issue.

* chore: Update package-lock.json

* fix: Reset applied tags in selectbox when fetching

Whenever we get new applied tags from the backend, we reset the applied
tags that are checked, and only check the explicit tags. This was
causing an issue of duplicate applied tags being added to the selectbox.

* chore: Update package.json

---------

Co-authored-by: Braden MacDonald <braden@opencraft.com>
2024-03-13 18:57:30 +05:30
Maria Grimaldi
7f5687f175 refactor: address PR reviews 2024-03-12 16:42:11 -03:00
Maria Grimaldi
c8434b87c0 fix: import grouptypes from correct file 2024-03-12 16:42:11 -03:00
Maria Grimaldi
9299f4cf93 fix: import grouptypes from correct file 2024-03-12 16:42:11 -03:00
Maria Grimaldi
59d2dcaacb refactor!: put open manage team behind a configuration flag 2024-03-12 16:42:11 -03:00
Cristhian Garcia
42f8c3d95f chore: fix typo 2024-03-12 16:42:11 -03:00
Cristhian Garcia
9ff77945e3 chore: update open managed description 2024-03-12 16:42:11 -03:00
Cristhian Garcia
584823b879 fix: reorder groups 2024-03-12 16:42:11 -03:00
Cristhian Garcia
6e83e90cf0 feat: add open managed group type 2024-03-12 16:42:11 -03:00
Jeremy Ristau
ad7ba2f302 chore: update CODEOWNERS with new team (#888) 2024-03-12 11:57:23 -04:00
Ihor Romaniuk
bec59e5bbe feat: [FC-0044] Unit page - display xblock components (#857) 2024-03-12 11:48:11 -04:00
Jeremy Ristau
85ca350591 Merge pull request #463 from openedx/gh-team-update
chore: Github team update
2024-03-12 09:34:04 -04:00
Jeremy Ristau
d8503fbfe2 chore: update CODEOWNERS 2024-03-11 22:33:21 -04:00
Jeremy Ristau
9a8deced9b chore: update CODEOWNERS 2024-03-11 22:32:32 -04:00
Jeremy Ristau
1797707a9a chore: update openedx.yaml 2024-03-11 22:32:01 -04:00
Jesper Hodge
1fdddfb869 Fix replace broken selectable box component everywhere (#887)
The Configure Live modal in Pages & Resources page uses a selectable box to select the video conferencing tool. It seems broken as well (not selectable).

It looks like the bug with not working SelectableBox (see e.g. #886) affects pretty much any component that uses it.

Thus, this PR replaces every usage of the paragon component with our working copy from flcc.
2024-03-11 17:10:33 -04:00
Jesper Hodge
b5a287639d Fix SelectableBox problems (#886)
Due to a bug in the SelectableBox component, selecting values was not possible in different components throughout this MFE.

This fixes the Gallery and the Select Problem Types components by updating the FLCC version and replacing the SelectableBox copy with an import from FLCC.

For a full bug description see #880.
2024-03-11 17:09:58 -04:00
Jesper Hodge
7f73b895d1 fix: gallery (#462) 2024-03-11 15:43:35 -04:00
Jesper Hodge
679e15ed00 fix: gallery 2024-03-11 19:39:18 +00:00
Jesper Hodge
fb2a79985e Fix: selectable box in gallery (#461)
This fixes a bug where images and videos are not clickable when using the Gallery component.
Please see https://github.com/openedx/frontend-lib-content-components/pull/460 for a full description of the bug.
2024-03-11 15:26:54 -04:00
Jesper Hodge
b3b2881efb Revert "fix: gallery"
This reverts commit 736a786e12.
2024-03-11 18:42:17 +00:00
Jesper Hodge
736a786e12 fix: gallery 2024-03-11 18:41:17 +00:00
Jesper Hodge
be00028c4a Fix Problem Type Select (#460)
* fix: override selectablebox with copy of version from edx paragon 21.5.6

* chore: remove console logs

* fix: lint

* chore: update snapshots
2024-03-11 12:59:05 -04:00
renovate[bot]
dad4bd5282 fix(deps): update dependency @edx/frontend-component-footer to v13.0.4 (#881)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-11 10:25:27 -04:00
Jesper Hodge
17b1360c07 Fix: SortAndFilter modal doesnt respond to click (#880)
Description
We are encountering a bug in our stage environment that is very hard to reproduce locally, but not impossible. This is the same bug dealt with in several previous PRs like for example #871 (here I'm working on another component that uses the same paragon component and therefore encounters the same bug).

Since I was able to reproduce it locally, it is definitely not just a bug affecting only 2U-specific things.

Expected behavior
open a course with files
select a different sorting order (for example oldest to newest)
you should be able to select the different option
you should be able to successfully apply it
Actual behavior on stage environment
you can't select different option
you can't apply different option
Previous steps
Previously, I reproduced this locally by just adding the latest version of SelectableBox as a copy into this repo and importing it from there. Then, under the mistaken assumption that there was a missing context provider, I added that and it got fixed locally. However it turned out to not work on stage.

Measures taken in this PR
I replaced the entire SelectableBox component with all subcomponents with a version from @edx/paragon@21.5.6, which was apparently the version in the commit that didn't have the error yet. In theory, this would just fix the problem, though I have my doubts. But it's worth a try. I only imported it in one place, in this SortAndFilter modal.
I added console logging for onChange events which seem to the problem, as they are not triggering a change in the value of the context on stage. I see little choice than to log them to get more info. This will only affect the component that's not working.
2024-03-08 17:44:03 -05:00
Chris Chávez
c39b52a6bf [UU-58] Implement tagging & taxonomy feature in outline (#855)
* feat: TagCount component

* feat: Update ContentTagsDrawer to use it in the MFE

* feat: Manage tags menu added on units

* feat: Tag count added on unit

* feat: Add button feat to Tag count

* test: Course Outline api tests

* test: Ignore lines that can not be tested

* style: Comment added on ContentTagsDrawer

* style: Nits on CardHeader
2024-03-08 10:00:51 -05:00
PKulkoRaccoonGang
642b4e4052 refactor: refactoring after review 2024-03-08 08:17:54 -03:00
PKulkoRaccoonGang
423a3f3f72 fix: [AXIMST-470] fixed sidebar status after deleting or duplicating xblock 2024-03-08 08:17:54 -03:00
PKulkoRaccoonGang
f1f036576e refactor: after rebase 2024-03-08 08:17:54 -03:00
PKulkoRaccoonGang
deb76a0609 fix: [AXIMST-473] fixed sidebar publish status 2024-03-08 08:17:54 -03:00
PKulkoRaccoonGang
912c42e802 fix: [AXIMST-472] fixed sidebar visibility notification 2024-03-08 08:17:54 -03:00
PKulkoRaccoonGang
bdd641225f fix: [AXIMST-427] fixed unpublished changes alert 2024-03-08 08:17:54 -03:00
Peter Kulko
9021fccdb7 feat: [AXIMST-25] Course unit - Alert notification about unpublished changes (#135)
* feat: [AXIMST-25] added alert notification about unpublished changes

* feat: added tests

* feat: added translations
2024-03-08 08:17:54 -03:00
Peter Kulko
e4d88fb1fa feat: [AXIMST-24] Course unit - Sidebar buttons functional (#134)
* feat: [AXIMST-24] sidebar buttons functional

* refactor: removed modal extra className

* refactor: refactoring after review
2024-03-08 08:17:54 -03:00
Peter Kulko
073e191273 feat: [AXIMST-23] Course unit - Sidebar with unit info (#117)
* feat: added Sidebar with unit info

* feat: added unit location

* refactor: added legacy behavior

* feat: added live variant

* refactor: code refactoring

* feat: added tests and translations

* feat: added new font size

* refactor: after review
2024-03-08 08:17:54 -03:00
Ihor Romaniuk
f8095e6670 feat: [FC-0044] Unit page - display xblock components 2024-03-08 08:17:54 -03:00
Kristin Aoki
e4c3997d17 fix: revert addition of course-ingestion-tool submodule (#878) 2024-03-07 16:42:46 -05:00
Kristin Aoki
da7fe95f24 fix: accessibility page config reference (#875) 2024-03-07 16:11:04 -05:00
Jesper Hodge
896969c7de Fix radio context provider missing (#874)
This is a temporary fix for a bug that stems from a paragon component, SelectableBox. So we're using our own copy from fronten-lib-content-components.
2024-03-07 15:55:30 -05:00
Jesper Hodge
5069cf8638 fix: problem type not selectable (#459)
JIRA: https://2u-internal.atlassian.net/browse/TNL-11465

This is a problem we're experiencing on stage due to a bug in paragon. This is intended as a temporary fix until we can dig into why this is happening in paragon. But basically the Context for the SelectableBox is missing a provider in paragon. The short-term fix is to copy over paragon's selectablebox component and fix it. This is done so that our quick fix doesn't break anything else in paragon for now or cause any unexpected issues.
2024-03-07 15:21:02 -05:00
Kristin Aoki
8100281fb4 feat: add checklist page (#870)
* feat: add checklist page

* fix: failing tests

* fix: styling bugs

* fix: lint errors

* feat: add test fro CourseChecklist

* fix: lint errors

* feat: add ChecklistSection tests

* fix: lint error

* fix: missing api reply status
2024-03-07 15:20:33 -05:00
Jesper Hodge
f035391c2f fix: replace paragon radio select set with copy to debug (#871)
step 1 for trying fixes for the stage bug where the paragon radio select is not clickable. Here I just replace the paragon component with our identical copy to see what that changes. Followup steps are to change this component until hopefully the problem gets fixed.
2024-03-06 16:26:17 -05:00
Eugene Dyudyunov
3607e6423d fix: correct internal routing
The Content dropdown items have incorrect URLs for the
internal routing when MFEs are deployed using the common
domain and the PUBLIC_PATH.
2024-03-06 10:00:35 -03:00
Jesper Hodge
4395607074 chore: update frontend-lib-content-components to 2.1.0 to fix problem select (#867) 2024-03-05 13:03:07 -05:00
Kristin Aoki
f717cdac86 feat: add accessibility page (#861)
* feat: add accessibility page

* fix: lint errors

* feat: increase code coverage

* fix: lint errors
2024-03-05 12:15:58 -05:00
Jeremy Ristau
40c9d6ee0d chore: update tnl team name (#862) 2024-03-05 09:19:39 -05:00
Jeremy Ristau
4e78c07dac Merge pull request #458 from openedx/catalog-info-updates
chore: update tnl team name
2024-03-05 09:19:33 -05:00
Jeremy Ristau
e5a469f7ea chore: update tnl team name 2024-03-04 21:46:23 -05:00
Braden MacDonald
3c661e15cb Convert "Pages & Resources" page to a plugin system (#638)
* feat: Make "Pages & Resources" course apps into plugins

* feat: move ora_settings

* feat: move proctoring

* feat: move progress

* feat: move teams

* feat: move wiki

* feat: move Xpert settings

* fix: add webpack.prod.config.js

* fix: clean up unused parts of package.json files

* feat: Add an error message when displaying a Course App Plugin fails

* chore: fix various eslint warnings

* chore: fix jest tests

* fix: error preventing "npm ci" from working

* feat: better tests for <SettingsComponent>

* chore: move xpert_unit_summary into same dir as other plugins

* fix: eslint-import-resolver-webpack is a dev dependency

* chore: move learning_assistant to be a plugin too

* feat: for compatibility, install 2U plugins by default

* fix: bug with learning_assistant package.json
2024-02-28 11:50:54 -05:00
PKulkoRaccoonGang
49fce4622c refactor: tests refactoring 2024-02-27 14:50:06 -03:00
Chris Chávez
608b2f79f8 [FC-0036] Refined taxonomy details page (#833)
* UX refinements on tag list table
* Add page size to tag list table
* fix Datatable pagination
2024-02-27 21:02:06 +05:30
PKulkoRaccoonGang
6b57ce3e53 refactor: refactoring after review 2024-02-27 11:44:42 -03:00
Peter Kulko
6aff1c1168 feat: [AXIMST-19, 20, 22] Course unit - Modal windows for course unit page components (#118)
* feat: added modal windows for course unit page components

* refactor: code refactoring

* refactor: added translations

* refactor: refactoring after review

* refactor: after review
2024-02-27 11:44:42 -03:00
Ihor Romaniuk
2b11df9eb5 fix: [AXIMST-371] fix correct internal route on create new unit (#114) 2024-02-27 11:44:42 -03:00
Peter Kulko
7fcc501d2e feat: Unit creation button logic and refactoring 2024-02-27 11:44:42 -03:00
Jeremy Ristau
90fb3d8edc chore: add missing maintainership files (#840)
* chore: add catalog-info file for Open edX Backstage

* chore: Create CODEOWNERS
2024-02-27 06:41:21 -05:00
Rômulo Penido
0fc0ce0829 feat: add export course tags menu (#830)
This change adds an item in the Tools menu to export the course tags to a CSV file.
2024-02-21 12:50:40 +05:30
Braden MacDonald
16d2f38325 Display full descendant count on taxonomy tag list page [FC-0036] (#826) 2024-02-20 15:40:30 +05:30
Jhon Vente
90d5ac4ffc feat: TinyMCE plugin insert iframe (#427) 2024-02-16 14:02:34 -05:00
Brian Smith
76bb8e88c1 chore(deps): update paragon and frontend-build to openedx scope 2024-02-16 13:40:03 -03:00
Ihor Romaniuk
51c5f9c4dc refactor: Unit page - refactoring breadcrumbs, view live and preview links buttons (#827) 2024-02-14 13:38:54 -05:00
Navin Karkera
60c1a0343c feat: proctoring & prerequisite settings and page alerts (#816)
* feat: add proctoring exam link to actions

* feat: prerequisite settings in advanced tab

* refactor: use formik for configuration modal in outline

* feat: proctoring exam settings in subsection configuration

test: prereq & proctoring settings

* feat: outline alerts

test: outline page alerts

* refactor: replace highlights badge with bubble

* feat: discussion badge in outline

* refactor: status bar style and date format

* Fix spacing between checklist and highlights button
* Fix title alignment in status bar
* Align learn more link to center with respect to button
* Update start date display in local format

* fix: unit url

* refactor: redesign item header

* move status to end of card
* move edit icon next to title
* make it visible on hover

* test: improve coverage

* refactor: update messages and alert colors
2024-02-13 16:32:32 -05:00
Brian Smith
f33a3b5521 fix(deps)!: support paragon and frontend-build in openedx scope (#457)
BREAKING CHANGE: frontend-platform peer dependency updated to ^7.0.1
BREAKING CHANGE: @edx/paragon peer dependency updated to @openedx/paragon
2024-02-09 14:41:16 -05:00
Ihor Romaniuk
1555e9f88e feat: [FC-0044] Unit page - add new component section (#828)
* feat: Course unit - add new component section

* feat: Course unit - make Discussion and Drag-and-Drop button functional

* feat: Course unit - make Problem button functional

* feat: Unit page - make Video button functional
2024-02-09 14:27:00 -05:00
Chris Chávez
3938015aaa feat: Add export ID on Taxonomy details and import new Taxonomy (#814)
Adds a new prompt on the import new taxonomy workflow to enter the export_id, and adds the export_id on the Taxonomy page details.

Implements  modular-learning#183 '[Tagging] An "Export ID" identifies each Taxonomy'
2024-02-09 09:27:17 +05:30
Feanil Patel
62bff35fcd Merge pull request #381 from Mashal-m/mashal-m/update_lockfile
refactor: updated lock file version check to use new workflow
2024-02-08 14:30:16 -05:00
Rômulo Penido
a318c322b2 fix: revert code due to wrong merge conflict resolution (#824) 2024-02-08 21:48:07 +05:30
Peter Kulko
b234344aab feat: [FC-0044] Course unit page - Unit switch widget with a New unit creation button (#809)
* feat: added Unit switch widget with a New unit button

* refactor: refactoring after review

* refactor: changed the variable name
2024-02-08 10:15:21 -05:00
Adolfo R. Brandes
4850302175 fix: Runtime config support for feature flags
This makes sure the following feature flags work with dynamic runtime
configuration:

* ENABLE_NEW_EDITOR_PAGES
* ENABLE_UNIT_PAGE
* ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN
* ENABLE_TAGGING_TAXONOMY_PAGES

We also remove flags from the `.env*` files that are no longer in use.
2024-02-06 16:05:28 -03:00
Navin Karkera
815ddbe94e feat: copy & paste units
refactor: paste component

fix: lint issues and delete unused hook

test: add test

fix: update api for npm broadcast channel
2024-02-05 14:01:38 -05:00
Navin Karkera
2cb907e731 feat: xblock status component
feat: add custom relative dates flag to state

refactor: add gated status type

refactor: alert style

feat: add status text to units

test: add tests

fix: lint issues

refactor: break up xblock status component

fix: selector for isCustomRelativeDatesActive

fix: prereq default value
2024-02-05 14:01:38 -05:00
Ihor Romaniuk
9c52b8b6c5 feat: [FC-0044] Unit page header section (#808)
* feat: create Unit page and add page header functionality

* fix: after code review

---------

Co-authored-by: monteri <lansevermore>
2024-02-05 11:58:35 -05:00
Omar Al-Ithawi
056a15bedb feat: tutor-mfe compatiblilty for atlas pull (#817)
- install atlas
 - remove `--filter` to pull all languages by default
 - use ATLAS_OPTIONS to allow custom `--filter`
 - include frontend-platform, ai-translations and lib-contents in `atlas pull` command

Refs: FC-0012 OEP-58
2024-02-02 14:16:01 -05:00
Kristin Aoki
18537e3f62 fix: model update for usage locations (#819) 2024-02-02 11:21:44 -05:00
Rômulo Penido
24c48bc3ea feat: add search highlight/expand and "no tags" message (#799)
This change makes minor improvements in the search taxonomy UI.  It expands taxonomies that match the search and highlight the search term, and adds a "No tag found with search term '....'" message.
2024-02-02 20:39:30 +05:30
Jillian
49d4fd44a3 fix: fixed typo in updateContentTaxonomyTags URL [FC-0036] (#815)
* fix: fixed typo in updateContentTaxonomyTags URL
* fix: use params instead of urlencoding
2024-02-02 12:00:04 +05:30
Braden MacDonald
c7aef6e467 fix: minor TypeScript error - not sure how it got onto master 2024-02-01 01:43:48 +05:30
Braden MacDonald
d6338de8bc docs: fix incorrect waffle flag stated. 2024-02-01 01:43:48 +05:30
Jillian
b56b5d9b16 Use object permissions in Tagging frontend [FC-0036] (#787)
Uses the permissions added to the Tagging REST API by openedx/openedx-learning#138 to decide what actions (e.g. import, export, edit, delete) to present to the current user when viewing Tagging-related content.
2024-01-29 14:47:31 +05:30
Kristin Aoki
90bc242ddd fix: update video page load order (#810)
* fix: asset card/row menu appearing for videos

* fix: page load time

* fix: video status messages
2024-01-25 17:04:34 -05:00
dependabot[bot]
f8aa157c93 chore(deps): bump follow-redirects from 1.15.1 to 1.15.5 (#806)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.1 to 1.15.5.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.1...v1.15.5)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-24 17:19:11 -05:00
Syed Ali Abbas Zaidi
34fbadfd6a feat: migrate enzyme to RTL (#770) 2024-01-24 16:57:28 -05:00
Syed Ali Abbas Zaidi
dcdaace08d feat: migrate enzyme to edx react-unit-test-utils 2024-01-24 16:51:53 -05:00
Michael Roytman
6d431e5746 Add settings modal for Xpert Learning Assistant feature. (#794)
* feat: modify AppSettingsModal to add bodyChildren prop and to make the learnMoreText prop optional

This commit adds a new bodyChildren prop to the AppSettingsModal component. This prop is meant to be used by a parent to pass through React components that should be rendered between the enable toggle and the form. This allows parents to specify additional UI that doesn't belong in the form. For example, additional documentation about the feature or additional links are examples of additional UI that can be rendered this way.

This commit modifies the learnMoreText prop to the AppSettingsModal component optional. The learnMoreText prop is used as the text for the "learn more configuration" link. This link is rendered only if the corresponding documentationLink is provided, and this link is optional. Therefore, the corresponding learnMoreText prop should also be optional.

* feat: modify PagesAndResources to support additional pages in the "content permissions" section

This commit modifies the way that the PagesAndResources component renders pages in the "content permissions" section to enable additional pages in this section beyond just the Xpert unit summaries feature.

* feat: add settings modal for Xpert Learning Assistant feature

This commit adds a settings modal for the Xpert Learning Assistant feature.
2024-01-24 12:00:17 -05:00
Jorg Are
9e06065fd3 feat: replace ai translations edx component with the openedx version (#803) 2024-01-24 17:48:49 +01:00
Navin Karkera
09eef604f7 refactor: replace time picker by text box in advanced tab 2024-01-24 09:50:16 -05:00
Navin Karkera
5a2dbad343 test: improve coverage for section, subsection & unit configuration 2024-01-24 09:50:16 -05:00
Moncef Abboud
13cb1d3539 feat: add unit configuration modal 2024-01-24 09:50:16 -05:00
Navin Karkera
5a27d50d2a refactor: use time picker in advanced tab
fixes issues related to form autosuggest

fix: hide header only for advanced tab time picker
2024-01-24 09:50:16 -05:00
Stephannie Jimenez
ffec32cba8 feat: add subsection configuration modal
test: add render and API tests

fix: fix non saving options and add review style changes

fix: remove additional tab in the section configuration

fix: remove isSubsection state, fix css issues and fix tests

fix: add review changes, fix advanced tab hour selection and update tests

test: fix failing test in courseOutline.test.jsx

fix: remove unused state, add TODO comment, fix stack rendering and NaN values

feat: show previous state in autosuggest if an invalid option is provided and fix warnings

test: fix failing test
2024-01-24 09:50:16 -05:00
Navin Karkera
53118a4e0b feat: move up & down menu action for sections, subsections & units
test: add tests for move options

refactor: disable move option instead of hiding

fix: incorrect variable name in tests

feat: move up & down menu action for units

test: add tests for unit move options
2024-01-24 09:50:16 -05:00
Sid Verma
d2f63b8b16 feat: Add drag-n-drop support to course unit, refactor tests.
chore: address review feedback
2024-01-24 09:50:16 -05:00
Navin Karkera
0e829974ef feat: add colored left border to items in course outline
refactor: move common styles to conditional sortable element
2024-01-24 09:50:16 -05:00
Navin Karkera
eb0c61ce6d refactor: course outline badge status logic 2024-01-24 09:50:16 -05:00
Navin Karkera
b417cd64a0 feat: use actions and other flags to control item actions
Uses action flags from API to control display of delete, duplicate, child new button and dragging.
Use isHeaderVisible flag to control display of subsection headers.

All these changes prepare outline for entrance exam section display.

feat: use actions flags for subsections

test: actions
2024-01-24 09:50:16 -05:00
Sid Verma
70b4795650 feat: Add drag and drop support to subsections
feat: Update tests, fix bugs in drag and drop elements

chore: address review feedback
2024-01-24 09:50:16 -05:00
Rômulo Penido
3842b046cd fix: minor react errors in course authoring mfe [FC-0036] (#789)
* fix: remove console warnings and add missing typing checks
* fix: TagData <> TagListData swap names
* fix: toast needs show property
* fix: remove type guard from tagsCount
* fix: apply suggestions from code review
Co-authored-by: Jillian <jill@opencraft.com>
2024-01-24 16:59:33 +05:30
Raymond Zhou
c2ad1b8c99 fix: error handling for save api call (#805) 2024-01-23 15:55:24 -05:00
Kristin Aoki
bdb4ffe69d fix: gallery card text and load (#795) 2024-01-16 17:06:50 -05:00
Kristin Aoki
0a053d32ce feat: add missing transcript setting update date (#793) 2024-01-16 16:32:37 -05:00
Kristin Aoki
859819f0f0 feat: bump frontend-lib-content-components (#798) 2024-01-16 16:15:48 -05:00
Kristin Aoki
1bc4e51c22 fix: url for video uploads (#453)
* fix: url for video uploads

* fix: possible undefined error in postUrlRedirect
2024-01-16 11:00:43 -05:00
Navin Karkera
008d619236 feat: video sharing option dropdown (#779)
* feat: video sharing option dropdown

* test: video sharing option

* fix: lint issues

* refactor: messages for video sharing options

* test: add failure test for video sharing

* refactor: rename course block api url
2024-01-16 10:50:16 -05:00
Rômulo Penido
b59ecafc83 feat: refined ux update a taxonomy by downloading and uploading [FC-0036] (#732)
This PR improves the import tags functionality for existing taxonomies implemented at #675.

Co-authored-by: Jillian <jill@opencraft.com>
Co-authored-by: Braden MacDonald <mail@bradenm.com>
2024-01-16 12:00:15 +05:30
Rômulo Penido
1fef358f55 feat: assign taxonomy to organizations [FC-0036] (#760)
This PR adds a UI to assign organizations to a Taxonomy.

Co-authored-by: Jillian <jill@opencraft.com>
2024-01-12 13:10:56 +05:30
Kristin Aoki
bfcd3e6ff9 fix: mov files not being allowed on video upload (#792) 2024-01-11 12:19:09 -05:00
Kristin Aoki
433a87795c feat: bump frontend-lib-content-components (#791) 2024-01-11 10:18:26 -05:00
connorhaugh
a3975f47e2 fix: dont allow course detail page to break editor (#790)
At the moment, editors served from V1 libraries are broken because they use the course authoring MFE url (because they use editors in the same way courses do).
2024-01-11 09:44:29 -05:00
Kristin Aoki
4653322fca fix: video upload api fetch body (#454) 2024-01-10 17:13:29 -05:00
Jesper Hodge
0debaecad6 fix: export page timestamp (#785)
* fix: export page timestamp

* fix: tests
2024-01-10 16:59:50 -05:00
Kristin Aoki
97da4d1d61 fix: video upload api body and error control (#782) 2024-01-10 10:29:33 -05:00
Navin Karkera
faf90d1fa7 feat: unit list
refactor: hide tooltip based on arg

refactor: card header to include title link

feat: delete unit option

feat: duplicate unit option

refactor: title click handler name and remove unwanted scss properties

test: new unit and edit unit option

test: add delete unit and combine it with section and subsection test

test: add duplicate unit test and combine it with section & subsection test

refactor: replace act call by oneline

test: add publish unit & subsection test and combine it with section test

refactor: add jest-expect-message to add custom msg to tests

fix: lint issues

test: fix unit card tests

refactor: remove unnecessary css and message

refactor: pass title as component to card header

refactor: extract status badge to a component

fix: lint issues

refactor: rename status badge component

test: fix card header tests

refactor: new item button styling

feat: show loading spinner while sections are loading

refactor: new button style
2024-01-10 09:38:23 -05:00
Braden MacDonald
1e23ce1062 fix: react-router basepath was not set, breaking this MFE on tutor instances (#784) 2024-01-10 18:59:50 +05:30
kenclary
9ad192054b fix: configure webpack to fix paid powerpaste plugin (#783) 2024-01-09 16:15:34 -05:00
Kristin Aoki
bee3758d18 feat: update usage location call (#764) 2024-01-09 12:18:03 -05:00
Kristin Aoki
cae7f9bc22 feat: bump frontend-lib-content-components (#780) 2024-01-08 14:28:37 -05:00
Yusuf Musleh
138f1d29df feat: Add v2 lib components content tags support (#771)
Add support for fetching content data for Library V2 components in content tags drawer.
2024-01-08 15:12:32 +05:30
Rômulo Penido
6c0fc09075 feat: add import taxonomy feature [FC-0036] (#675)
This change adds a new button in the Taxonomy List to allow users to create new taxonomies by importing a CSV/JSON file.
2024-01-08 12:38:03 +05:30
Kristin Aoki
13f039ae4c feat: add blockId to return url (#442) 2024-01-05 09:30:35 -05:00
ayesha waris
2205506b26 chore: removed reported_content_email_notifications_flag dependency (#775) 2024-01-05 17:01:18 +05:00
Jesper Hodge
8833f7bfca fix: library editor not working locally (#448)
Internal issue: https://2u-internal.atlassian.net/browse/TNL-11299

In library-authoring, content blocks could not be edited locally due to some problem with the Accept header. This caused some 404s. Visually you would get an infinite loading spinner.

This depends on openedx/frontend-app-library-authoring#399.
2024-01-04 14:36:07 -05:00
Artur Gaspar
2e070c9a12 feat: error page on invalid course key (#761) 2024-01-04 10:28:19 -05:00
Kristin Aoki
52b75e0b06 fix: uploading progress percentage (#763) 2024-01-04 09:36:17 -05:00
Yusuf Musleh
278862127b feat: Add filter taxonomies by org (#755)
This implements filtering taxonomies on the taxonomy list page by selecting organization name, all taxonomies, or unassigned taxonomies.
2024-01-04 17:50:32 +05:30
Jesper Hodge
70581c54ab refactor: abstract XML parser output operations (#445)
This is a refactoring for a part of the ReactStateXMLParser. I wanted to use functions that are more generic and not just handle a list of edge cases. So I encapsulated the operation that was done in this part of the code to a function `findNodesAndRemoveTheirParentNodes` which is more generic and could be used for different operations.
2024-01-02 15:04:03 -05:00
renovate[bot]
4ffebdac77 fix(deps): update dependency @edx/frontend-lib-content-components to v1.177.9 (#762)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-21 11:37:04 -05:00
kenclary
69452344d8 Merge pull request #447 from openedx/kenclary/plugins
fix: update public plugins repo version
2023-12-21 11:13:10 -05:00
Ken Clary
8fdb395680 fix: update public plugins repo version 2023-12-21 11:09:29 -05:00
Jesper Hodge
782faddbf8 chore: update flcc to version 1.177.8 (#759) 2023-12-20 17:52:16 -05:00
Jesper Hodge
e77d3d1014 fix: do comment change to trigger release (#446) 2023-12-20 17:23:46 -05:00
Jesper Hodge
50c580cca2 fix editor deleting description (#444)
internal issue: https://2u-internal.atlassian.net/browse/TNL-11311

This fixes a bug where the editor was deleting the OLX tag when editing in the simple editor and then saving.
Also the description was being converted to em for the simple editor, but then not converted back. However, the xblock renders label and then em in reverse order for some reason.
To fix it, the em gets converted back to description now, but not for every em tag (added a class "olx_description" for the tags that should be converted).
2023-12-20 16:44:27 -05:00
Navin Karkera
df532b36ab test: improve coverage for course outline
refactor: remove delete unit hook and thunk till unit list is implemented

test: additional tests for sections

test: additional tests for subsections

test: replace query calls by button clicks
2023-12-20 10:20:48 -05:00
Navin Karkera
b0cb53ab44 refactor: reuse drag-n-drop component from lib-components
refactor: subsection and drag component style

refactor: subsection styling

refactor: generalize message ids for card header
2023-12-20 10:20:48 -05:00
Navin Karkera
580b8cbdb4 fix: handle scrolling with drag-n-drop
test: update tests

fix: scroll to element only when required

test: fix subsection component render

refactor: use textarea for highlights
2023-12-20 10:20:48 -05:00
Stephannie Jimenez
48ab324100 feat: add drag n drop functionality to section cards
feat: use react-dnd library for drag and drop implementation

style: fix linting issues

fix: finalize section order on drop instead of hover

fix: prevent same index drag to start request and restore state on error

fix: restore sectionlist order

fix: prevent drag event while editing the text

style: fix linting issues

test: fix failing tests

test: add missing hooks to SectionCard component in test

test: add wrapping to SectionCard test component

test: add tests for checking the API that sets the ordering

fix: merge scroll-to-bottom with drag and drop implementations

fix: fix linting issues
2023-12-20 10:20:48 -05:00
Navin Karkera
f79bebceeb feat: add subsection component
refactor: update publish modal to handle subsections and units

refactor: rename current section state and handlers

refactor: generalize edit title for section, subsection and unit

feat: generalize delete modal

feat: generalize publish modal

refactor: use currentSection and currentSubsection to improve delete item

feat: generalize duplication functionality

feat: generalize add new item for sections and subsections

test: fix subsection tests

fix: lint issues and test arguments

test: fix card header, delete and publish modal tests

fix: invalid use of delete subsection query for unit

refactor: use current section for highlights modal

feat: add auto scroll to subsection and improve scroll behaviour

fix: jsdoc types
2023-12-20 10:20:48 -05:00
Stephannie Jimenez
91ba00346c feat: add auto scroll to new sections when created
fix: rename util function and remove unused eslint comment

fix: fix tests by mocking scrollIntoView function

test: add assertion for checking call to mock function
2023-12-20 10:20:48 -05:00
Moncef Abboud
7286b21f5a feat: add Section Configure 2023-12-20 10:20:48 -05:00
Navin Karkera
134b75568a feat: section list and new section button
Also refactor api and hooks

fix: publish button behaviour and card header tests

fix: warning in highlights and publish modal test

fix: courseoutline tests

test: add test for new section functionality

fix(lint): lint issues

refactor: remove unnecessary css in CardHeader

refactor: rename emptyPlaceholder test file

refactor: replace ternary operator with 'and' condition

refactor: add black color to expand/collapse button

refactor: display only changed subsection and units in publish modal

refactor: update messages and css

refactor: wrap urls in function call

refactor: fix jsdoc types

refactor: use helmet for document title
2023-12-20 10:20:48 -05:00
vladislavkeblysh
59071424b3 feat: course outline - sections list
* feat: [2u-259] add components

* feat: [2u-259] fix sidebar

* feat: [2u-259] add tests, fix links

* feat: [2u-259] fix messages

* feat: [2u-159] fix reducer and sidebar

* feat: [2u-259] fix reducer

* feat: [2u-259] remove warning from selectors

* feat: [2u-259] remove indents

---------

Co-authored-by: Vladislav Keblysh <vladislavkeblysh@Vladislavs-MacBook-Pro.local>

feat: Course Outline  - Sections list (#59)

* feat: [2u-336] add tests

* feat: [2u-271] fix button

* feat: [2u-336] add component, refactor header

* fix: [2u-342] fix translates and indents

* fix: [2u-342] fix constants and expand block

* feat: [2u-336] remove new section from menu

---------

Co-authored-by: Vladislav Keblysh <vladislavkeblysh@Vladislavs-MacBook-Pro.local>

feat: Course outline - Content empty (#72)

* feat: [2u-324] add component

* feat: [2u-324] add translates

* feat: [2u-324] update tests

* feat: [2u-324] update branch

* fix: [2u-324] fixed empty handler

feat: Course outline - Section Publish (#61)

* feat: [2u-354] add publish modal, api and update tests

* feat: [2u-354] refactor modal

* fix: [2u-354] removed comments

* fix: [2u-354] fix indents

* fix: [2u-354] removed translates duplicates

* fix: [2u-354] rename handlers

feat: Course outline - Update section card (#71)

* feat: [2u-615] update section card

* fix: [2u-615] fix handler names

* fix: [2u-615] fix indents

* fix: [2u-615] add empty handler

* fix: [2u-615] fix data test id name

* fix: [2u-615] fix styles

fix: [2u-696] add saving processing for higlights and enable highlights (#78)

feat: Course outline - Section Edit (#70)

* feat: [2u-336] add tests

* feat: [2u-271] fix button

* feat: [2u-336] add component, refactor header

* feat: [2u-342] add modal

* fix: [2u-342] fix translates and indents

* feat: [2u-342] add modal

* feat: [2u-342] add api

* feat: [2u-342] add tests and translates

* feat: [2u-342] fix indents

* fix: [2u-342] fix indents, variant and utils

* feat: [2u-342] fixed slice, thunks, hooks

* feat: [2u-354] add publish modal, api and update tests

* feat: [2u-615] update section card

* feat: [2u-348] add api, handlers, tests

* feat: [2u-348] add description for api

* fix: [2u-348] fix useEscapeClick

* fix: [2u-348] remove useEffect from CardHeader

* fix: [2u-348] fixed handlers and tests

* fix: [2u-348] fixed handlers and tests

---------

Co-authored-by: Vladislav Keblysh <vladislavkeblysh@Vladislavs-MacBook-Pro.local>

feat: Course outline - Section Delete (#74)

* feat: [2u-336] add tests

* feat: [2u-271] fix button

* feat: [2u-336] add component, refactor header

* feat: [2u-342] add modal

* fix: [2u-342] fix translates and indents

* feat: [2u-342] add modal

* feat: [2u-342] add api

* feat: [2u-342] add tests and translates

* feat: [2u-342] fix indents

* fix: [2u-342] fix indents, variant and utils

* feat: [2u-342] fixed slice, thunks, hooks

* feat: [2u-354] add publish modal, api and update tests

* feat: [2u-615] update section card

* feat: [2u-348] add api, handlers, tests

* feat: [2u-510] add delete api, add delete modal

* fix: [2u-510] fixed tests

---------

Co-authored-by: Vladislav Keblysh <vladislavkeblysh@Vladislavs-MacBook-Pro.local>

feat: Course outline - Section duplicate (#88)

* feat: [2u-336] add tests

* feat: [2u-271] fix button

* feat: [2u-336] add component, refactor header

* feat: [2u-342] add modal

* fix: [2u-342] fix translates and indents

* feat: [2u-342] add modal

* feat: [2u-342] add api

* feat: [2u-342] add tests and translates

* feat: [2u-342] fix indents

* fix: [2u-342] fix indents, variant and utils

* feat: [2u-342] fixed slice, thunks, hooks

* feat: [2u-354] add publish modal, api and update tests

* feat: [2u-615] update section card

* feat: [2u-348] add api, handlers, tests

* feat: [2u-510] add delete api, add delete modal

* feat: [2u-360] add api

* feat: [2u-360] add slice

* feat: [2u-360] add tests

* fix: [2u-360] fixed tests

---------

Co-authored-by: Vladislav Keblysh <vladislavkeblysh@Vladislavs-MacBook-Pro.local>

fix: Course outline - Highlights links (#89)

* fix: fixed doc urls

* fix: fixed components

feat: Course outline - Collapse all sections (#75)

* feat: added collapse all section logic

* fix: fixed tests

fix: final revision commits

fix: increase code coverage on the page
2023-12-20 10:20:48 -05:00
renovate[bot]
f938d08361 fix(deps): update dependency @edx/frontend-lib-content-components to v1.177.7 (#757)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-19 14:25:06 -05:00
kenclary
47a3fd6836 Merge pull request #443 from openedx/kenclary/plugins
fix: re-enable private plugins, with newer version of public plugin repo
2023-12-19 13:09:13 -05:00
Ken Clary
82f6d7d3ca fix: re-enable private plugins, with newer version of public plugin repo 2023-12-19 12:28:57 -05:00
Jorg Are
f78e8a5671 fix: bump frontend-component-ai-translations-edx version (#756) 2023-12-19 18:08:55 +01:00
Kristin Aoki
4c7faad987 feat: break up load api to tab specific (#740) 2023-12-19 09:10:05 -05:00
Chris Chávez
bf46008878 style: UX Refinements on taxonomy pages [FC-0036] (#723)
This change makes the following updates to the UX of the taxonomy pages:

* On the taxonomies list, display the full name of taxonomies in a tooltip if it's longer than what's displayed
* On the taxonomy detail page, please change the title of the "Value" column to "Tag name"
* On taxonomy detail page, remove the "child tags" column and put it in parentheses instead
* Update tags count color
* Several minor issues brought up here: https://github.com/openedx/modular-learning/issues/105#issuecomment-1829412705. 
* Fix issue with scroll position not being reset on navigation
2023-12-19 09:44:44 +05:30
Rômulo Penido
a37d13f788 feat: add download template button to taxonomy list [FC-0036] (#674)
This commit adds a new button in the Taxonomy List to allow users to download a sample taxonomy template in the format used to import taxonomies.
2023-12-19 09:31:48 +05:30
Kristin Aoki
c68b2e3b06 Revert "chore(deps): update dependency @edx/frontend-build to v13.0.14 (#695)" (#753)
This reverts commit cb8bf2cd89.
2023-12-18 12:53:50 -05:00
dependabot[bot]
613220441f build(deps): bump sharp from 0.32.1 to 0.32.6 in /www (#441)
Bumps [sharp](https://github.com/lovell/sharp) from 0.32.1 to 0.32.6.
- [Release notes](https://github.com/lovell/sharp/releases)
- [Changelog](https://github.com/lovell/sharp/blob/main/docs/changelog.md)
- [Commits](https://github.com/lovell/sharp/compare/v0.32.1...v0.32.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 09:56:26 -05:00
renovate[bot]
cb8bf2cd89 chore(deps): update dependency @edx/frontend-build to v13.0.14 (#695)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-18 09:55:30 -05:00
renovate[bot]
089d8a8f79 fix(deps): update dependency @edx/frontend-component-footer to v12.6.1 (#688)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-18 09:47:00 -05:00
dependabot[bot]
57937995e2 build(deps): bump @babel/traverse from 7.21.5 to 7.23.6 in /www (#440)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.21.5 to 7.23.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.6/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 09:37:57 -05:00
renovate[bot]
de9072d506 fix(deps): update dependency react-datepicker to v4.24.0 (#726)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-18 09:33:47 -05:00
dependabot[bot]
60ccf0fb53 build(deps): bump @babel/traverse from 7.22.5 to 7.23.6 (#439)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.5 to 7.23.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.6/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 09:28:08 -05:00
renovate[bot]
279f8f2a6c fix(deps): update dependency react-textarea-autosize to v8.5.3 (#751)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-18 09:10:25 -05:00
Yusuf Musleh
7a4c9a36b6 feat: Search Content Tags (#737)
This change adds the ability to search content tags in the content tags
drawer, in order to filter tags. This change also refactors the way data
is loaded from the server, handling pre-loaded data and pagination.
2023-12-18 11:16:22 +05:30
Kristin Aoki
476f779e76 fix: files page timeout (#749) 2023-12-15 16:55:24 -05:00
Raymond Zhou
75eb0c307e fix: datatable state persistence issues (#746) 2023-12-15 14:52:59 -05:00
Jorg Are
da5d64ad9e fix: update transcript settings default logic (#745)
* fix: update transcript settings default logic

* fix: remove extra state logic
2023-12-15 10:52:12 -05:00
Peter Kulko
ad8fe53348 chore: added start:with-theme npm script (#747) 2023-12-15 10:15:04 +02:00
Jesper Hodge
94725dfe3c Fix undo reverts and update flcc to working version (#748)
* Revert "Fix  tinymce editor problems (#743)"

This reverts commit e6ce05571f.

* chore: update flcc to working version

* chore: update flcc to version that disables plugins
2023-12-14 15:30:05 -05:00
Jesper Hodge
287cc23ee7 fix: disable a11ychecker and powerpaste plugins (#437) 2023-12-14 15:09:04 -05:00
Jesper Hodge
56ffc495dd chore: disable tinymce plugins (#436) 2023-12-14 14:55:36 -05:00
Jesper Hodge
fbd3d8506f fix: editor container can scroll again (#435)
This is a fix to a problem where some css prevented the editors from scrolling.
2023-12-14 11:53:26 -05:00
kenclary
4483214de1 Merge pull request #434 from openedx/revert-432-kenclary/plugins
Revert "fix: disable paid plugins, to test prod issue in stage"
2023-12-13 10:46:40 -05:00
kenclary
aa17203e07 Revert "fix: disable paid plugins, to test prod issue in stage" 2023-12-13 10:39:37 -05:00
Jesper Hodge
e6ce05571f Fix tinymce editor problems (#743)
Internal issue: https://2u-internal.atlassian.net/servicedesk/customer/portal/9/CR-6328?created=true

Reverted 6 merged PRs due to problems.

scroll was not working on editors
potential problems with editor content loading

------------------------------------------------------


* Revert "fix(deps): update dependency @edx/frontend-lib-content-components to v1.177.4 (#742)"

This reverts commit cc40e9d6cb.

* Revert "feat: add escalation email field for LTI-based proctoring providers (#736)"

This reverts commit 0f483dc4e1.

* Revert "fix: video downloads (#728)"

This reverts commit c5abd21569.

* Revert "fix: import api to chunk file (#734)"

This reverts commit 6f7a992847.

* Revert "feat: Taxonomy delete dialog (#684)"

This reverts commit 1eff489158.

* Revert "fix(deps): update dependency @edx/frontend-lib-content-components to v1.177.1 (#727)"

This reverts commit dcabb77218.
2023-12-12 18:23:26 -05:00
renovate[bot]
cc40e9d6cb fix(deps): update dependency @edx/frontend-lib-content-components to v1.177.4 (#742)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-12 16:14:03 -05:00
Jesper Hodge
57042c90bf Revert "fix: get library blocks editing to work locally (#431)" (#433)
This reverts commit fd3ed5d146.
2023-12-12 15:57:03 -05:00
kenclary
6dab3a1cea Merge pull request #432 from openedx/kenclary/plugins
fix: disable paid plugins, to test prod issue in stage
2023-12-12 15:30:44 -05:00
Ken Clary
7c7b36b402 fix: disable paid plugins, to test prod issue in stage 2023-12-12 15:22:15 -05:00
Jesper Hodge
fd3ed5d146 fix: get library blocks editing to work locally (#431)
* fix: get library blocks editing to work locally

* fix: tests
2023-12-12 14:34:27 -05:00
Michael Roytman
0f483dc4e1 feat: add escalation email field for LTI-based proctoring providers (#736)
This commit adds an escalation email field for LTI-based proctoring providers to the Proctoring modal on the Pages & Resources page. This field behaves identically to the Proctortrack escalation email.
2023-12-12 14:28:23 -05:00
Kristin Aoki
c5abd21569 fix: video downloads (#728) 2023-12-12 11:00:40 -05:00
Kristin Aoki
6f7a992847 fix: import api to chunk file (#734) 2023-12-12 10:28:33 -05:00
Chris Chávez
1eff489158 feat: Taxonomy delete dialog (#684)
This adds:
    New submenu 'Delete' on the Taxonomy card menu
    Delete Dialog with the functionality to delete a Taxonomy
    Show a Toast after delete the Taxonomy
    Enable export for System defined Taxonomies
2023-12-12 17:54:39 +05:30
renovate[bot]
dcabb77218 fix(deps): update dependency @edx/frontend-lib-content-components to v1.177.1 (#727)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-11 13:11:53 -05:00
kenclary
1137c88a02 Merge pull request #428 from openedx/kenclary/plugins
fix: misssing part of paid plugin configuration
2023-12-11 12:07:25 -05:00
Ken Clary
b0ca07d801 fix: misssing part of paid plugin configuration 2023-12-11 11:46:57 -05:00
Jorg Are
67cda575a5 fix: bump ai translations component (#739) 2023-12-11 09:16:40 -05:00
Kristin Aoki
195c9e416c fix: grading segment number ranges (#729) 2023-12-08 13:26:36 -05:00
Kristin Aoki
5db6b2049f fix: wrong min count alert showing (#730) 2023-12-08 13:21:36 -05:00
Yusuf Musleh
c9b73a5008 feat: Add content tags tree state + editing (#704)
This commit adds the add/remove functionality of content tags where the
state is stored and changes are updated in the backend through the API.
Changes are reflected in the UI automatically.
2023-12-08 13:55:57 +05:30
Kristin Aoki
56ad86ee60 feat: update usage metrics to be a hyperlink (#717) 2023-12-07 12:30:15 -05:00
Navin Karkera
04c14274fd feat: course outline page (#694)
* feat: Course outline Top level page (#36)

* feat: [2u-259] add components

* feat: [2u-259] fix sidebar

* feat: [2u-259] add tests, fix links

* feat: [2u-259] fix messages

* feat: [2u-159] fix reducer and sidebar

* feat: [2u-259] fix reducer

* feat: [2u-259] remove warning from selectors

* feat: [2u-259] remove indents

---------

Co-authored-by: Vladislav Keblysh <vladislavkeblysh@Vladislavs-MacBook-Pro.local>

feat: Course outline Status Bar (#50)

* feat: [2u-259] add components

* feat: [2u-259] fix sidebar

* feat: [2u-259] add tests, fix links

* feat: [2u-259] fix messages

* feat: [2u-159] fix reducer and sidebar

* feat: add checklist

* feat: [2u-259] fix reducer

* feat: [2u-259] remove warning from selectors

* feat: [2u-259] remove indents

* feat: [2u-259] add api, enable modal

* feat: [2u-259] add tests

* feat: [2u-259] add translates

* feat: [2u-271] fix transalates

* feat: [2u-281] fix isQuery pending, utils, hooks

* feat: [2u-281] fix useScrollToHashElement

* feat: [2u-271] fix imports

---------

Co-authored-by: Vladislav Keblysh <vladislavkeblysh@Vladislavs-MacBook-Pro.local>

feat: Course Outline Reindex (#55)

* feat: [2u-277] add alerts

* feat: [2u-277] add translates

* feat: [2u-277] fix tests

* fix: [2u-277] fix slice and hook

---------

Co-authored-by: Vladislav Keblysh <vladislavkeblysh@Vladislavs-MacBook-Pro.local>

fix: Course outline tests (#56)

* fix: fixed course outline status bar tests

* fix: fixed course outline status bar tests

* fix: fixed course outline enable highlights modal tests

* fix: enable modal tests

fix: increase code coverage on the page

* refactor: improve course outline page

feat: lms live link

chore: update outline link

fix: course outline link

refactor: remove unnecessary css and rename test file

refactor: remove unnecessary css from outlineSidebar

test: make use of message variable instead of hardcoded text

refactor: remove unnecessary h5 class

test: use test id for detecting component

refactor: update course outline url and some default messages

---------

Co-authored-by: vladislavkeblysh <138868841+vladislavkeblysh@users.noreply.github.com>
2023-12-06 10:06:29 -05:00
Jorg Are
bebbc1535b feat: add ai translations component to transcript settings (#722) 2023-12-05 16:22:46 -05:00
Artur Gaspar
1c9771b332 feat: video gallery thumbnail fallback (#412) 2023-12-04 09:58:40 -05:00
Artur Gaspar
1ddaf9a662 fix: video editor and uploader layout fixes (#410)
* fix: video upload page layout fixes

* fix: video editor thumbnail fallback icon colour and size

* fix: video uploader close button go back instead of closing app

Change the video uploader close button to always go back in navigation history,
and change the gallery to replace its location with the video uploader's when
automatically loading it due to an empty video list, so that when the uploader
goes back in the history, it goes to what was before the gallery.

* fix: video editor spinners vertical alignment

The Editor component uses the pgn__modal-fullscreen class to be fullscreen,
but it is not structured like a Paragon FullscreenModal and the fullscreen
positioning style is not applied correctly, particularly in the case where
the content is smaller than the body - a vertically centred component will
be centred to the content size, not to the screen.

Here we directly apply the style that would have applied to the relevant
elements of a Paragon FullscreenModal.

* fix: use trailingElement for video uploader input button

Also styles the button so it looks the same on hover/active.
2023-12-04 09:58:17 -05:00
Artur Gaspar
2aeb094315 fix: video gallery design fixes (#407) 2023-12-04 09:35:30 -05:00
renovate[bot]
1636226572 fix(deps): update dependency @edx/frontend-lib-content-components to v1.176.4 (#720)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-01 12:33:36 -05:00
Dmytro
ed051c3543 fix: default display of Show Answer and Show reset option (#403)
Hardcoding values for showAnswer and showResetButton
in initialState leads to incorrect selection of additional
states specified in Advanced settings.
2023-12-01 09:26:12 -05:00
Dmytro
60439d5be6 fix: displaying the correct default randomization value (#413) 2023-11-30 16:33:25 -05:00
Maria Grimaldi
2fbcfc03dd fix: remove unnecessary course-v1 from courseId string (#687) 2023-11-30 15:56:38 -05:00
ABBOUD Moncef
8928d35f17 fix: make filter dropdown closable by clicking outside + revert to single-value video filter (#414) 2023-11-30 14:43:12 -05:00
Ihor Romaniuk
bad763596c fix: video thumbnail availability (#421) 2023-11-30 14:34:19 -05:00
Kristin Aoki
ac1fc43250 fix: visibility of transcript dropdowns (#719) 2023-11-30 13:21:34 -05:00
Kristin Aoki
a2dceac62f feat: add notification of transcription error (#715) 2023-11-28 16:55:00 -05:00
Kristin Aoki
2402769d9d fix: sort of boolean columns (#705) 2023-11-28 13:13:54 -05:00
Feanil Patel
7030d6c1ba build: Updating workflow commitlint.yml.
The .github/workflows/commitlint.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.
2023-11-27 12:18:56 -05:00
renovate[bot]
1edc7d3329 fix(deps): update dependency @edx/frontend-lib-content-components to v1.176.0 (#709)
* fix(deps): update dependency @edx/frontend-lib-content-components to v1.176.0

* fix: mock frontend-components-tinymce-advanced-plugins for jest

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ken Clary <kclary@edx.org>
2023-11-23 15:34:29 -05:00
Braden MacDonald
352ef35ac2 feat: display all child tags in the "bare bones" taxonomy detail page (#703)
Also includes:
- feat: set <title> on taxonomy list page and taxonomy detail page
- fix: display all taxonomies on the list page, even if > 10
- refactor: separate out loading spinner component
2023-11-23 01:44:17 +05:30
Kristin Aoki
f9b008e8e8 feat: original fetch to includes usage metrics (#701) 2023-11-22 13:54:13 -05:00
Kristin Aoki
251259e4bd feat: add new libray button (#710) 2023-11-22 13:42:37 -05:00
kenclary
c0bc54a664 Merge pull request #420 from openedx/kenclary/plugins
feat: use plugins repo, and enable accessibility checker and powerpaste
2023-11-22 10:50:56 -05:00
Adolfo R. Brandes
a622f8e86e fix: Fix data API URL handling
All configuration calls must handled asynchronously, otherwise they risk
failure in runtime configuration scenarios.
2023-11-21 16:57:04 -03:00
Ken Clary
5342f164a2 feat: use plugins repo, and enable accessibility checker and powerpaste 2023-11-21 14:27:10 -05:00
Rômulo Penido
02cdccc77c feat: bare bones taxonomy detail page [FC-0036] (#655)
* feat: System-defined tooltip added

* feat: Taxonomy card menu added. Export menu item added

* feat: Modal for export taxonomy

* feat: Connect with export API

* test: Tests for API and selectors

* feat: Use windows.location.href to call the export endpoint

* test: ExportModal.test added

* style: Delete unnecesary code

* docs: README updated with taxonomy feature

* style: TaxonomyCard updated to a better code style

* style: injectIntl replaced by useIntl on taxonomy pages and components

* refactor: Move and rename taxonomy UI components to match 0002 ADR

* refactor: Move api to data to match with 0002 ADR

* test: Refactor ExportModal tests

* chore: Fix validations

* chore: Lint

* refactor: Moving hooks to apiHooks

* feat: add taxonomy detail page

* fix: address nits in PR review

* refactor: move data/selectors to data/apiHooks

and fix tests to mock useQuery.

* fix: address nits in PR review

* fix: replace taxonomy menu ModalPopup with Dropdown menu

Avoids clicking through to the card when using the menu button to hide
a card's menu.

* fix: change taxonomy URLs

* /taxonomy-list is now /taxonomies, and there's a temporary redirect
* /taxonomy-list/🆔 is now /taxonomy/🆔

---------

Co-authored-by: Christofer <christofer@opencraft.com>
Co-authored-by: XnpioChV <xnpiochv@gmail.com>
Co-authored-by: Christofer Chavez <christofer@example.com>
Co-authored-by: Jillian Vogel <jill@opencraft.com>
Co-authored-by: Braden MacDonald <braden@opencraft.com>
2023-11-20 17:15:31 -03:00
Yusuf Musleh
375006deb1 feat: Implement Content Tags Drawer
This implements a side drawer widget for content taxonomy tags.
It includes displaying the object's tags, along with their
lineage (ancestor tags) data. It also implements the listing the
available taxonomy tags (including nesting ones) to select from
to apply to this unit.

Note: The editing of tags (adding/removing) will be added in a future
PR.

* feat: Add initial UnitTaxonomyTagsDrawer widget
* feat: Add fetching unit taxonomy tags from backend
* feat: Add fetching/group tags with taxonomies
* feat: Add fetch Unit data and display name
* feat: Add Taxonomy Tags dropdown selector
* feat: Add TagBubble for tag styling
* chore: Add distinct keys to elements + remove logs
* feat: Add close drawer with ESC- keypress
* feat: Make dropdown selectors keyboard accessible
* chore: Fix issues causing validation to fail
* test: Add coverage tests for UnitTaxonomyDrawer
* feat: Incorporate tags lineage data from API
* refactor: Remove/replace deprecated injectIntl
* test: Remove redux store related code + fix warnings
* feat: Use <Loading /> instead of loading string
* docs: Add docs string to TaxonomyTagsCollapsible
* feat: Use <Spinner/> to allow mutiple loading to show
* feat: Rename UnitTaxonomyTagDrawer -> ContentTagsDrawer
* feat: Add ContentTagsTree component to render Tags
* feat: Only fetch tags when dropdowns are opened
* refactor: Simply dropdown close/open states
* feat: Use built in class styles instead of custom
* feat: Replace hardcoded values with scss variables
* refactor: follow existing structure for reactQuery/APIs
* feat: Change tag bubble outline color
* feat: Add TagOutlineIcon for implicit tags
* feat: Make aria label internationalized
* feat: Replace custom styles with builtin classes
* fix: Fix bug with closing drawer
* refactor: Simplify content tags fetching code
* refactor: Simplify getTaxonomyListApiUrl
2023-11-20 16:56:46 -03:00
Kristin Aoki
9b053de0b7 fix: filter overwritten by sort (#702) 2023-11-20 14:06:36 -05:00
Kristin Aoki
a62c53eb00 fix: pagination style (#697) 2023-11-20 09:18:36 -05:00
Kristin Aoki
08d895b2e0 fix: list more info menu styles (#696) 2023-11-17 09:48:32 -05:00
Kristin Aoki
eb3ee3a6b2 fix: transcript tab layout (#686)
* fix: transcript tab layout

* fix: console warnings for missing config values
2023-11-17 09:48:13 -05:00
Kristin Aoki
af0124d4e6 fix: general page layout and colors (#693) 2023-11-16 18:05:50 -05:00
Kristin Aoki
3d37bc056d fix: page specific messages (#691) 2023-11-16 16:40:57 -05:00
dependabot[bot]
a25bc0670e chore(deps-dev): bump semver from 5.7.1 to 5.7.2 (#665)
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-16 14:02:38 -05:00
dependabot[bot]
0f4662265a chore(deps): bump tinymce from 5.10.5 to 5.10.9 (#685)
Bumps [tinymce](https://github.com/tinymce/tinymce/tree/HEAD/modules/tinymce) from 5.10.5 to 5.10.9.
- [Changelog](https://github.com/tinymce/tinymce/blob/5.10.9/modules/tinymce/CHANGELOG.md)
- [Commits](https://github.com/tinymce/tinymce/commits/5.10.9/modules/tinymce)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-16 13:52:56 -05:00
Kristin Aoki
79bb38a098 fix: use styles from frontend-content-header (#690) 2023-11-16 13:38:59 -05:00
Jesper Hodge
ed1c83fe7f chore: update decode-uri-component (#692) 2023-11-16 13:33:51 -05:00
dependabot[bot]
b0bd80d8d1 build(deps): bump decode-uri-component from 0.2.0 to 0.2.2 (#401)
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-16 13:02:56 -05:00
renovate[bot]
9aef1a88ba fix(deps): update dependency @edx/frontend-component-header to v4.10.1 (#689)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-16 12:59:43 -05:00
renovate[bot]
0f80e27978 fix(deps): update dependency @reduxjs/toolkit to v1.9.7 (#679)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-16 12:51:52 -05:00
dependabot[bot]
c5fc16b77a build(deps): bump @xmldom/xmldom from 0.7.5 to 0.7.8 (#376)
Bumps [@xmldom/xmldom](https://github.com/xmldom/xmldom) from 0.7.5 to 0.7.8.
- [Release notes](https://github.com/xmldom/xmldom/releases)
- [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/xmldom/xmldom/compare/0.7.5...0.7.8)

---
updated-dependencies:
- dependency-name: "@xmldom/xmldom"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-16 12:13:57 -05:00
dependabot[bot]
d5f0691fc3 chore(deps): bump @babel/traverse from 7.22.17 to 7.23.2 (#664)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.17 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-16 10:53:29 -05:00
Emad Rad
91019b4a51 feat: Persian language support added (#553)
* fix: corrected typos

justify-contnt-center -> justify-content-center
Visiblity -> Visibility
Wraper -> Wrapper
closeAssetinfo -> closeAssetInfo
RestictDatesInput -> RestrictDatesInput
isOnSmallcreen -> isOnSmallScreen
Repsonse -> Response
configuation -> configuration
seconary -> secondary
comparesion -> comparison

* feat: Persian language (fa_IR) added

* refactor: better variable name for languages

* chore: sort languages alphabetically
2023-11-16 09:01:08 +05:30
renovate[bot]
2804f38d4f chore(deps): update dependency axios-mock-adapter to v1.22.0 (#678)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-15 16:48:29 -05:00
renovate[bot]
416ac4fbdc chore(deps): update dependency @edx/frontend-build to v13.0.5 (#676)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-15 16:48:15 -05:00
renovate[bot]
14e3c258fb fix(deps): update dependency @edx/brand to v1.2.3 (#667)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-15 16:47:48 -05:00
Kristin Aoki
ce9db575a6 fix: table search filter (#683) 2023-11-14 13:21:11 -05:00
Chris Chávez
1ee80b68ec feat: Taxonomy export menu [FC-0036] (#645)
* feat: System-defined tooltip added
* feat: Taxonomy card menu added. Export menu item added
* feat: Modal for export taxonomy
* feat: Connect with export API
* test: Tests for API and selectors
* feat: Use windows.location.href to call the export endpoint
* test: ExportModal.test added
* style: Delete unnecessary code
* docs: README updated with taxonomy feature
* style: TaxonomyCard updated to a better code style
* style: injectIntl replaced by useIntl on taxonomy pages and components
* refactor: Move and rename taxonomy UI components to match 0002 ADR
* refactor: Move api to data to match with 0002 ADR
* test: Refactor ExportModal tests
* chore: Fix validations
* chore: Lint
* refactor: Moving hooks to apiHooks
* style: Nit on return null

---------

Co-authored-by: Rômulo Penido <romulo@dash.dev.br>
Co-authored-by: Christofer Chavez <christofer@example.com>
2023-11-14 13:08:37 -05:00
Kristin Aoki
7c7ea1fbc2 fix: active transcript preference not loading (#682) 2023-11-14 10:43:01 -05:00
Kristin Aoki
3378c8e170 fix: combine filter and sort into one modal (#680) 2023-11-13 15:22:44 -05:00
Kristin Aoki
2fbb490cbb fix: total file count update on add and delete (#681) 2023-11-13 14:46:31 -05:00
Zachary Hancock
e41efba0cd feat: opt out is not supported by lti proctoring (#673)
This toggle does nothing if an LTI tool is selected. We should hide it in that case.
2023-11-13 09:14:54 -05:00
Zachary Hancock
7c7b3cdc07 feat: remove old/duplicate proctoring component (#671) 2023-11-09 08:55:25 -05:00
Kristin Aoki
78eb512836 refactor: files-and-videos folder (#672) 2023-11-08 15:54:47 -05:00
Kristin Aoki
3dac6aa188 fix: modal exit redirect (#659) 2023-11-07 16:09:25 -05:00
renovate[bot]
4a3d1a1787 fix(deps): update dependency @edx/frontend-lib-content-components to v1.175.1 (#663)
* fix(deps): update dependency @edx/frontend-lib-content-components to v1.175.1

* fix: upgrade paragon

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: KristinAoki <kaoki@2u.com>
2023-11-07 15:43:44 -05:00
renovate[bot]
2cfde7d3f4 fix(deps): update dependency @edx/frontend-component-header to v4.9.3 (#651)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-07 14:47:50 -05:00
Kristin Aoki
05e90b59d2 fix: fidelity typo in preference api (#662) 2023-11-07 14:05:08 -05:00
Kristin Aoki
02a683f09a fix: end date error when certificate row not shown (#668) 2023-11-07 11:31:21 -05:00
renovate[bot]
f61f7429bd chore(deps): update dependency @edx/frontend-build to v13.0.4 (#641)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 15:37:34 -05:00
renovate[bot]
09f908b019 fix(deps): update dependency @edx/frontend-component-footer to v12.5.1 (#650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 15:25:11 -05:00
renovate[bot]
d5cc56756e fix(deps): update dependency react-transition-group to v4.4.5 (#647)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 13:26:16 -05:00
renovate[bot]
77a355ee8d chore(deps): update dependency @edx/browserslist-config to v1.2.0 (#649)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 09:55:07 -05:00
renovate[bot]
7bcce0b9d9 fix(deps): update font awesome (#648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 09:43:19 -05:00
renovate[bot]
e1602258dc chore(deps): update dependency glob to v7.2.3 (#643)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 09:40:08 -05:00
renovate[bot]
78ef3c3f37 fix(deps): update dependency moment to v2.29.4 [security] (#630)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 09:35:33 -05:00
renovate[bot]
890d664746 chore(deps): update dependency @testing-library/jest-dom to v5.17.0 (#653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 09:18:13 -05:00
Kristin Aoki
a28338df30 feat: add video page (#640) 2023-11-06 08:51:21 -05:00
Jeremy Ristau
fc8070025b Merge pull request #415 from Mashal-m/mashal-m/update-README
refactor: updated README file to reflect template changes
2023-10-31 08:39:15 -04:00
Kristin Aoki
3aa9088309 fix: upgrade paragon (#416) 2023-10-30 11:39:09 -04:00
Jesper Hodge
221fcf77dc feat: change filter status text (#657)
Changed the filter status text on the files and uploads table to display also the number of files, even when a filter is applied.

https://2u-internal.atlassian.net/browse/TNL-11086 (internal ticket)

I just copied most of paragon's FilterStatus component, made some adjustments, and then overrode the default component.
2023-10-27 10:25:51 -04:00
Syed Ali Abbas Zaidi
42614b8d8e chore: bump frontend-platform (#409) 2023-10-27 09:30:51 -04:00
mashal-m
b3811b8f4d refactor: updated README file to reflect template changes 2023-10-27 12:07:46 +05:00
Stanislav
378b0e93eb fix: Missed favicon in Safari (#633)
Co-authored-by: Stanislav Lunyachek <lunyachek@MacBook-Pro-M1.local>
2023-10-25 14:07:26 -04:00
sundasnoreen12
a69711942b fix: fixed issue of unable to call handle submit function on button click (#656) 2023-10-25 22:58:46 +05:00
Braden MacDonald
0679022f7a docs: some updates to the readme (#625) 2023-10-25 10:29:51 -04:00
Syed Ali Abbas Zaidi
d497b01c45 feat: upgrade react router to v6 (#519)
## Ticket
[React Router Upgrade to v6](https://github.com/openedx/platform-roadmap/issues/276).

## Description
This PR upgrades React Router from `v5` to `v6`.
2023-10-20 16:52:23 -04:00
Jesper Hodge
682c3b64b2 chore: adjust renovate config (#637) 2023-10-20 13:53:42 -04:00
Feanil Patel
9715429ed0 chore: Update to the new version of brand-openedx in the new scope. (#646)
Part of https://github.com/openedx/axim-engineering/issues/23

This updates the `@edx/brand` alias to point to the `brand-openedx` package at
the `openedx` scope. This does not impact imports because this package is used
via an alias.
2023-10-20 13:29:49 -04:00
Chris Chávez
ad4d9b9c63 Taxonomy list page [FC-0036] (#622) 2023-10-20 11:55:20 -04:00
Jesper Hodge
85a19f7971 chore: change files page title (#639)
Changed "Files & Uploads" page title to just "Files"
2023-10-19 11:21:46 -04:00
Peter Kulko
6705f638c0 fix: fixed sidebar margin top (#73) (#593) 2023-10-18 13:42:44 -04:00
Jesper Hodge
618831f1eb fix: info modal and list view thumbnails (#636) 2023-10-17 12:17:03 -04:00
renovate[bot]
6287e8c01b fix(deps): update dependency @edx/frontend-lib-content-components to v1.175.0 (#631)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-17 12:05:34 -04:00
Jesper Hodge
beb035b3e1 fix file upload thumbnails (#629)
Internal issue: https://2u-internal.atlassian.net/browse/TNL-11085

File upload gallery cards had distorted thumbnails. The goal is to fix this.
I centered the thumbnails instead of stretching them. I had to change the card layout a bit to do that, so in order to make everything look fine, I worked a bit on margins and paddings and font-size in order to bring this close to the figma mockups. It's not perfect because when you resize the browser window, the grid does some resizing that doesn't look as good as in figma, but I think it should be good enough for now.
2023-10-17 10:22:49 -04:00
Kristin Aoki
5c101b09d4 feat: update list view to be table (#628) 2023-10-16 14:58:28 -04:00
Jesper Hodge
7132136a91 chore: disable renovate automerge and add flcc rules (#624)
* chore: disable renovate automerge and add flcc rules

* chore: add dependency dashboard
2023-10-11 16:58:52 -04:00
Jesper Hodge
cfa4577b75 chore: add test change for flcc automerge (#408) 2023-10-11 13:58:42 -04:00
Muhammad Abdullah Waheed
03bf93ad13 feat: babel-plugin-react-intl to babel-plugin-formatjs migration (#621)
* feat: babel-plugin-react-intl to babel-plugin-formatjs migration

* fix: upgraded frontend-build to fix security issue
2023-10-11 11:38:36 -04:00
kenclary
2377eadcc0 Merge pull request #400 from abdullahwaheed/abdullahwaheed/react-intl-to-formatjs
feat: babel-plugin-react-intl to babel-plugin-formatjs migration
2023-10-11 11:38:30 -04:00
Navin Karkera
65859924c2 fix: await async saveSetting func to remove unwanted err msg (#610) 2023-10-11 10:42:43 -04:00
kenclary
b55b86cf56 Merge pull request #404 from open-craft/navin/fix-max-attempts-setting
fix: make max attempts setting fallback to default
2023-10-11 10:04:16 -04:00
Abdullah Waheed
71fd5fb1e0 fix: upgraded frontend-build to fix security issue 2023-10-11 18:44:01 +05:00
Abdullah Waheed
9ddcba2763 Merge branch 'main' of github.com:openedx/frontend-lib-content-components into abdullahwaheed/react-intl-to-formatjs 2023-10-11 18:43:34 +05:00
Bilal Qamar
97d0a1ce61 feat: update react & react-dom to v17 (#514)
* feat: update react & react-dom to v17

* refactor: updated package-lock

* refactor: updated failing tests

* refactor: updated FilesAndUploads test to resolve delay issue

* refactor: updated DiscussionSettings tests

* refactor: downgraded frontend-lib-content-components

* refactor: resolved lint issue

* refactor: bumped frontend-lib-content-components version

* refactor: updated CollapsibleStateWithAction test suit

* refactor: update FilesAndUploads test
2023-10-11 11:08:26 +05:00
Jeremy Ristau
a76c93c789 Merge pull request #361 from ghassanmas/fix-360
fix: resolve  #360 make style consistent across outlines
2023-10-10 14:28:38 -04:00
Navin Karkera
6889cd1e82 test: add test for initial attempts display value 2023-10-10 20:19:00 +05:30
Navin Karkera
35a2f3bb7f fix: use only null in state for empty value 2023-10-10 19:52:39 +05:30
Navin Karkera
e676616386 fix: snapshots 2023-10-10 12:53:11 +05:30
Navin Karkera
2209e5b963 fix: lint issues 2023-10-10 12:19:14 +05:30
Navin Karkera
82b770bdef refactor: improve hooks condition handling 2023-10-10 12:13:46 +05:30
Navin Karkera
398839d76c refactor: improve setting parser condition handling 2023-10-09 21:29:53 +05:30
Navin Karkera
5df26bf83b test: fix related test cases 2023-10-09 20:34:05 +05:30
Navin Karkera
c70679da54 fix: make max attempts setting fallback to default
The max attempts setting for a problem xblock should be set to null for
course default max attempt setting to take effect. This makes sure that
xblock setting is updated if course default is updated.
2023-10-06 20:35:53 +05:30
Abdullah Waheed
e474a6fc91 feat: babel-plugin-react-intl to babel-plugin-formatjs migration 2023-10-04 22:17:12 +05:00
German
3fe35344f0 feat: update copy for xpert summary card (#619) 2023-10-03 17:18:04 -03:00
edx-transifex-bot
bbca5a29b7 chore(i18n): update translations (#616)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-10-02 15:44:34 -04:00
Kristin Aoki
2a6a816baf feat: update footer and header to use frontend-component version (#618) 2023-10-02 15:06:33 -04:00
Kristin Aoki
564d724d5b feat: remove footer component (#397) 2023-10-02 10:29:37 -04:00
Mashal Malik
73f7d5d5f5 refactor: add @openedx in renovate automate configuration (#617) 2023-10-02 10:16:08 -04:00
Kristin Aoki
0871ce345a fix: studio home load screen (#615) 2023-09-29 14:29:31 -04:00
Kristin Aoki
01ddac380f fix: studio home UI bugs (#611) 2023-09-28 18:36:51 -04:00
Kristin Aoki
4840666664 fix: export download link prefix (#614) 2023-09-28 12:14:25 -04:00
Kristin Aoki
21e4ece669 fix: bump frontend-lib-content-components (#613) 2023-09-27 12:58:01 -04:00
Kristin Aoki
887a628c23 fix: export and import UI bugs (#612) 2023-09-27 12:10:45 -04:00
Kristin Aoki
a0089eb1be fix: typeahead sort and styling (#396) 2023-09-27 12:09:27 -04:00
Kyrylo Kholodenko
2ea876ae4f feat: implement import page (#587) 2023-09-25 12:07:08 -04:00
Kristin Aoki
c47c800cfa fix: advanced settings card alignment (#608) 2023-09-22 17:29:38 -04:00
Kristin Aoki
ef9633af35 fix: course updates UI bugs (#606)
* fix: change edit and delete buttons to icons

* fix: padding and button color

* fix: delete buttons and variant

* fix: date error icon color

* fix: page explanation text
2023-09-22 15:51:04 -04:00
Kristin Aoki
217b86e616 fix: missing header items (#607) 2023-09-22 14:56:14 -04:00
sundasnoreen12
37aabc4948 fix: update toggle state based on api response (#604)
* fix: update toggle state based on api response

* refactor: added statefulbutton instead of button

---------

Co-authored-by: SundasNoreen <sundas.noreen@arbisoft.com>
2023-09-22 16:58:49 +05:00
kenclary
eb320abfed Merge pull request #386 from open-craft/navin/partial-credit
fix: switch to advanced editor for partial credit support
2023-09-20 11:58:52 -04:00
ruzniaievdm
e099243437 feat: create Studio Home Page MFE (#589) 2023-09-19 10:04:43 -04:00
Kristin Aoki
6f238bdbe0 fix: bump frontend-lib-content-components (#602) 2023-09-15 14:02:18 -04:00
Kristin Aoki
773812c3e1 fix: block url conditional (#393) 2023-09-15 11:23:01 -04:00
Kristin Aoki
9eefc07832 fix: empty v2 library studio view (#394) 2023-09-15 09:05:51 -04:00
Kristin Aoki
bc25f9c21b fix: v2 libraries default open to advanced editor (#392) 2023-09-14 11:41:14 -04:00
kenclary
4cf99ab930 Merge pull request #388 from open-craft/farhaan/bb-7835-fix-styling
fix: Fix styling for components in dropzone
2023-09-14 11:37:47 -04:00
Kristin Aoki
77dfd0296c fix: bump frontend-lib-content-components (#601) 2023-09-14 09:42:04 -04:00
Kyrylo Kholodenko
1888993113 feat: implement export page (#586) 2023-09-14 09:07:24 -04:00
Farhaan Bukhsh
db8929d1a8 fix: Fix styling for components in dropzone
Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
2023-09-14 12:52:26 +05:30
Kristin Aoki
126e662d80 feat: remove replace video button for libs (#390) 2023-09-13 13:35:42 -04:00
Kristin Aoki
39aa5aa749 feat: update isLibrary check to include v2 libs (#389) 2023-09-13 13:13:18 -04:00
Kristin Aoki
73af4317f6 fix: name of blank problems (#391) 2023-09-13 10:55:28 -04:00
Kristin Aoki
fb28693854 fix: bump frontend-lib-content-components (#600) 2023-09-12 10:45:31 -04:00
Kristin Aoki
7f8c6f2d61 feat: update header to be keyboard accessible (#597) 2023-09-12 10:19:29 -04:00
Kristin Aoki
4f76b7c85e fix: sort items in typeahead dropdown (#387) 2023-09-12 09:48:43 -04:00
Syed Ali Abbas Zaidi
c44c72cec0 feat: upgrade react router to v6 (#280) 2023-09-12 09:04:42 -04:00
Navin Karkera
e3f5bbfe0c fix: switch to advanced editor for partial credit support
This commit reverts to advanced editor when partial_credit attribute is
added to multichoice, single select and numerical problems. Without this
change, the partial_credit attribute is removed from the problem on the
next edit.
2023-09-11 20:14:04 +02:00
Mohamed Akram
25e4e39953 fix: preformatted content being re-formatted (#376)
The issue had to do with how Firefox handles pasting newlines inside a <pre
contenteditable> tag (TinyMCE's editor works via contenteditable) and
fast-xml-parser's parsing. In Firefox, newlines are converted to <br> when
pasted, while Chrome preserves them. The parser by default trims spaces in text
nodes. In Firefox, the parser creates individual text nodes between the <br>
elements, and those have leading spaces in the example. In Chrome, there are no
<br> elements and the entire content is a single text node as-is. Setting
trimValues to false disables the trimming and resolves this issue in Firefox.

While investigating this, I noticed the builder also mishandles <br /> tags
emitted by the editor, converting them to <br></br>. The unpairedTags option in
the builder ensures they are output correctly as a single tag, and setting
suppressUnpairedNode to false ensures that single tag is <br/> rather than <br>
to remain XML compatible.

While trying to resolve this, I was looking into the paste plugin in TinyMCE.
It changes the behavior of pasting, making it more consistent between Chrome
and Firefox (i.e. both emit <br>) and is incorporated into TinyMCE 6 core.
Unfortunately, it seems to mangle pasting inside a <pre> tag by inserting
redundant nbsp characters (tinymce/tinymce#9017). TinyMCE 6 also outputs <br>
rather than <br /> - adding unpairedTags to the parser options is meant to
handle this, but it does not seem to behave entirely correct
(NaturalIntelligence/fast-xml-parser#609). These should be kept in mind if/when
upgrading to TinyMCE 6 (the different behaviors can be seen easily at
https://fiddle.tiny.cloud).
2023-09-11 10:47:04 -04:00
Kristin Aoki
15984473b4 fix: bump frontend-lib-content-components (#595) 2023-09-07 09:53:20 -04:00
ruzniaievdm
b03ecf1562 fix: reworked grading deadline (#584) 2023-09-07 09:18:08 -04:00
Kristin Aoki
9ebe187029 fix: undefined set selection for image modal (#384) 2023-09-06 16:48:21 -04:00
Kristin Aoki
fdc5916ada fix: course team UI bugs (#592) 2023-09-06 14:45:17 -04:00
Kristin Aoki
a54d351e9c fix: schedule and details UI bugs (#588) 2023-09-06 12:32:19 -04:00
Kristin Aoki
62cde57556 fix: grading page UI bugs (#591) 2023-09-06 11:20:12 -04:00
Kristin Aoki
2bd8037d7b feat: change head title depending on page (#582) 2023-09-06 11:02:16 -04:00
Kyrylo Kholodenko
a1793efcc0 feat: add help-urls (#585) 2023-09-05 14:17:39 -04:00
Kristin Aoki
ed2eed5110 feat: add file zip on download (#580) 2023-09-05 10:23:47 -04:00
Kristin Aoki
e50b8c7407 feat: add file size and usage metrics (#573) 2023-08-31 12:21:37 -04:00
vladislavkeblysh
ffae3bd868 feat: Created Course updates page (#581) 2023-08-31 10:56:45 -04:00
Jesper Hodge
8fe8bc1587 docs: document jest troubleshooting (#382) 2023-08-28 12:05:47 -04:00
mashal-m
e23a0887ce refactor: update lock file version 2023-08-28 13:54:54 +05:00
kenclary
8e659527f0 Merge pull request #366 from open-craft/farhaan/fix-drag-drop-component
Re-write the dropzone component and fix styling issues
2023-08-25 12:50:24 -04:00
Kristin Aoki
181f9c7a5f feat: add sort function and modal (#577)
* feat: add sort modal and function

* fix: dateAdded typo

* chore: update mock api data
2023-08-25 10:08:15 -04:00
Jhon Vente
1d95af5a31 [DOCS] Readme updated according OEP-55 (#526) 2023-08-24 09:16:27 -04:00
Kristin Aoki
d7a4b5b45b fix: add word break style for long words (#574) 2023-08-23 15:17:26 -04:00
kenclary
45e4bc5376 Merge pull request #368 from open-craft/kshitij/fix-video-sort-filter
fix: Video Gallery filters and sorting
2023-08-23 06:43:56 -07:00
vladislavkeblysh
2e8eed7504 feat: Created Course Team (#564) 2023-08-23 09:21:43 -04:00
German
d768bfc97a fix: xpert unit sumamries settings ui fixes (#576)
1. https://jira.2u.com/browse/ACADEMIC-16289
2. https://jira.2u.com/browse/ACADEMIC-16422
2023-08-22 15:43:17 -03:00
Jesper Hodge
9c997ab845 fix: Pass correct prop to TinyMceWidget and update FLCC (#575)
* fix: Pass correct prop to TinyMceWidget

* chore: update flcc

* fix: lockfile
2023-08-22 13:28:30 -04:00
kenclary
259b9f3d1f fix: don't get returnUrl for v2 blocks. (#380) 2023-08-21 19:57:08 -04:00
Kristin Aoki
c1976ce4d3 feat: add delete confirmation modal (#570) 2023-08-21 17:28:49 -04:00
Kristin Aoki
be74de2b22 fix: file info bugs (#571) 2023-08-21 16:47:52 -04:00
Jesper Hodge
e691df9cb5 fix: answer text flipped (#379)
This fixes a bug where an answer text was flipped in terms of the character order when typing (TNL-10980). One of the prop names of the TinyMceWidget that is imported in course-authoring had to be changed, so this goes together with https://github.com/openedx/frontend-app-course-authoring/pull/575.
2023-08-21 16:43:46 -04:00
German
fda1208660 feat: add xpert summaries configuration by default for units (#567)
* feat: add xpert summaries configuration by default for units
2023-08-21 16:14:39 -03:00
Farhaan Bukhsh
b7a04e17da fix: Fixing the accessing of undefined variables in video
Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
2023-08-21 10:11:56 +05:30
Farhaan Bukhsh
f822d95d6a fix: Used Dropzone instead of having custom component
This PR fixes style component and remove any new component introduced.
We introduce a new thumbnail for setting page as well.

Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
2023-08-21 10:11:56 +05:30
kenclary
12266836eb Merge pull request #378 from openedx/kenclary/supplemental
fix: revert to blank-url version of error handling, for now
2023-08-18 14:09:29 -07:00
Ken Clary
3c1f870aac fix: revert to blank-url version of error handling, for now 2023-08-18 13:46:57 -07:00
kenclary
6de926ce7e Merge pull request #377 from openedx/kenclary/supplimental
fix: don't even bother fetching block ancestor in v2.
2023-08-17 17:37:31 -04:00
Ken Clary
9079196309 fix: don't even bother fetching block ancestor in v2. 2023-08-17 14:23:13 -07:00
Kristin Aoki
b65f4f2b74 feat: bump frontend-lib-content-components (#569) 2023-08-17 11:26:39 -04:00
Kshitij Sobti
fb7caffdd5 feat: Allow selecting my multiple filters in video gallery
The sort and filter UI of the video gallery was not working, this fixes that
issue, and also adds a new UI for filering videos that allows filtering videos
to include more than one status.

It also fixes the hooks related to VideoGallery to avoid potential bugs in the
future and updates tests to use react testing library instead of enzyme.

It also reduces the padding in gallery page.
2023-08-17 13:21:47 +05:30
Kristin Aoki
9438a5b89a feat: add typeahead component (#375) 2023-08-16 18:18:06 -04:00
David Nuon
530c355787 fix: Add enabled badge to xpert settings tile (#566)
* feat: Add "Enabled" badge to xpert settings tile

* fix: Update model with state instead of non-existent prop from response
2023-08-15 09:17:58 -07:00
sundasnoreen12
fc21e22afb test: added test cases of discussion restriction (#556)
* test: added test cases of discussion restriction

* refactor: added null default value for dataTestId

---------

Co-authored-by: SundasNoreen <sundas.noreen@arbisoft.com>
2023-08-15 14:38:45 +05:00
Peter Kulko
f9bc5c4927 feat: created Grading page (#557) 2023-08-14 14:44:01 -04:00
Kristin Aoki
484b141328 fix: overflow-y scroll behavior (#565) 2023-08-14 12:10:53 -04:00
Kristin Aoki
dc0762312e feat: bump frontend-lib-content-components (#562) 2023-08-11 14:29:57 -04:00
Kristin Aoki
e9c0f6cc82 fix: selectedVideo undefined error (#374) 2023-08-11 14:05:07 -04:00
Raymond Zhou
33f46be993 feat: flcc to 1.168.0 (#561) 2023-08-11 13:05:02 -04:00
Kristin Aoki
d1c176cfc8 fix: width and height of asset preview (#558) 2023-08-10 16:54:51 -04:00
Raymond Zhou
a18c45f0db feat: call failRequest on fetch failure (#373) 2023-08-10 12:58:14 -04:00
David Nuon
17d14968fa fix: Change wording to not crowd xpert tile in preferences page (#560) 2023-08-09 12:40:14 -07:00
Kristin Aoki
df51130fce feat: bump frontend-lib-content-components (#559) 2023-08-09 15:22:41 -04:00
Kristin Aoki
669fbfb3d2 fix: blockId checks for v1 or v2 libraries (#372) 2023-08-09 14:55:46 -04:00
Kristin Aoki
bc05d2c01e feat: upgrade frontend-lib-content-components (#554) 2023-08-08 13:03:08 -04:00
Kristin Aoki
918370f743 fix: error return while editor loading (#369) 2023-08-08 12:41:15 -04:00
ruzniaievdm
a0e37c0357 feat: Added Schedule and Details MFE page (#547) 2023-08-08 09:49:53 -04:00
sundasnoreen12
a218e7e5f8 test: added test cases for hide discussion tab (#552)
Co-authored-by: SundasNoreen <sundas.noreen@arbisoft.com>
2023-08-08 14:11:41 +05:00
David Nuon
f2a4386892 Update verbiage for Xpert Settings (#550)
* chore: Update verbiage for Xpert configuration screen

* fix: Change "generate" to "display" in xpert modal text

* fix: Updated learn more link

* fix: Change link and add targets
2023-08-07 07:17:40 -07:00
Kristin Aoki
c9b111a022 fix: remove env variable for files and uploads (#549) 2023-08-04 14:25:05 -04:00
Kristin Aoki
b9feb50a2c feat: add files and uploads page (#541) 2023-08-04 11:57:44 -04:00
ruzniaievdm
d25ae09273 feat: Add TinyMceWidget on export (#365) 2023-08-03 12:44:07 -04:00
Zachary Hancock
7fdf8da8ed fix: load up-to-date config on studio fetch (#548) 2023-08-01 15:37:13 -04:00
David Nuon
1dba6208a5 feat: configuration for xpert unit summaries (#540)
Adds setting modal for Xpert unit summaries

Includes hiding the config section for xpert summary - 
this is done based on a flag from 3d113d267c
2023-08-01 09:07:08 -04:00
Kristin Aoki
9f4422d1b9 fix: ui bugs (#542) 2023-07-31 17:23:07 -04:00
kenclary
38b85f70ac Merge pull request #343 from Mashal-m/mashal-m/react-upgrade-to-v17
feat: update react & react-dom to v17
2023-07-31 14:54:39 -04:00
mashal-m
cc4a7cc83d build: update lock file 2023-07-31 16:52:05 +05:00
mashal-m
3f98349f94 build: update lock file 2023-07-31 16:47:50 +05:00
mashal-m
ed7e98b6ea Merge branch 'main' of https://github.com/openedx/frontend-lib-content-components into mashal-m/react-upgrade-to-v17 2023-07-31 16:23:46 +05:00
Omar Al-Ithawi
8bfc3f2945 feat: include paragon in atlas pull (#538)
This pull request is part of the [FC-0012 project](https://openedx.atlassian.net/l/cp/XGS0iCcQ) which is sparked by the [Translation Infrastructure update OEP-58](https://open-edx-proposals.readthedocs.io/en/latest/architectural-decisions/oep-0058-arch-translations-management.html#specification).
2023-07-25 11:16:29 -04:00
Kristin Aoki
0e1a7e2603 feat: make placeholder depend on api response (#537) 2023-07-25 10:32:31 -04:00
sundasnoreen12
cc7fc6a9e1 chore: add paragon messages (#530) (#534)
Co-authored-by: Mashal Malik <107556986+Mashal-m@users.noreply.github.com>
2023-07-24 19:13:20 +05:00
Mashal Malik
da1e7a0277 chore: add paragon messages (#530) 2023-07-21 11:08:39 +05:00
kenclary
a935d296c9 Merge pull request #355 from openedx/kenclary/TNL-10743
feat: more/correct v2 url handling. TNL-10742
2023-07-20 15:31:33 -04:00
Jesper Hodge
3565741839 fix image resize (#299)
Description:
This is a bug where the image resizing in text editor and problem editor was completely broken. Putting in a text value when the aspect ratio lock was enabled would change both values but not to the size you wanted. If you disabled the lock, not just one but both values would change.

This is a problem that mostly affects images that are rectangular, not square. There's an example image below which is one that caused problems on prod.

Main fixes:
when I keep the image ratio locked, I can change one value (like width) and the other will jump to the proportionate value, but rounded to full pixels.
when I unlock the aspect ratio and change a value, then click save on the image dimension modal, only the one value will change, which will stretch the image in whatever direction. This is reflected in the tinymce image and then the updated value will appear when I reopen the image dimension modal. It is not possible to reset the image to the original dimensions any longer. The new values are saved.
The image dimensions in the edit image settings modal should always reflect the actual dimensions of the image when I look at it e.g. in the course outline. (Otherwise I may click save and the image is squished.)
There was a problem with deselecting an image: when you edit image dimensions and then save or press cancel, the "edit image" button will not disappear, but the image is not selected anymore. When you do not click anything else but immediately click on this button, sometimes (at least the second or third time you do this) this will throw an error. I fixed it so it will just open the default "select image" modal.
Other requirements:
Resizing the image means that when I open the dimensions update, I see the new dimensions.
Images in the editor are now displayed with the correct dimensions, proportional or stretched, if those dimensions don't exceed the size of the editor.
A known smaller bug emerging from this is that when you have more than one instance of the same image in the same editor, you cannot get or set its dimensions correctly. I believe I have gotten it into the following state: When you click one of the copies, you will either get the correct dimensions of the selected copy, or if not, it will display the original image dimensions. When you edit the dimensions, the correct copy of the image will be updated.
Out of Scope:
This cannot handle more than one instance of the same image properly. There will be a separate bug issue for this.
Sometimes, when you edit image dimensions and then reopen the image dimension modal, the dimensions will be null and thus just not appear in the modal - randomly. This is a bug as well.
2023-07-20 14:52:21 -04:00
Ken Clary
7178e5e4c9 feat: more/correct v2 url handling. TNL-10742 2023-07-20 14:37:22 -04:00
Raymond Zhou
4a5eaaf15e feat: setup game editor work (#363) 2023-07-20 04:59:54 -04:00
Peter Kulko
87ead24e20 feat: added Advanced settings page (#521)
Co-authored-by: sendr <sendr84@gmail.com>
Co-authored-by: ruzniaievdm <ruzniaievdm@gmail.com>
2023-07-19 10:45:50 -04:00
Kristin Aoki
e05e6325c9 fix: marketing base url typo (#533) 2023-07-17 11:35:39 -04:00
Leangseu Kim
b090c8c153 feat: add open responses card to page and resources 2023-07-13 11:07:58 -04:00
Kristin Aoki
7939af4737 fix: partner portal typo (#362) 2023-07-11 14:10:04 -04:00
Kristin Aoki
3c3dfeb325 feat: use new studio footer (#532) 2023-07-11 13:05:10 -04:00
Kristin Aoki
3586307ee7 feat: react based studio footer (#359) 2023-07-11 10:30:52 -04:00
Ghassan Maslamani
2bc447fab0 fix: resolve #360 make style consistent across outlines
This changes make the style consistent across the first three
 headline line levels

 1. The first level add CSS prop initial
 2. The second level change prop from capital to inital
 3. No change
2023-07-11 14:50:29 +03:00
Jesper Hodge
f942ef9594 fix enable rules of hooks (#329)
This is a PR enabling eslint "rules-of-hooks".

This lint rule catches some very annoying bugs and enforces you to use correct naming for custom hooks (prepend with "use"), which is a mandatory react rule and important for a number of reasons.

I added eslint-disable statements wherever the rules are broken, and if this is merged, I would expect new code not to break the rules any longer.

I strongly suggest that the much better way to extract and reuse hooks and logic from components is the way the community does it and the React docs suggest. The new React docs are really fantastic in this regard and you can use the patterns found there very well to have an excellent application. https://react.dev/learn/reusing-logic-with-custom-hooks
2023-07-05 15:11:07 -04:00
mashal-m
505704e8f3 build: update edx pkgs and peer Dep 2023-07-05 16:44:24 +05:00
kenclary
7ee8cc7fb1 feat: update flcc to 1.62.0 (#528) 2023-06-30 15:41:54 -04:00
Raymond Zhou
912fff9b0f feat: update flcc to 1.61.0 (#527) 2023-06-30 11:59:46 -04:00
kenclary
c09faa3b09 Merge pull request #354 from open-craft/farhaan/bb-7522-fix-spinner-for-video
feat: Add loading spinner to the video upload page
2023-06-30 11:10:50 -04:00
kenclary
2ab7aa5cea Merge pull request #350 from open-craft/navin/hide-hide-option
refactor: hide switch to hide selected videos
2023-06-30 11:10:42 -04:00
Kristin Aoki
2c71385ce7 fix: env custom pages conditional render (#525) 2023-06-29 10:45:56 -04:00
Raymond Zhou
436fdfc470 feat: parse out explanation text (#358) 2023-06-28 14:05:31 -04:00
Kristin Aoki
139457087b feat: add custom pages (#510) 2023-06-27 16:26:35 -04:00
Raymond Zhou
3a26285bd1 feat: update flcc to 1.60.0 (#523) 2023-06-27 13:19:26 -04:00
Raymond Zhou
86b67022ba feat: parser changes (#356) 2023-06-27 12:50:07 -04:00
Navin Karkera
7a53de4f2d refactor: hide switch to hide selected videos
The backend for hiding selected videos is not implemented and it is currently not required, so this commit hides the option.
2023-06-24 19:39:38 +05:30
Farhaan Bukhsh
ab640fb561 refactor: Moved the function to hooks in order to keep the components
dumb

It is done to make sure the business logic is not in a component and can
be individually tested.

Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
2023-06-23 20:01:33 +05:30
Farhaan Bukhsh
4d684d620e fix: Added tests for video upload
Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
2023-06-23 20:01:33 +05:30
Kristin Aoki
714c946f0f fix: persisting title from previous editor load (#353) 2023-06-22 19:22:40 -04:00
kenclary
769cdb08fb Merge pull request #344 from jansenk/events
feat: send event on checkbox change
2023-06-22 10:37:20 -04:00
jansenk
1a0cc2db2a style: rename function 2023-06-22 09:26:11 -04:00
Farhaan Bukhsh
a86b844208 feat: Add spinner to video element to load
Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
2023-06-22 13:46:08 +05:30
Kristin Aoki
c8e85fae0b fix: failure toast and close modal bugs (#352) 2023-06-21 13:48:54 -04:00
mashal-m
42210e7c89 build: update lock file 2023-06-21 14:16:10 +05:00
mashal-m
ef662f9ceb Merge branch 'main' of https://github.com/openedx/frontend-lib-content-components into mashal-m/react-upgrade-to-v17 2023-06-21 14:10:01 +05:00
Mashal Malik
8a2c725eda chore: update lock version file (#351) 2023-06-21 05:03:50 -04:00
mashal-m
530183a297 build: update paragon version 2023-06-19 15:55:53 +05:00
Kristin Aoki
b0fef766eb feat: add ErrorAlert to exports (#349) 2023-06-15 15:28:12 -04:00
Raymond Zhou
e2c1deaeb3 feat: flcc to 1.157.0 (#520) 2023-06-14 14:07:42 -04:00
Raymond Zhou
83f034e500 feat: save spacing for richText parser (#348) 2023-06-14 12:04:44 -04:00
Kristin Aoki
61baf1a886 chore: bump frontend-lib-content-components (#518) 2023-06-13 16:51:29 -04:00
kenclary
ac2444c258 Merge pull request #347 from openedx/kenclary/TNL-10742
feat: v2 xblocks should use v2 url. TNL-10742
2023-06-13 16:32:44 -04:00
Kristin Aoki
665a53a713 feat: add draggable list (#342) 2023-06-13 16:31:17 -04:00
Kristin Aoki
8eb10b7b12 feat: update returnToUnit to include api response (#346) 2023-06-13 15:29:04 -04:00
Ken Clary
ffd311881a feat: v2 xblocks should use v2 url. TNL-10742 2023-06-13 15:09:04 -04:00
Maria Grimaldi
51e5e7126c fix: cast progress graph configuration to string (#495) 2023-06-12 11:10:59 -04:00
jansenk
491d870cd2 style: quality 2023-06-12 10:25:04 -04:00
jansenk
e2535b2467 feat: send event on checkbox change 2023-06-12 09:56:32 -04:00
jansenk
0111d1c2f5 fix: remove param from end of video features url 2023-06-12 09:56:07 -04:00
mashal-m
86b0c4da82 feat: update react & react-dom to v17 2023-06-12 18:13:45 +05:00
Jenkins
a53a93ccee chore(i18n): update translations 2023-06-11 17:32:51 -04:00
Kristin Aoki
82b0f67f11 feat: add returnUrl prop to editors (#341) 2023-06-08 13:32:53 -04:00
Dmytro
e980f1f20e fix: disable invalid link Video Uploads (#511) 2023-06-08 11:06:11 -04:00
Kristin Aoki
fac9eab496 feat: bump frontend-lib-content-components (#509) 2023-06-06 11:38:57 -04:00
Kristin Aoki
f3c4669604 fix: doc typos (#340) 2023-06-06 11:00:37 -04:00
ayesha waris
1b1afcf195 feat: integrated backend discussions restriction with UI (#507)
* feat: integrated backend discussions restriction with UI

* refactor: code refactoring

* test: fixes test cases

* refactor: discussion restriction component

---------

Co-authored-by: ayesha waris <73840786+ayeshoali@users.noreply.github.com>
Co-authored-by: Awais Ansari <awais.ansari63@gmail.com>
2023-06-06 14:55:19 +05:00
kenclary
43b6ec7708 Merge pull request #339 from openedx/revert-338-kenclary/update-react
Revert "chore: update react to 17, etc. TNL-10715"
2023-06-05 16:43:00 -04:00
kenclary
6317c46120 Revert "chore: update react to 17, etc. TNL-10715" 2023-06-05 16:38:58 -04:00
Kristin Aoki
831c6096cb fix: choice parsing for answers with multi lines (#337) 2023-06-02 11:13:33 -04:00
kenclary
dd79d49533 Merge pull request #338 from openedx/kenclary/update-react
chore: update react to 17, etc. TNL-10715
2023-06-02 09:17:51 -04:00
Ken Clary
648e700818 chore: update react to 17, etc. TNL-10715 2023-06-01 16:11:58 -04:00
Kristin Aoki
788f671626 feat: bump frontend-lib-content-components (#506) 2023-05-31 14:27:04 -04:00
Kristin Aoki
88629d6df1 fix: olx parsers with formatted text (#336) 2023-05-30 11:42:59 -04:00
Jenkins
ac7b4c9fcf chore(i18n): update translations 2023-05-28 17:32:49 -04:00
Bilal Qamar
2199a24dd7 feat: upgraded to node v18, added .nvmrc and updated workflows (#281)
Co-authored-by: Abdullah Waheed <abdullah.waheed@arbisoft.com>
2023-05-26 09:52:56 -04:00
Mashal Malik
9a4af8ff2e feat: upgraded to node v18, added .nvmrc and updated workflows (#464)
* feat: upgraded to node v18, added .nvmrc and updated workflows

* feat: upfate validate workflow

* feat: update validate workflow

* fix: update lock file

* refactor: update validate file

* build: update pkg

* refactor: updated packages

* build: updated frontend-build, frontend-platform, component-footer & component-header packages

* refactor: updated workflow

* refactor: updated workflow

* refactor: updated workflow

* build: update commit file

* build: update lock file

* refactor: update workflow

* refactor: update workflow

* refactor: update workflow

* refactor: update workflow

* build: update pkg

* build: update pkgs

* build: update lock file

---------

Co-authored-by: Bilal Qamar <59555732+BilalQamar95@users.noreply.github.com>
2023-05-25 13:49:24 +05:00
Kristin Aoki
d80b6faaad feat: replace canvas with jest-canvas-mock (#335) 2023-05-24 09:56:39 -04:00
kenclary
9cfd8013d2 feat: update frontend-lib-content-components to v1.151.2 (#503) 2023-05-22 11:06:42 -04:00
Kristin Aoki
e0c0c918d0 fix: feedback clearing on type change (#334) 2023-05-22 09:57:22 -04:00
Jenkins
74f5a0e8ee chore(i18n): update translations 2023-05-21 17:32:46 -04:00
sundasnoreen12
0d67c2588d feat: implemented discussion restriction UI (#494)
* feat: implemented discussion restriction UI

* refactor: fixed UI figma design issues

* refactor: fixed 2nd review points

* refactor: fixed review issues regarding confirmation popup

* refactor: changed tab component to button group

* perf: performance improvement changes

* refactor: fixed memorization issues

* refactor: fixed memo issues

---------

Co-authored-by: sundasnoreen12 <sundasnoreen12@ggmail.com>
Co-authored-by: SundasNoreen <sundas.noreen@arbisoft.com>
2023-05-19 17:16:48 +05:00
Kristin Aoki
738f501cf9 feat: add new header and page routes (#501) 2023-05-18 17:08:38 -04:00
kenclary
9239d2b8b1 Merge pull request #333 from open-craft/chris/video-editor-flow-fix
fix: Temporal testing line deleted from VideoUploadEditor::hooks.js
2023-05-18 12:42:39 -04:00
XnpioChV
3bdafd6e36 fix: Temporal testing line deleted from VideoUploadEditor::hooks.js 2023-05-18 11:31:35 -05:00
kenclary
aee971924f Merge pull request #326 from open-craft/chris/FAL-3383-new-video-editor-flow
[FAL-3383] Implement new video UX flow on new video editor
2023-05-18 11:49:10 -04:00
Chris Chávez
ff6a5d99d6 [FAL-3383] Implement new video UX flow on new video editor (#498)
* feat: Video Gallery URL updated to match to the new flow needs

* chore: Video Gallery Url updated and blockId added
2023-05-18 09:55:33 -04:00
Raymond Zhou
a46a34412c feat: update flcc to 1.150.1 (#500) 2023-05-17 15:43:48 -04:00
Raymond Zhou
741b83bdf2 fix: delete answers without changing expandable (#328) 2023-05-17 14:22:51 -04:00
Kristin Aoki
be8f9ecc86 feat: add raw olx settings parser (#332) 2023-05-15 15:12:32 -04:00
Mashal Malik
084d61ffa1 Update codecov and add browserslist config (#209)
* refactor: update code cov and add browserlist config
2023-05-11 14:44:48 -04:00
Kristin Aoki
99cd3bf1d9 chore: refactor OLXParser and related doc strings (#330) 2023-05-11 13:30:47 -04:00
Jesper Hodge
8cd222b9fa Add ADR for organizing our folder structure in a feature-based manner (#296)
* docs: add feature based application organization adr

* Update 0006-feature-based-application-organization.rst

* docs: update adr
2023-05-11 12:06:11 -04:00
Kristin Aoki
db6c3172de feat: bump frontend-lib-content-components (#499) 2023-05-11 10:31:23 -04:00
Kristin Aoki
1d66a9d14d feat: remove matlab api widget and refs (#331) 2023-05-10 15:17:16 -04:00
Omar Al-Ithawi
0d38279950 feat: use atlas in make pull_translations (#490)
Changes
-------
 - Bump frontend-platform to bring `intl-imports.js` script
 - Move all i18n imports into `src/i18n/index.js` so `intl-imports.js` can
   override it with latest translations
 - Add `atlas` into `make pull_translations` when `OPENEDX_ATLAS_PULL`
   environment variable is set.

Refs: [FC-0012 project](https://openedx.atlassian.net/l/cp/XGS0iCcQ) implementing Translation Infrastructure OEP-58.
2023-05-09 10:13:50 -04:00
Kristin Aoki
109334a9bf fix: show setting note for course set social share (#327) 2023-05-09 09:27:32 -04:00
Jesper Hodge
003f33152d docs: add adr with test naming conventions (#320)
* docs: add adr with test naming conventions

* docs: fix rst

* docs: add further reading section

* Update docs/decisions/0006-test-names.rst
2023-05-05 16:20:19 -04:00
XnpioChV
5c432a03db test: Adding missing test to reach coverage/patch 2023-05-05 14:10:18 -05:00
Kristin Aoki
3dd28082ea chore: bump frontend-lib-content-components (#497) 2023-05-05 11:01:36 -04:00
XnpioChV
15393f3da1 style: Fixing styles on gallery, editor and uploader video page 2023-05-04 18:00:06 -05:00
XnpioChV
135e0d7859 feat: Adding navigation on the url input of the video uploader page 2023-05-04 17:31:56 -05:00
XnpioChV
e20dedd0a5 feat: Allow to import transcripts from selected video 2023-05-04 17:31:55 -05:00
Pooja Kulkarni
359a5fd505 feat: Create new editor page for video upload 2023-05-04 17:31:54 -05:00
XnpioChV
da00ad7539 chore: All selection and save flow added 2023-05-04 17:01:42 -05:00
XnpioChV
23593b44fe chore: Error message on unselected video added & replace video button navigation added 2023-05-04 16:59:31 -05:00
XnpioChV
d2b3edad57 feat: Adding blockId to the Gallery. Adding cancel and next navigation 2023-05-04 16:58:36 -05:00
Raymond Zhou
adc21735fc fix: expandable textarea should save on blur (#325) 2023-05-04 17:17:14 -04:00
Kristin Aoki
34d6fcc552 feat: allow social share widget in libraries (#324) 2023-05-04 13:25:28 -04:00
Kristin Aoki
767283cbc6 chore: bump frontend-lib-content-components (#496) 2023-05-03 10:56:28 -04:00
Kristin Aoki
c49779a293 feat: add a button to return to studio (#322) 2023-05-03 10:35:02 -04:00
Kristin Aoki
6aaedfc500 feat: add social share widget (#323) 2023-05-03 09:55:31 -04:00
Jenkins
0066902127 chore(i18n): update translations 2023-04-30 17:32:45 -04:00
Raymond Zhou
9a567b875e feat: update flcc to 1.45.0 (#492) 2023-04-27 15:13:29 -04:00
Raymond Zhou
d68bdf9dc6 feat: page errors indefinitely with no ancestor (#321) 2023-04-27 14:38:59 -04:00
kenclary
76ad7d8bda Merge pull request #262 from open-craft/pooja/bb7157-add-video-upload-feature
[BB-7157] Create new editor page for video upload
2023-04-26 13:41:37 -04:00
Pooja Kulkarni
bef6813d2c feat: Create new editor page for video upload 2023-04-26 11:36:26 -04:00
Kristin Aoki
a7f877caf5 feat: bump flcc and paragon (#491) 2023-04-26 10:23:03 -04:00
Kristin Aoki
bade92f613 feat: remove custom css with paragon upgrade (#319) 2023-04-26 09:55:40 -04:00
Kristin Aoki
21841fe09f fix: editable header width (#318) 2023-04-26 09:13:04 -04:00
Raymond Zhou
e75928a774 feat: update flcc to 1.142.0 (#489) 2023-04-25 16:31:58 -04:00
Raymond Zhou
8676ba257c feat: default randomization to never (#317) 2023-04-25 16:14:40 -04:00
kenclary
eaab798da2 Merge pull request #278 from open-craft/chris/FAL-3375-video-selection-gallery
[FAL-3375] Feat: Video selection gallery page
2023-04-25 15:54:05 -04:00
Chris Chávez
4b7f46852b [FAL-3375] Feat: Adding video selection gallery page to the routes (#461)
* feat: Adding video selection gallery page to the routes

* test: CourseAuthoringRoutes.test.jsx added
2023-04-25 12:45:00 -04:00
Pooja Kulkarni
1e0c128ad6 fix: make blockid parameter optional (#455) 2023-04-25 11:36:58 -04:00
Yoiber
e3887129fc chore(i18n): add more languages (#450)
* chore(i18n): add more languages

* chore(i18n): Pylint fixes

* chore(i18n): Typo to named the imports
2023-04-25 08:58:37 -04:00
ayesha waris
2eaf882734 fix: only global staff can see 2 edx discussion providers in settings (#477)
* fix: only global staff can see 2 edx discussion providers in settings

* test: adds and updated test cases for app list

* refactor: memoized showoneedxprovider constant

---------

Co-authored-by: ayesha waris <73840786+ayeshoali@users.noreply.github.com>
2023-04-25 15:00:35 +05:00
Jenkins
284c402a49 chore(i18n): update translations 2023-04-23 17:32:44 -04:00
XnpioChV
5695f92127 chore: Padding on gallery fixed 2023-04-21 18:06:10 -05:00
XnpioChV
36e56588cb test: Adding test for VideoGallery and SelectionModal 2023-04-21 18:06:09 -05:00
XnpioChV
b3bced9875 feat: New screens for loading, no videos and error states. Also tests added 2023-04-21 18:06:08 -05:00
XnpioChV
71efe876d3 style: adding more styles on the selection modal 2023-04-21 18:04:06 -05:00
XnpioChV
3b69958427 feat: Search and filters added 2023-04-21 18:04:05 -05:00
XnpioChV
b78e58cd2a feat: New request hook to fetch videos 2023-04-21 18:04:05 -05:00
XnpioChV
14504073e0 feat: Video selection page created
The SelectImageModal component has been refactored so that it can also be used on the video selection page; and all its child components.
Now this component is called SelectionModal and is used both for the image selector and in this new video selection screen.
The assets api has been used to get the videos.
2023-04-21 18:04:04 -05:00
Raymond Zhou
2a5f6795d3 feat: tinyMCE formatting and parsing (#311) 2023-04-21 11:42:10 -04:00
connorhaugh
802d328f4a feat: remove layered button (#314) 2023-04-21 11:04:23 -04:00
connorhaugh
d08eb0e3a9 feat: upgrade flcc (#487) 2023-04-21 10:41:37 -04:00
Kristin Aoki
c37e6957f6 fix: advanced editor not saving (#315) 2023-04-21 10:25:01 -04:00
Kristin Aoki
492ee27d8e fix: settings helper text font size (#313) 2023-04-21 09:19:39 -04:00
Raymond Zhou
80461755d1 feat: hint trash button should not be squished (#312) 2023-04-20 16:49:43 -04:00
Kristin Aoki
76b7623cb0 feat: bump frontend-lib-content-components 1.137.0 (#486) 2023-04-20 13:41:47 -04:00
Kristin Aoki
b674cd0cb0 feat: add no answer confirmation alert (#310) 2023-04-20 12:49:07 -04:00
connorhaugh
1e25091698 feat: add hotjar tracking (#485) 2023-04-19 16:56:18 -04:00
Kristin Aoki
8f15480c28 feat: update problem descriptions (#309) 2023-04-18 13:23:18 -04:00
Kristin Aoki
4f1dc98ecc fix: settings font size and spacing (#308) 2023-04-18 11:56:20 -04:00
Jenkins
1289f7d4e2 chore(i18n): update translations 2023-04-16 17:27:42 -04:00
Kristin Aoki
eb1b2eb883 feat: bump frontend-lib-content-components 1.135.1 (#483) 2023-04-14 16:44:30 -04:00
Kristin Aoki
405003045c fix: textArr join error in syntaxChecker (#307) 2023-04-14 16:28:16 -04:00
Kristin Aoki
74e45139bf feat: bump frontend-lib-content-components 1.135.0 (#482) 2023-04-14 12:20:52 -04:00
Kristin Aoki
1d2a4c212d feat: add xml linter to code mirror (#306) 2023-04-14 11:58:49 -04:00
Raymond Zhou
f9a240ade4 feat: update FLCC to 1.134.0 (#481) 2023-04-13 17:59:54 -04:00
Raymond Zhou
4b7b1c91ec feat: further number parse changes (#305) 2023-04-13 17:31:03 -04:00
Raymond Zhou
b09e7f3683 feat: update FLCC to ver1.133.0 (#480) 2023-04-13 13:12:20 -04:00
Raymond Zhou
286d2209cb feat: XMLBuilder and XMLParser performing unwanted processing in encoding / parsing (#304) 2023-04-13 12:25:43 -04:00
sundasnoreen12
b19d52555f refactor: removed all extra messages files (#475)
Co-authored-by: sundasnoreen12 <sundasnoreen12@ggmail.com>
2023-04-13 17:13:49 +05:00
Raymond Zhou
ab4dd9a4a8 feat: update FLCC to 1.132.0 (#476) 2023-04-11 14:17:58 -04:00
Raymond Zhou
1e6d8b51e4 feat: show reset for advanced problem editor (#302) 2023-04-11 12:53:53 -04:00
connorhaugh
73a4b893f7 Feat parse solution as sibling (#303)
* fear: parse solution as siblings

* feat: add error resliancy to solution check.
2023-04-11 09:23:02 -04:00
connorhaugh
a94942a36e Feat update flcc for cat 2 part deux (#473)
* feat: update flcc for cat-2

* feat: updte flcc for cat-2 part deux
2023-04-10 15:55:59 -04:00
connorhaugh
d58f349e1f feat: parse solution as siblings (#301)
this creates the ability for explanations to work if they are:

Not a child of a response type
Use an h2 tag for the explanation title
do not use the "Explanation" title.
2023-04-10 15:37:02 -04:00
connorhaugh
ab7c51994c feat: update flcc for cat-2 (#472) 2023-04-10 10:58:59 -04:00
connorhaugh
8a7dbdf4be fix: parse description/label as children (#300)
* fix: parse description/label as children

* fix: bounce problem tag attributes to advanced
2023-04-10 10:38:30 -04:00
Kristin Aoki
284139e247 feat: add check to sanitize answer ranges (#298) 2023-04-07 09:04:15 -04:00
Raymond Zhou
67967a92cf feat: update FLCC to 1.129.0 (#471) 2023-04-06 14:12:09 -04:00
Raymond Zhou
9a19711755 feat: revert group feedback expanded text field (#297) 2023-04-06 13:40:13 -04:00
Kristin Aoki
4cfc5a6ea6 feat: add alert to notify video id changes (#295) 2023-04-06 12:34:07 -04:00
connorhaugh
6efa8c5356 feat: upgrade flcc to 125 (#470) 2023-04-05 10:24:15 -04:00
connorhaugh
82cfa9897c feat: default scoring to 1, not zero. (#294)
Since every course has graded problems, every course author will be editing or creating problems, and every learner will need to receive points for any problem in a graded assignment in order to earn a passing grade. So if what I encountered and am reporting here is reproducible, this should probably be addressed before the problem editor feature is released.
2023-04-05 09:36:20 -04:00
Kristin Aoki
c28669f5b2 feat: upgrade flcc package to 1.126.1 (#469) 2023-04-04 09:54:05 -04:00
Kristin Aoki
bd964854de fix: misspelling of explanation in description (#293) 2023-04-03 13:56:50 -04:00
Kristin Aoki
f99421f493 feat: add catch for script tags in question (#292) 2023-04-03 11:50:35 -04:00
connorhaugh
c2b67429d3 feat: add answer range (#291)
* feat: add answer range

* feat: add margin for doropdown

* fix: improve test converage
2023-03-30 10:34:45 -04:00
Omar Al-Ithawi
98ec415e2b fix: let make extract_translations find messages (#290)
Otherwise it'll complain for not finding any message.

`defineMessage` ensures that React i18n static code collector is able to
find the messages.

References
----------

This pull request is part of the [FC-0012 project](https://openedx.atlassian.net/l/cp/XGS0iCcQ) which is sparked by the [Translation Infrastructure update OEP-58](https://open-edx-proposals.readthedocs.io/en/latest/architectural-decisions/oep-0058-arch-translations-management.html#specification).

Check the links above for full information about the overall project.
2023-03-29 10:17:25 -04:00
kenclary
270f4a8a12 chore: upgrade frontend-lib-content-components to 1.124.0. (#468) 2023-03-27 13:57:44 -04:00
kenclary
e7d69f4e5d Merge pull request #289 from openedx/kenclary/TNL-10535
fix: ErrorPage rewritten as functional component with injected i18n. TNL-10535
2023-03-24 16:40:10 -04:00
Ken Clary
f2ad93789f fix: ErrorPage rewritten as functional component with injected i18n. TNL-10535 2023-03-24 16:22:32 -04:00
connorhaugh
3bfa83220f feat: error on funky multiple choice tags. (#288)
This change increases the frequency at which odd tags (like shuffle) on multiple choice problems don't go to the visual editor.
Instead, they will now divert to the advanced editor. There might be other places where we need to follow a similar pattern, but we don't know those yet.
2023-03-24 10:05:21 -04:00
Kristin Aoki
7ef1963327 feat: add default advanced setting ui callouts (#285) 2023-03-23 17:37:13 -04:00
Ken Clary
4410f0a544 Merge branch 'keith/video-preview-widget' 2023-03-23 14:59:56 -04:00
Ken Clary
ed0aa88841 chore: fixup from rebase 2023-03-23 14:59:38 -04:00
Ken Clary
7080189a65 chore: fixup from rebase 2023-03-23 14:56:49 -04:00
Ken Clary
7a27b65a8c Merge branch 'keith/video-preview-widget' 2023-03-23 14:19:24 -04:00
Keith Grootboom
57a558f258 chore: use Hyperlink instead 2023-03-23 14:18:16 -04:00
Keith Grootboom
475c32463a refactor: review changes to use existing youtube function 2023-03-23 14:18:16 -04:00
Keith Grootboom
b4fb88c73c feat: add video preview widget to settings modal 2023-03-23 14:18:10 -04:00
kenclary
c7c0d5e100 Merge pull request #258 from open-craft/0x29a/video-duration-widget-improvements
feat: video duration section improvements [BD-12]
2023-03-23 11:47:31 -04:00
kenclary
21e01d1b77 Merge pull request #284 from open-craft/maxim/transcripts-section-improvements
feat: offer to import youtube transcripts dynamically
2023-03-23 11:18:31 -04:00
Kristin Aoki
641a169e6f feat: upgrade flcc to 1.11.8.0 (#466) 2023-03-22 14:52:22 -04:00
connorhaugh
df5af3efd9 feat: tolerance Setting Widget (#286)
* feat: tolerance Setting Widget

* fix: tolerance position and percent summary
2023-03-22 14:31:02 -04:00
Kristin Aoki
16003a7f4a feat: move explanation to main body of editor (#287) 2023-03-22 09:36:01 -04:00
Maxim Beder
6c8fca8113 test: add tests for VideoSettingsModal 2023-03-20 15:26:45 +01:00
Maxim Beder
363df38b1f feat: offer to import youtube transcripts dynamically
Before this commit, the popup message which offers to import transcripts
from YouTube would only appear after saving the video settings, and then
reopenning the editor.

With this commit, the popup appears dynamically, i.e. whenever a URL is
changed to a YouTube one, certain validations and check take place,
after which, if possible to do so, user will be offered to import
transcripts from the YouTube video.
2023-03-20 14:04:38 +01:00
kenclary
25e254bbfb chore: update frontend-lib-content-components to 1.117.1. (#465) 2023-03-16 14:46:56 -04:00
Raymond Zhou
da9cb6054c Revert "feat: update switch to Advanced Editor warning (#259)" (#282)
This reverts commit 4a24d25f22.
2023-03-16 13:44:41 -04:00
Kristin Aoki
fa14365d54 feat: update text areas to expandable text areas (#274) 2023-03-16 13:17:01 -04:00
kenclary
84b9dd7e88 Merge pull request #277 from jansenk/jkantor/video-public-allowed-gate
feat: don't show video sharing checkbox unless waffle from studio is active
2023-03-15 16:21:03 -04:00
jansenk
97d0a74fef feat: call video_features cms endpoint for waffle status 2023-03-15 14:39:15 -04:00
jansenk
a895c28c4c feat: hide checkbox when video share not enabled for course 2023-03-15 14:39:15 -04:00
connorhaugh
2dc42e6a46 fix: show answer attempts spacing (#276) 2023-03-14 12:29:25 -04:00
Raymond Zhou
56621ca575 feat: remove general and group feedback (#273) 2023-03-13 12:25:38 -04:00
Mashal Malik
2ea3efed4b Update transifex api from v2 to v3 (#264) 2023-03-13 12:25:12 -04:00
connorhaugh
18fd63ab69 feat: expanding complex text area (#257) 2023-03-13 09:21:17 -04:00
Jenkins
af0ddf532a chore(i18n): update translations 2023-03-12 17:32:39 -04:00
kenclary
b917586fd8 Merge pull request #268 from openedx/kenclary/TNL-10507-1
fix: ProblemEditor useEffect at beginning of component; message for block failed to load. TNL-10507
2023-03-10 18:19:25 -05:00
Ken Clary
55b1e41898 fix: ProblemEditor useEffect at beginning of component; message for block failed to load. TNL-10507 2023-03-10 15:01:39 -05:00
Jesper Hodge
0ce9e4dfd3 fix: answer button accessibility (#271) 2023-03-10 14:18:21 -05:00
kenclary
eaf76c8dee chore: update frontend-lib-content-components to 1.113.0. (#459) 2023-03-10 10:51:50 -05:00
kenclary
b4edfe0bcb Merge pull request #270 from jansenk/jkantor/video-public
feat: Add "allow sharing" checkbox to video editor
2023-03-09 14:28:56 -05:00
Ihor Romaniuk
5c0ca7b706 feat: replace hardcoded edx string with site_name from configs (#425)
* feat: replace hardcoded edx string with site_name from configs

* feat: add ability to obtain site name dynamically

* fix: localize overriding createPortal method
2023-03-09 13:38:22 -05:00
jansenk
4467590a65 test: code review 2023-03-09 13:08:09 -05:00
jansenk
2be52871aa test: update snapshot 2023-03-09 11:38:17 -05:00
jansenk
968f48c55a feat: add checkbox for public video sharing field 2023-03-09 11:38:17 -05:00
Jeremy Ristau
54d4d2cca2 Merge pull request #269 from openedx/maintainership-phase1
chore: add missing maintainership files
2023-03-09 10:22:27 -05:00
Jeremy Ristau
07a84bc133 chore: add CODEOWNERS file to repo, point to tnl
This is related to Maintainership Pilot expectations. This CODEOWNERS file will notify teaching-and-learning team members of PR submissions, but there are currently no additional branch protections related to this ownership.
2023-03-09 09:58:32 -05:00
Jeremy Ristau
7fc149b882 chore: add catalog-info file for Open edX Backstage
This file populates Backstage info for the Open edX community. It helps identify that tnl is the owner of this component. This is in relation to Maintainership OEP-55.
2023-03-09 09:53:39 -05:00
Raymond Zhou
e6b532c71e Feat allow not select feedback for only multi select problem type (#267) 2023-03-08 15:18:57 -05:00
Raymond Zhou
b0c1e4d754 feat: clear save failed status when closing error (#266) 2023-03-07 13:27:55 -05:00
Mashal Malik
530b247c33 refactor: remove unused tranisfex v2 url (#457) 2023-03-06 12:18:02 +05:00
Ihor Romaniuk
a5bc86e948 feat: replace hardcoded logo with logo from configs (#426) 2023-03-03 08:29:12 -05:00
Raymond Zhou
b84c9c006e feat: fix time between attempts label (#263) 2023-03-02 16:46:05 -05:00
Kristin Aoki
5d77dddaf6 feat: add labels and blockquotes to clear format (#261) 2023-03-02 11:43:49 -05:00
connorhaugh
9910937269 feat: flcc 1.109.2 (#456) 2023-03-02 10:48:29 -05:00
connorhaugh
77f030c3fe fix: remove reset widget from advnaced editor 2023-03-02 10:20:22 -05:00
connorhaugh
7a8a182d5a fix: explantion data parse (#260) 2023-03-02 09:20:37 -05:00
Raymond Zhou
4a24d25f22 feat: update switch to Advanced Editor warning (#259) 2023-03-01 16:06:49 -05:00
Raymond Zhou
493ef9026e feat: numeric input UI to allow only correct (#256) 2023-03-01 13:02:27 -05:00
0x29a
897c440f26 test: test the new message, update the snapshot 2023-03-01 17:15:54 +01:00
0x29a
5b543ea93e feat: show "Custom" instead of "Total" when the widget is collapsed 2023-03-01 17:15:41 +01:00
0x29a
0fa18e4199 feat: outline and align right the total label 2023-03-01 16:31:20 +01:00
Kristin Aoki
c65f60ec10 feat: refactor tinymce editor to sharedComponents (#255) 2023-02-28 16:37:52 -05:00
Raymond Zhou
e0c5573c8d feat: tinyMCE config changes (#253) 2023-02-27 13:48:04 -05:00
connorhaugh
1344c289df feat: update flcc (#453) 2023-02-24 11:41:11 -05:00
Feanil Patel
7f4111c12c Update standard workflow files. (#452)
* build: Creating a missing workflow file `self-assign-issue.yml`.

The .github/workflows/self-assign-issue.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.

* build: Creating a missing workflow file `add-remove-label-on-comment.yml`.

The .github/workflows/add-remove-label-on-comment.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.

* build: Updating a missing workflow file `add-depr-ticket-to-depr-board.yml`.

The .github/workflows/add-depr-ticket-to-depr-board.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.
2023-02-24 10:44:48 -05:00
connorhaugh
3c3361c765 fix: file upload on safari (#254)
* fix: file upload on safari

* fix: lint fix
2023-02-24 10:40:56 -05:00
Feanil Patel
a184ac981c Update standard workflow files. (#252)
* build: Creating a missing workflow file `self-assign-issue.yml`.

The .github/workflows/self-assign-issue.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.

* build: Creating a missing workflow file `add-depr-ticket-to-depr-board.yml`.

The .github/workflows/add-depr-ticket-to-depr-board.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.

* build: Creating a missing workflow file `add-remove-label-on-comment.yml`.

The .github/workflows/add-remove-label-on-comment.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.

* build: Updating a missing workflow file `commitlint.yml`.

The .github/workflows/commitlint.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.
2023-02-24 10:40:43 -05:00
Kristin Aoki
76aae38d3a fix: check for if an answer changes to false (#250) 2023-02-23 10:02:22 -05:00
Raymond Zhou
f18353e5fc feat: fix initial advanced problem type olx (#251) 2023-02-22 17:59:27 -05:00
connorhaugh
316f6f2850 feat: reset card link works (#249) 2023-02-17 13:53:09 -05:00
Kristin Aoki
70c0fc6dcf fix: settings col width in advanced view (#233) 2023-02-15 16:37:55 -05:00
Raymond Zhou
6156798e02 feat: move solution tag inside problem type tag (#248) 2023-02-15 12:53:02 -05:00
Raymond Zhou
3f9fc513cf feat: remove Hints widget from advanced editor (#247) 2023-02-15 12:52:52 -05:00
connorhaugh
105fdea8ef feat: upgrade FLCC to 1.101.3 (#451) 2023-02-15 08:41:05 -05:00
connorhaugh
61c99b9b40 feat: add error boundary (#246)
* feat: add error boundary
2023-02-14 15:21:43 -05:00
Raymond Zhou
529ec8ddf2 feat: fix checkbox colors for feedback (#244) 2023-02-14 12:34:55 -05:00
connorhaugh
b4f1676acf feat: add feedback link (#245)
For Problem Editor Beta
2023-02-14 10:20:26 -05:00
connorhaugh
9d91e3f242 feat: update flcc to 1.99.3 (#449) 2023-02-13 12:58:00 -05:00
Kristin Aoki
eeaa0e3f68 fix: getGeneralFeedback selectedFeedback error (#243) 2023-02-10 14:54:56 -05:00
kenclary
0367ef776e Merge pull request #240 from openedx/kenclary/TNL-10433
fix: more correct parsing of settings, for scoring; use empty string instead of null for empty attempts. TNL-10433.
2023-02-10 12:01:10 -05:00
Raymond Zhou
eff3df7115 Feat allow singleselect to accept multiple correct answers (#241) 2023-02-10 11:31:52 -05:00
Kristin Aoki
a22ad54502 fix: update problem description in preview block (#242) 2023-02-10 11:00:49 -05:00
connorhaugh
fdcb3a5e7f feat: update FLCC to 1.99.0 (#447) 2023-02-10 10:22:48 -05:00
Ken Clary
38e262eee0 fix: more correct parsing of settings, for scoring; use empty string instead of null for empty attempts. TNL-10433. 2023-02-10 09:17:44 -05:00
connorhaugh
d69d3e1ce7 feat: Group, General Feedback Settings, Randomization
This Ticket adds three new settings widgets to the Problem Editor:
Randomization: This is a setting for advanced problems only which deals with python scripts.
General Feedback: This is feedback which is only applied to certain problem types for mass-adding feedback to incorrect problems.
Group Feedback: For certain problems, the user can provide specific feedback for a combination of specific answers.
2023-02-10 08:50:32 -05:00
kenclary
7c0309189f Merge pull request #238 from openedx/kenclary/TNL-10426
feat: multiple numericalresponse tags no longer special-cased against going to the advanced editor. TNL-10426.
2023-02-09 18:40:59 -05:00
Raymond Zhou
1282a72acf feat: green radio+checkbox only for valid Checker (#239) 2023-02-09 18:33:20 -05:00
Ken Clary
746cd7cc28 feat: multiple numericalresponse tags no longer special-cased against going to the advanced editor. TNL-10426. 2023-02-09 14:47:29 -05:00
Bilal Qamar
86974b76a9 Fixed broken "Pages and Resources" page (#446)
* fix: updated AppHeader call

* fix: AppHeader function signature changed to react component
2023-02-09 06:45:37 -05:00
Raymond Zhou
46427ee156 feat: make radio and checkbox green (#234) 2023-02-08 14:41:36 -05:00
Kristin Aoki
87aaa7f3ff fix: add empty answer to problem without answers (#235) 2023-02-07 20:04:16 -05:00
Kristin Aoki
835915750c feat: update frontend-lib-content-component 1.95.0 (#445) 2023-02-07 16:13:51 -05:00
Kristin Aoki
613b8d16ae feat: update question parser to preserve text structure (#232) 2023-02-07 14:31:41 -05:00
Raymond Zhou
7e8f9a2f0c feat: change title when problem type changes (#231) 2023-02-06 13:11:59 -05:00
kenclary
65b663bfc6 Merge pull request #229 from openedx/kenclary/TNL-10413
fix: correct question parsing/building after use of styling. TNL-10413.
2023-02-06 10:17:02 -05:00
Jenkins
fe8a125d1a chore(i18n): update translations 2023-02-05 16:32:36 -05:00
Ken Clary
ea6c2c6658 fix: correct question parsing/building after use of styling. TNL-10413. 2023-02-03 11:28:20 -05:00
Raymond Zhou
d978725c35 feat: react to OLX parser process number answers (#230) 2023-02-02 15:30:05 -05:00
Kristin Aoki
57dd03f40f feat: fix feedback box bugs (#227) 2023-02-02 13:51:58 -05:00
Kristin Aoki
796dd388f7 fix: selectTypeModal header z-index (#228) 2023-02-02 13:43:49 -05:00
Kristin Aoki
f82e572ad2 feat: update frontend-lib-content-components (#439) 2023-02-01 15:46:13 -05:00
Kristin Aoki
74b2b76beb fix: add check for question length for parser (#226) 2023-02-01 15:00:03 -05:00
kenclary
2f1072812d Merge pull request #224 from openedx/kenclary/TNL-10390
fix: correct css for text margins in question tinymce. TNL-10390.
2023-02-01 13:58:38 -05:00
Jesper Hodge
861b47b772 feat add olx solution support (#225)
This adds support for the tag in OLX and maps it to the description in the settings options on the ShowAnswer card.
https://2u-internal.atlassian.net/browse/TNL-10397
2023-02-01 13:28:14 -05:00
Ken Clary
b1201cdcef fix: correct css for text margins in question tinymce. TNL-10390. 2023-02-01 12:49:38 -05:00
Kristin Aoki
83d45a249a feat: add label button to question tinyMCE (#221) 2023-02-01 11:35:15 -05:00
Kristin Aoki
8aa03496fb feat: update frontend-content-components & paragon (#438) 2023-01-31 15:21:13 -05:00
kenclary
09d5ce35f3 Merge pull request #219 from openedx/kenclary/TNL-10395
fix: question field uses real placeholder instead of template. fixed blank question handling. TNL-10395, TNL-10411.
2023-01-31 13:31:36 -05:00
Kristin Aoki
787c4068f2 feat: add margin to prevent footer blocking content (#222) 2023-01-31 12:12:38 -05:00
Muhammad Abdullah Waheed
3c2c347bb9 Automate Browserlist DB Update (#357)
* feat: added cron github action to auto update brwoserlist DB periodically

* refactor: used a shared script to update broswerslist DB, create PR and automerge it
2023-01-31 17:40:26 +05:00
Ken Clary
80f2689cc2 fix: question field uses real placeholder instead of template. fixed blank question handling. TNL-10395, TNL-10411. 2023-01-30 19:06:53 -05:00
Jenkins
0d166288cc chore(i18n): update translations 2023-01-29 16:32:35 -05:00
kenclary
4a1bac3bdb Merge pull request #218 from openedx/TNL-10400
fix: give QuestionWidget Editor a minimum height. TNL-10400.
2023-01-27 10:37:04 -05:00
Ken Clary
84fe6605c2 fix: give QuestionWidget Editor a minimum height. TNL-10400. 2023-01-26 16:48:21 -05:00
kenclary
fa2387ae00 Merge pull request #217 from openedx/kenclary/TNL-10401
fix: multiple non-numeric problems of the same type should also route to the advanced editor. TNL-10401.
2023-01-26 15:57:21 -05:00
Ken Clary
3b9681618c fix: multiple non-numeric problems of the same type should also route to the advanced editor. TNL-10401. 2023-01-26 12:28:55 -05:00
Kristin Aoki
afec2865c0 fix: separate feedback onchange for each field (#214) 2023-01-26 09:48:43 -05:00
Bilal Qamar
f8954ef870 refactor: upgraded frontend-build version to v12
PR #322
2023-01-26 09:02:14 -03:00
Jesper Hodge
83acc741f5 fix remove problem placeholder answer text (#215)
* refactor: rename hook correctly

* fix: problem templates
2023-01-25 15:02:35 -05:00
Kristin Aoki
2d669cbe3e feat: update styling in advanced problem editor (#216) 2023-01-25 10:42:30 -05:00
Kristin Aoki
656e8f8568 feat: update answer helper text (#213) 2023-01-25 10:10:08 -05:00
Muhammad Adeel Tajamul
66afd4ddac fix: updated openedx discussion provider help text (#431)
Co-authored-by: adeel.tajamul <adeel.tajamul@arbisoft.com>
2023-01-25 07:42:29 +05:00
Kristin Aoki
a99eb8a44a feat: upgrade frontend-lib-content-components (#436) 2023-01-24 13:51:06 -05:00
Kristin Aoki
be6aca8e8e feat: add function to remove empty hints (#211) 2023-01-24 13:12:41 -05:00
Jesper Hodge
1a2f175989 fix: problem editor styling (#212)
* fix: test name

* fix: make horizontal paddings 24px

* fix: space between widgets

* fix: remove settings heading

* fix: remove button hover effects

* fix: font size

* fix: make buttons small

* fix: change theme

* fix: reset buttons

* refactor: add Button component

* fix: hints widget

* fix: hints widget

* fix: tooltip

* fix: make settings fixed width

* fix: modal heading

* fix: center header text

* fix: modal header

* fix: settings fonts

* fix: settings fonts

* fix: fonts

* fix: padding

* fix: alignments

* fix: package.json

* fix: package.json

* fix: lint
2023-01-24 13:00:38 -05:00
Kristin Aoki
77afb7465b feat: update onclick to close modal + top scroll (#207) 2023-01-24 11:34:55 -05:00
kenclary
b2981318b0 chore: update version of frontend-lib-content-components to 1.85.0. (#435) 2023-01-24 10:16:03 -05:00
Raymond Zhou
f135bd2b4a feat: align files with commit 5d52a28 and f9dff0 (#210)
This PR aims to fix the commit mistakes I made when trying to merge with a refactored fork. This will keep the changes I made in the refactor.
2023-01-24 09:43:01 -05:00
rayzhou-bit
acee24eaa7 Merge branch 'main' of https://github.com/openedx/frontend-lib-content-components 2023-01-23 15:42:18 -05:00
rayzhou-bit
b7c654399b fix: fix revert 2023-01-23 15:42:13 -05:00
rayzhou-bit
1557fabf9e Revert "Merge branch 'main' of github.com:rayzhou-bit/frontend-lib-content-components into rayzhou-bit-main"
This reverts commit f9dff0df85, reversing
changes made to 5d52a289dc.
2023-01-23 15:12:32 -05:00
rayzhou-bit
bafc3c8de8 Revert "feat: merge conflicts"
This reverts commit 73ec807dd3, reversing
changes made to 62cfecc456.
2023-01-23 14:51:21 -05:00
rayzhou-bit
73ec807dd3 feat: merge conflicts 2023-01-23 13:49:25 -05:00
rayzhou-bit
f9dff0df85 Merge branch 'main' of github.com:rayzhou-bit/frontend-lib-content-components into rayzhou-bit-main 2023-01-23 13:42:15 -05:00
kenclary
5d52a289dc Merge pull request #200 from openedx/kenclary/TNL-10332
fix: various style and UX fixes. TNL-10332.
2023-01-23 13:01:26 -05:00
Ken Clary
d3be3d4240 fix: Correct summary of number of attempts; new separator between attempts summary and points summary; hint text for both attempts and points settings; fixed some widget paddings; advanced problems do not show type setting, and type setting menu does not show advanced problems. TNL-10332. 2023-01-23 12:34:43 -05:00
Jenkins
5142f3afd4 chore(i18n): update translations 2023-01-22 16:32:35 -05:00
Jesper Hodge
5aca835a4b Fix problem editor margins and borders (#203)
* fix: fix border and allow customizing of tinymce style

* fix: make tinymce widget look like on figma

* fix: update settingsoptions card border

* fix: header typography

* fix: spacings

* chore: update snapshots

* Update src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>

* Update src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>

* Update src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>

* Update src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>

* Update src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>

* Update src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>

* fix: html and react problems in problem editor

* chore: update snapshots

* chore: apply pr suggestions

* chore: fix test coverage

* chore: fix lint

* chore: fix tests

* chore: fix lint

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>
2023-01-20 15:59:30 -05:00
Kristin Aoki
d6d3363866 feat: make problem select and preview a fixed size (#204) 2023-01-20 15:05:53 -05:00
Kristin Aoki
f08a662136 feat: remove drag and drop from advanced problems (#206)
Drag and drop V2 is still available in studio via the "Advanced" component.
2023-01-20 14:22:33 -05:00
Raymond Zhou
b7b3601337 feat: update FLCC to 1.81.0 (#432) 2023-01-20 11:55:58 -05:00
Raymond Zhou
9c4077f32d feat: set videoId to '' instead of null on load (#205) 2023-01-20 11:22:18 -05:00
rayzhou-bit
317cb48dbe feat: fix to updateDuration call 2023-01-20 05:44:22 -05:00
rayzhou-bit
62cfecc456 feat: fix input for updateDuration call 2023-01-19 16:25:08 -05:00
rayzhou-bit
f64d1bb127 feat: package-lock 2023-01-18 18:18:39 -05:00
rayzhou-bit
af94e15ffb feat: name changes 2023-01-18 18:08:07 -05:00
Kristin Aoki
5e037a7209 feat: change editor footer from sticky to fixed (#202) 2023-01-18 14:39:46 -05:00
Kristin Aoki
adb24ef9ea feat: change default question text (#199) 2023-01-18 12:50:26 -05:00
Jesper Hodge
9a8307001f add testability adr (#201)
* chore: hook ADR round 1

* chore: add adr number

* chore: revert changes that are not docs-related

* chore: revert changes

Co-authored-by: Ben Warzeski <bwarzeski@edx.org>
2023-01-18 11:44:50 -05:00
rayzhou-bit
34b7b3314c feat: revert to connect 2023-01-18 06:12:49 -05:00
Kristin Aoki
50e5ca86c6 feat: upgrade paragon and lib-content-components (#430) 2023-01-17 16:26:11 -05:00
Kristin Aoki
7d21a3d4c9 feat: change feedback and delete icons to outline variants (#198) 2023-01-17 15:41:11 -05:00
Jesper Hodge
b7c24d1e1a Fix answer box styling. TNL-10333 (#197)
* fix: use feedback icon with correct hover color

* fix: problem answer layout squishes delete button background

* fix: remove borders from textarea

* fix: textarea resize

* refactor: remove renderThing-antipattern in answer option

* fix: answer option feedback color

* fix: add second feedback box to all problem types

* refactor: move extra components out of answer option file

* fix: icon disappearing on hover when active

* fix: update snapshot

* fix: lint

* fix: add tests

* fix: add tests

* fix: snapshots

* Update src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>

* fix: resolve discussions from PR

Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>
2023-01-13 17:00:56 -05:00
connorhaugh
95ae45cce8 fix: update title on type select (#196)
* fix: update title on type select

* fix: lint fix

* fix: add test
2023-01-13 14:37:52 -05:00
Kristin Aoki
1a370c12d9 feat: update problem type titles + small ui fixes (#195) 2023-01-13 10:04:01 -05:00
connorhaugh
fe9a9a37e7 feat: upgrade to 1.76.0 (#429) 2023-01-12 15:43:22 -05:00
Kristin Aoki
990e35bdc2 feat: remove question box branding + menu (#192)
This PR address the console.log errors from the question box tinyMCE, removing the tinyMCE branding and menu.
2023-01-12 15:30:06 -05:00
connorhaugh
2a9851544e Fix: advanced problem template reference (#194)
* fix: correctly reference advanced problem templates

* fix: remove console log
2023-01-12 13:23:02 -05:00
Raymond Zhou
aad7a6b706 feat: video editor fix to disappearing transcript (#178)
* feat: video editor fix to disappearing transcript
2023-01-12 12:51:56 -05:00
Jesper Hodge
574c2cc76a fix: cannot open problem editor because of typeerror undefined (#193)
* fix: cannot open problem editor because of typeerror

* fix: snapshots
2023-01-12 12:25:18 -05:00
connorhaugh
1c5ab42ea6 feat: update flcc to 1.74.0 (#428) 2023-01-12 10:53:46 -05:00
kenclary
6d823e4e7c Merge pull request #187 from openedx/kenclary/TNL-10324
fix: match new frontend behavior with legacy behavior (zero problem attempts is zero attempts, null attempts is infinite); negative values disallowed and forced to zero. TNL-10324.
2023-01-12 10:40:47 -05:00
Ken Clary
390d620664 fix: match new frontend behavior with legacy behavior (zero problem attempts is zero attempts, null attempts is infinite); negative values disallowed and forced to zero. TNL-10324. 2023-01-12 10:31:13 -05:00
connorhaugh
6b2b5ac455 Update workflow 2023-01-12 09:56:59 -05:00
connorhaugh
70f5bb1080 build: pin semantic release to unblock pipeline (#191)
I saw that the release CI https://github.com/openedx/frontend-lib-content-components/actions/runs/3897027405/jobs/6654318273 was failing the release on [semantic-release]: node version >=18 is required. Found v16.19.0.

from https://github.com/semantic-release/semantic-release/releases/tag/v20.0.0 we learn that node v18 is now the minimum required version of node.

The version of semantic-release that runs in a repo is usually based on the relevant Github Acton workflow file for the release, defined in the repo itself.

I am pinning that version to 19.0.5 until the next node upgrade, as it seems we recently upgrade to node 16.
2023-01-12 09:31:31 -05:00
Kristin Aoki
5f5dc911da fix: change # destination to advance settings url (#189) 2023-01-12 09:22:37 -05:00
rayzhou-bit
e66863795c feat: tests argh 2023-01-11 20:14:03 -05:00
connorhaugh
9b2e284ee3 fix: remove test (#188) 2023-01-11 16:53:16 -05:00
Kristin Aoki
d23a790ad2 feat: allow feedback to copy when problem changed (#184) 2023-01-11 16:40:36 -05:00
connorhaugh
cf1daa3ba5 Feat add templates for default problems after problem select (#185)
https://2u-internal.atlassian.net/browse/TNL-10316 is the relevant bug.

Problems have default values when created using the select type page.
2023-01-11 14:26:46 -05:00
Jesper Hodge
2c6679fe06 Feat raw olx editing. TNL-10218 (#182)
* refactor: move CodeEditor to shared components and remove circular dependency

* feat: add code editor to problem editor

* fix: typo

* feat: add save function to raw olx editor and add highlighting

* feat: simplify and add tests to edit problem view

* feat: add tests to problem edit view

* fix: update raw editor tests

* fix: code editor tests

* fix: package-lock

* fix: lint
2023-01-11 14:23:06 -05:00
connorhaugh
f81b0ee925 docs: add period to module.config.js (#183) 2023-01-11 10:25:25 -05:00
kenclary
6a4ac3525f Merge pull request #180 from openedx/kenclary/TNL-10280
feat: confirmation dialog for closing any editor. TNL-10280.
2023-01-11 10:07:46 -05:00
Ken Clary
9ba0da04c3 feat: confirmation dialog for closing any editor. TNL-10280. 2023-01-11 09:58:43 -05:00
Kristin Aoki
af4cd55390 feat: unselect multiple choices type multi->single (#181) 2023-01-10 13:25:19 -05:00
connorhaugh
880d205cbb Feat: raw editor ingress and egress logic (#179)
* feat: conditional rendering of olx editor.

* fix: open the raw editor if advanced is chosen

* fix: add test fix

* feat: add button to switch visual->advanced

* fix: add tests + lint for visual->advanced button

* feat: revert to advanced if parser fails

* fix: improve coverage

* feat: add confirm dialog to switch

* fix: load settings with advanced

* fix: refactor + lint fix
2023-01-10 09:42:44 -05:00
Kristin Aoki
74fcbe426d feat: upgrade lib-content-components to 1.73.0 (#419) 2023-01-06 10:41:27 -05:00
Mehak Nasir
6a65826fd5 fix: group at subsection field added in redux state 2023-01-05 14:31:44 +05:00
Jesper Hodge
ad47bfacd4 docs: add fix for m1 macs to readme (#421) 2023-01-03 16:32:54 -05:00
rayzhou-bit
e946fb8711 feat: duration change first pass 2023-01-02 20:52:16 -05:00
Jenkins
9e9bac997b chore(i18n): update translations 2022-12-25 16:32:50 -05:00
Kristin Aoki
09bb1dab2b feat: add import transcripts from youtube (#176) 2022-12-23 10:54:07 -05:00
connorhaugh
014fbeac71 feat: release select type page (#418) 2022-12-23 09:22:14 -05:00
connorhaugh
2896393c53 feat: add problem type select
Co-authored-by: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>
Co-authored-by: Raymond Zhou <56318341+rayzhou-bit@users.noreply.github.com>
2022-12-22 16:52:21 -05:00
connorhaugh
0b214faeca feat: add problem editor to FLCC (#417) 2022-12-20 16:14:43 -05:00
connorhaugh
83cbac6270 feat: doc updates for problem editor release 2022-12-20 15:31:14 -05:00
connorhaugh
8dea72de99 feat:problem editor
Co-authored-by: Farhaan Bukhsh <farhaan@opencraft.com>
Co-authored-by: Navin Karkera <navin@disroot.org>
Co-authored-by: Kaustav Banerjee <kaustav@opencraft.com>
2022-12-20 14:52:20 -05:00
Zachary Hancock
abff65a11a fix: LTI providers should not be shown unless enabled (#416) 2022-12-19 10:46:04 -05:00
connorhaugh
6f82e87574 feat: make delete and info icons outlines (#150)
* feat: make delete and info icons outlines

* feat:upgrade paragon
2022-12-14 11:44:41 -05:00
Adolfo R. Brandes
2f6eed237a fix: Editors should support runtime configuration
Fetching settings directly via `process.env` circumvents the runtime
configuration mechanism.  Change the editor page to use `getConfig()`
instead.
2022-12-13 23:55:36 +00:00
Kristin Aoki
f85d86f796 fix: left and right alignment (#167) 2022-12-13 15:04:53 -05:00
Zubair Shakoor
128b112af7 fix: -t flag added in pull translation command (#142) 2022-12-13 13:23:24 -05:00
Kristin Aoki
7daa2c2dba fix: vertical spacing between items (#166) 2022-12-13 13:20:15 -05:00
Kristin Aoki
7253c9bba3 feat: upgrade lib-content-components 1.69.1 (#411) 2022-12-13 09:27:22 -05:00
Kristin Aoki
b5be6f441c fix: add check if field is cleared (#165) 2022-12-12 14:18:25 -05:00
Kristin Aoki
07202c0518 feat: separate video id and url fields into two (#164) 2022-12-12 10:55:32 -05:00
Jenkins
a84d3c09e8 chore(i18n): update translations 2022-12-11 16:27:30 -05:00
Raymond Zhou
f5e1f1cf6b feat: FLCC v1.68.0 (#407) 2022-12-09 11:37:31 -05:00
Raymond Zhou
bcb3c3f7fb feat: parseTranscript to work with all languages (#162)
* feat: parseTranscript to work with all languages
2022-12-09 11:05:50 -05:00
ayesha waris
8096a389da style: confirmation modal added for in-context discussion toggle (#404)
* style: confirmation modal added for in-context discussion toggle

* refactor: removed duplicate message id

* refactor: function moved to on line direct call
2022-12-09 18:40:21 +05:00
Raymond Zhou
cc61e2944c feat: header overlap content when scrolling down (#161)
* feat: header overlap content when scrolling down
2022-12-07 15:24:48 -05:00
kenclary
1a21850fc4 feat: upgrade lib-content-components to 1.66.3. (#405) 2022-12-07 11:58:26 -05:00
Kristin Aoki
01c3a42eb2 fix: transcript styling (#159) 2022-12-07 11:16:02 -05:00
kenclary
a15b894ec1 Merge pull request #160 from openedx/kenclary/TNL-10236
fix: editor title editing no longer tries to scale with input size. TNL-10236.
2022-12-07 11:10:21 -05:00
kenclary
014523731e Merge pull request #158 from openedx/kenclary/TNL-10235
fix: editor container titles are the same height whether displaying or editing. TNL-10235.
2022-12-07 11:01:38 -05:00
Ken Clary
f6574c6849 fix: editor title editing no longer tries to scale with input size. TNL-10236. 2022-12-07 10:31:29 -05:00
Ken Clary
3239810a7f fix: editor container titles are the same height whether displaying or editing. TNL-10235. 2022-12-07 10:05:33 -05:00
Kristin Aoki
efae5ecd4b feat: upgrade ib-content-components to 1.66.0 (#403) 2022-12-05 16:59:49 -05:00
Kristin Aoki
6368ccf5ac feat: show view license link at all levels (#157) 2022-12-05 12:43:14 -05:00
Jenkins
1e043325d6 chore(i18n): update translations 2022-12-04 16:32:30 -05:00
Kristin Aoki
0b199b1f5d feat: add black canvas when current thumbnail is deleted (#156) 2022-12-02 16:47:00 -05:00
Raymond Zhou
73150817ca feat: update paragon to v20.21.0 (#155) 2022-12-02 12:54:45 -05:00
Raymond Zhou
0bfce5594d feat: update paragon to v20.21.0 (#399) 2022-12-02 11:12:25 -05:00
connorhaugh
dbe2787785 feat: update flcc to 1.63.1 (#400) 2022-12-02 10:47:53 -05:00
connorhaugh
20e98319af feat: remove default background color for editors (#398) 2022-12-02 10:23:07 -05:00
Kristin Aoki
f824ef0551 fix: license styling (#154) 2022-12-01 15:14:44 -05:00
connorhaugh
35b58a42b5 feat: left align widget "add" buttons (#153) 2022-12-01 12:20:29 -05:00
Kristin Aoki
6a2bafb402 fix: font/icon size and color (#152) 2022-11-30 15:31:39 -05:00
connorhaugh
e70800543a feat: increase spacing between video settings (#149) 2022-11-30 14:12:03 -05:00
kenclary
1360994fc5 Merge pull request #151 from edx/kenclary/TNL-10240
fix: correct various strings, including making them sentence-case. TNL-10240.
2022-11-30 12:45:04 -05:00
Ken Clary
c4a7b97b63 fix: correct various strings, including making them sentence-case. TNL-10240. 2022-11-30 12:36:42 -05:00
Abdullah Waheed
2063049747 feat: added new translations in Makefile and updated all the translations 2022-11-30 13:30:22 +00:00
edX requirements bot
a7def9ce25 fix: -t flag added in pull translation command (#393) 2022-11-30 16:44:21 +05:00
Kristin Aoki
2418207149 feat: bump lib-content-componets to 1.61.0 (#397) 2022-11-29 16:34:42 -05:00
Kristin Aoki
7a356175e7 feat: update onBlur to set state to default values (#148) 2022-11-29 15:23:14 -05:00
Kristin Aoki
5da5967e97 feat: update lib-content-componets to 1.60.1 (#395) 2022-11-29 15:10:40 -05:00
Kristin Aoki
f38ae87c95 fix: length error on undefined fallback videos (#147) 2022-11-29 12:44:56 -05:00
Jenkins
45b2bf5b13 chore(i18n): update translations 2022-11-27 16:32:27 -05:00
ayesha waris
7527f6c764 Style: Toggle remains Disabled and Link to instructor dashboard show on Disabled Cohorts (#375)
* style: on disabled cohorts toggle remains disabled and link to instructor dashboard shows

* fix: test cases fixed

Co-authored-by: Mehak Nasir <mehaknasir94@gmail.com>
2022-11-21 14:41:40 +05:00
Ghassan Maslamani
bdfa1fdeb3 fix: force studio url to reload if changed
This chagne make it possible if this module was loaded **then**
  the configuration for studio url is changed, then it will pick
  the last value.

  More context overhangio/tutor-mfe/issues/86
2022-11-18 13:49:16 +00:00
Raymond Zhou
79f58cc8d0 feat: update to flcc 1.60 (#392) 2022-11-17 12:25:03 -05:00
Jeremy Ristau
08b5475548 feat: add CODEOWNERS file
feat: add CODEOWNERS file
2022-11-17 11:48:04 -05:00
Jeremy Ristau
f00f6174ac feat: add CODEOWNERS file
The repo should have a CODEOWNERS file, but this effort is specifically to help test a need to make an edx-platform file notify T&L when changes are made.  We are doing the testing on this edx repo that we own to minimize the churn and impact on edx-platform.

The scope of this effort includes:
# Create a CODEOWNERS file in this repo that calls out a single file as owned by a team.
# Submit a no-change PR to that file and ensure the team is notified
# Submit a no-change PR to another file in the repo and ensure the team is NOT notified
# Update the CODEOWNERS file to have T&L team as owners of the whole repo, instead of just one file (long term desired state)
2022-11-17 10:29:16 -05:00
Adolfo R. Brandes
437d0a37a9 docs: Document current feature-set of this MFE
This updates the README to include brief documentation on the purpose of
this MFE, the main features it provides, as well as their configuration
and external requirements.
2022-11-17 14:13:23 +00:00
Saad Yousaf
0e24a0767b fix: show Zoom settings when pii sharing is enabled and make launch email optional (#387)
Co-authored-by: Mehak Nasir <mehaknasir94@gmail.com>
2022-11-16 13:14:16 +05:00
Raymond Zhou
6c574ac18e feat: duration entree features (#143) 2022-11-15 16:22:29 -05:00
connorhaugh
91abf56977 feat: upgrade flcc to 1.58.0 (#390) 2022-11-15 13:31:15 -05:00
connorhaugh
ea50afc165 feat: update readme
adding this messaging to improve quality and trigger a release.
2022-11-15 12:17:30 -05:00
connorhaugh
3506db7c14 Feat improve transcript flow (#141)
https://2u-internal.atlassian.net/browse/TNL-10199
Rewrite of Transcripts widget to improve flow.
2022-11-15 12:03:04 -05:00
Kristin Aoki
f0734d86db feat: upgrade frontend-lib-components to 1.57.0 (#388) 2022-11-15 09:27:46 -05:00
Kristin Aoki
8eab620b65 feat: auto-resize width of input and allow save onBlur (#144) 2022-11-14 13:24:11 -05:00
Ahtisham Shahid
3581d633c1 Revert "fix: show Zoom settings when pii sharing is enabled and make launch email optional (#380)" (#386)
This reverts commit b8895bef33.
2022-11-11 21:25:41 +05:00
Saad Yousaf
b8895bef33 fix: show Zoom settings when pii sharing is enabled and make launch email optional (#380)
Co-authored-by: Mehak Nasir <mehaknasir94@gmail.com>
2022-11-11 18:08:26 +05:00
Kristin Aoki
89d0d12559 feat: bump upgrade of flcc to 1.56.1 (#385) 2022-11-10 09:35:09 -05:00
Kristin Aoki
af6e21e1fe fix: non-blocking UI bugs for video settings (#140) 2022-11-09 12:32:36 -05:00
Abderraouf Mehdi Bouhali
34fe291268 fix(rtl): mirror legacy page card arrow 2022-11-07 16:03:36 +00:00
Jenkins
4b1e292e1c chore(i18n): update translations 2022-11-06 16:32:32 -05:00
Raymond Zhou
b4b31794af feat: allow license library content edit (#139)
* feat: allow license library content edit
2022-11-03 19:58:22 -04:00
Kristin Aoki
90eb6fd0c3 feat: upgrade frontend-lib-content-components to 1.55.0 (#379) 2022-11-03 19:05:20 -04:00
kenclary
5f5250fd2c Merge pull request #131 from edx/kenclary/TNL-9823
feat: duration widget for video settings. TNL-9823.
2022-11-03 18:44:12 -04:00
Kristin Aoki
4187f6a884 feat: remove thumbnail widget from content libraries (#138) 2022-11-03 18:23:34 -04:00
Kristin Aoki
e5932ced27 feat: remove handout widget for content libraries (#137) 2022-11-03 18:16:04 -04:00
Ken Clary
a4f0a8f162 feat: duration widget for video settings. TNL-9823. 2022-11-03 17:55:05 -04:00
Kristin Aoki
50da8a0f0b feat: upgrade flcc to 1.52.0 and paragon to 20.18.0 (#377) 2022-11-02 11:26:46 -04:00
Raymond Zhou
79ae64b562 feat: license widget (#132) 2022-11-02 10:41:40 -04:00
Awais Ansari
7dcd328f2e fix: add . to helper message 2022-11-01 17:39:01 +05:00
Awais Ansari
e2d66cc605 fix: update helper text for bigBlueButton free plan 2022-11-01 17:39:01 +05:00
kenclary
f5fc721b3b feat: update lib-content-components to 1.51.2. TNL-10183. (#371) 2022-10-27 14:46:37 -04:00
kenclary
9c397d8802 Merge pull request #136 from edx/kenclary/TNL-10183
fix: filter for images in a null-safe/undefined-safe way. Fixes TNL-10183.
2022-10-27 10:52:40 -04:00
Ken Clary
c89ad83ed5 fix: filter for images in a null-safe/undefined-safe way. Fixes TNL-10183. 2022-10-26 20:32:52 -04:00
connorhaugh
bfb4de6b1a Fix: temp remove video preview text from settings editor (#134)
* fix: remove preview

* fix: unrelated remove removed fetchImages
2022-10-25 11:44:39 -04:00
Kristin Aoki
c4bbb6fa70 feat: update frontend-lib-content-components to 1.51.1 2022-10-25 11:10:20 -04:00
Kristin Aoki
ca5846f1f6 fix: text editor save to use assets array instead of object 2022-10-25 10:42:26 -04:00
Kristin Aoki
8e55081ce1 feat: revert selection.url change and replaced with getting the relative path substring from the absolute url 2022-10-24 16:49:47 -04:00
Kristin Aoki
e6101ed3ac feat: update setAssetToStaticUrl to include all assets 2022-10-24 13:44:33 -04:00
Muhammad Adeel Tajamul
748aee2cff fix: added bbb learn more env variable (#365)
Co-authored-by: adeel.tajamul <adeel.tajamul@arbisoft.com>
2022-10-20 17:31:14 +05:00
Kristin Aoki
31473d3f49 feat: update lib-content-components to 1.49.0 2022-10-19 15:30:18 -04:00
Kristin Aoki
cb01ff17a0 feat: add handout widget 2022-10-19 12:17:59 -04:00
Kristin Aoki
8a2c337170 feat: update edx packages: pargon, frontend-platform,browserslist-config (#129) 2022-10-18 12:12:38 -04:00
Kristin Aoki
4de727791a feat: update frontend-lib-content-components to 1.48.0 and upgrades paragon to 20.13.0 2022-10-17 15:34:09 -04:00
Kristin Aoki
84f35dad40 feat: add automatic thumbnail resampling for thumbnail widget 2022-10-13 15:07:08 -04:00
Kristin Aoki
e9a123c16b feat: limit xblock title display 2022-10-13 12:44:56 -04:00
Zachary Hancock
d1d04d5585 feat: configure lti providers on exam settings modal (#363) 2022-10-13 09:22:36 -04:00
Raymond Zhou
6f41a14012 feat: update FLCC (#362) 2022-10-12 16:02:05 -04:00
Kristin Aoki
36576903ea feat: thumbnail widget 2022-10-12 15:42:38 -04:00
Raymond Zhou
b035725344 feat: bump version (#127) 2022-10-12 15:11:29 -04:00
rayzhou-bit
72aa2dbe92 feat: bump version 2022-10-12 15:00:16 -04:00
Raymond Zhou
d2f07045f0 Feat video source integration (#125)
* feat: video source integration

Co-authored-by: KristinAoki <kaoki@2u.com>
2022-10-12 12:38:44 -04:00
kenclary
a7abab1236 Convert video settings from xblock metadata into the redux store. In-progress draft. TNL-10009. (#119)
* feat: Convert video settings from xblock metadata into the redux store. In-progress draft. TNL-10009.

Co-authored-by: rayzhou-bit <rzhou@2u.com>
2022-10-04 14:59:22 -04:00
Raymond Zhou
cc3a2d8b85 feat: videosource to backend (#118)
* feat: videosource to backend
2022-09-30 11:00:06 -04:00
Kristin Aoki
79ceaca8cc feat: update UI for video source widget (#120) 2022-09-28 11:34:56 -04:00
connorhaugh
45215ba504 Feat: full transcript widget (#117) 2022-09-27 14:09:29 -04:00
Kristin Aoki
ff636837cf feat: transcript parent widget component 2022-09-14 11:07:20 -04:00
connorhaugh
3f303a718d feat: unescape alphanumerics in code Editor (#112)
* feat: unescape alphanumerics in code Editor

* fix: replace disable with not render
2022-09-09 11:09:53 -04:00
Kristin Aoki
03a609ea98 feat: allow .ico image file types 2022-09-08 12:36:34 -04:00
Raymond Zhou
54d773a19a Feat shared widget componentries and layout (#107) 2022-09-07 12:22:27 -04:00
connorhaugh
720b41193f fix: intermittent raw loading error (#109)
As sometimes the html components are rendered downstream before the block is loaded, we need to handle the case of no block value being provided to the raw editor at the time of first render. Subsequent thunk actions will trigger re-renders to display the block.
2022-09-01 14:35:44 -04:00
Kristin Aoki
e5ea0a096c feat: change image absolute urls to relative urls 2022-08-31 12:03:07 -04:00
Kristin Aoki
2d427da80f feat: add tinyMCE functionality for drag resizing of images 2022-08-31 09:18:36 -04:00
Kristin Aoki
4e69fffbef feat: remove the ability to use image tools for content libraries 2022-08-30 16:40:17 -04:00
Kristin Aoki
36380edce4 feat: add codeMirror lineWrapping extension 2022-08-29 09:39:46 -04:00
Kristin Aoki
2b49304ecc feat: change static url to asset url in editor 2022-08-24 12:53:18 -04:00
Raymond Zhou
09110ec0b0 feat: run onImgLoad even on broken images (#102)
* feat: run onImgLoad even on broken images
2022-08-23 06:00:46 -04:00
connorhaugh
564dcb8ebc feat: improve raw HTML editing Experince. (#101)
* feat: add contents

* feat: add codemirror support to raw HTML editing

* feat: add test coverage

* fix: error

* fix: update codeeditor file path

* fix: update messages
2022-08-22 15:19:21 -04:00
Kristin Aoki
617f316f37 feat: allow relative urls for assets 2022-08-18 09:04:44 -04:00
Farhaan Bukhsh
405c3cf7e3 chore: Update the Readme for project (#98)
Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>

Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
2022-08-17 11:13:40 -04:00
Kristin Aoki
d68c357a86 feat: resolve console errors regarding skin.css 2022-08-15 11:26:39 -04:00
Kristin Aoki
f86e1fe97e feat: update editor text to match studio styling 2022-08-12 10:47:11 -04:00
Raymond Zhou
476c450e6c Feat updates to code block button (#93)
* feat: updates to code block button

* feat: updates to code block button
2022-08-10 12:59:03 -04:00
Kristin Aoki
1b180de468 feat: add altText and isDecorative persistence for image editing 2022-08-10 09:17:35 -04:00
Kristin Aoki
d739bcbdb5 feat: Prevent image uploads larger than 10 MB and add spinner 2022-08-08 15:44:59 -04:00
Kristin Aoki
9b23731acc feat: Add functionality to resize image based on percentage (#94)
* feat: add image resizing with percents
2022-08-05 12:13:57 -04:00
Kristin Aoki
3821378dc1 Merge pull request #92 from edx/KristinAoki/tinymce-underline
fix: added underline option to toolbar. TNL-10003
2022-08-01 10:29:21 -04:00
KristinAoki
6be7433567 Merge branch 'KristinAoki/tinymce-underline' of https://github.com/edx/frontend-lib-content-components into KristinAoki/tinymce-underline 2022-08-01 09:15:35 -04:00
KristinAoki
cc7e502011 fix: added underline option to toolbar. TNL-10003 2022-08-01 09:11:01 -04:00
KristinAoki
a918da46c2 Add underline option to editor toolbar 2022-07-29 12:02:08 -04:00
Raymond Zhou
80dfc6ba15 feat: www gallery raw selection (#88)
* feat: www gallery raw selection
2022-07-22 10:58:55 -04:00
connorhaugh
224720c32e feat: reorganize dependancies (#91) 2022-07-11 16:19:31 -04:00
connorhaugh
9829a5d4ef feat: No - OP (#90) 2022-07-07 08:47:56 -04:00
connorhaugh
2473c9a875 chore: upgrade paragon dep (#89)
* chore: upgrade paragon dep

* fix: eslint config
2022-07-05 09:08:15 -04:00
Raymond Zhou
b047f7a2a8 feat: raw html editor (#86)
raw html editor
2022-06-28 14:25:09 -04:00
connorhaugh
b2fec70702 feat: swap relative Urls bool (#85) 2022-06-10 10:46:34 -04:00
connorhaugh
728dc58ac4 fix: Remove addition to URLs in text editor (#84) 2022-06-09 09:43:23 -04:00
connorhaugh
81fc4fca6f docs: adopt ADR 4 (#79) 2022-06-09 09:41:56 -04:00
connorhaugh
cc4e19cd2a fix: redirect to correct learning context (#83)
For https://2u-internal.atlassian.net/browse/TNL-9955
there are multiple instantiations of the text editor in different contexts, so we need to handle them upfront to not break either experience flow” or something along those lines…
2022-06-09 09:41:31 -04:00
Raymond Zhou
ca9f788838 feat: toolbar stays visible when scrolling (#82) 2022-05-27 12:40:20 -04:00
kenclary
0e523feb09 Merge pull request #81 from edx/kenclary/TNL-9943
fix: allow style tags inside html body, to match old editor behavior. TNL-9943.
2022-05-26 13:44:41 -04:00
kenclary
c02bb9df7f Merge branch 'main' into kenclary/TNL-9943 2022-05-25 17:23:07 -04:00
Ken Clary
5922e25b3d fix: allow style tags inside html body, to match old editor behavior. TNL-9943. 2022-05-25 15:15:43 -04:00
connorhaugh
3b859734fd feat: add simple code block and blockquote buttons (#80)
* feat: add simple code block and blockquote buttons

* fix: readaad context toolbar
2022-05-25 13:58:11 -04:00
connorhaugh
a74b2f2272 docs: V2 Content Editors ADR (#72)
This PR adds an overarching ADR for the V2 Content Editors framework, as well as several other, smaller, ADRs related to architectural abstractions.
2022-05-19 10:05:52 -04:00
connorhaugh
a3c50b2723 feat: allow all html tags (#77)
* feat: allow all html tags.
2022-05-18 13:24:13 -04:00
connorhaugh
94009e6ed7 fix: delete z index css (#78)
* fix: delete z index css

* fix: remove from index.css
2022-05-17 13:50:41 -04:00
connorhaugh
5aea213b8c feat: add autocreate new xblock (#67)
This PR adds an automated script for generating the boilerplate code for adding a new xblock editor. As well as documentation for how to do so.
2022-05-16 16:14:18 -04:00
kenclary
0d51b45636 Merge pull request #75 from edx/kenclary/TNL-9879
chore: npm update, using node 16 / npm 8. TNL-9879.
2022-05-15 10:18:28 -04:00
Ken Clary
d74f2b8ba9 chore: npm update, using node 16 / npm 8. TNL-9879. 2022-05-15 09:57:10 -04:00
Raymond Zhou
76dcd1a920 Feat alert users with feedback instead of disabling next/save buttons (#68)
* feat: implemented user feedback ErrorAlerts
2022-05-13 15:23:42 -04:00
Jawayria
b5480beaf8 feat: Add package-lock file version check (#73) 2022-05-13 14:55:29 -04:00
Tim McCormack
18e3012462 fix: Fix pull_translations by using correct CLI flag for languages (#74)
Docs: https://developers.transifex.com/docs/using-the-client

Apparently this CLI option changed from singular to plural at some point.
2022-05-13 13:03:26 -04:00
Ben Warzeski
564b953860 feat: video skeleton hooks (#65)
* feat: video skeleton hooks

* fix: lint

* Update src/editors/containers/VideoEditor/components/VideoSettingsModal/components/CollapsibleFormWidget.jsx

Co-authored-by: connorhaugh <49422820+connorhaugh@users.noreply.github.com>

Co-authored-by: connorhaugh <49422820+connorhaugh@users.noreply.github.com>
2022-05-09 14:23:21 -04:00
bszabo
964f00e563 Merge pull request #71 from edx/TNL-9888-text-editor-ADR
docs: ADR for text editor decisions May 2022
2022-05-06 11:39:56 -04:00
Bernard Szabo
46458b0f58 docs: TNL-9888 ADR for text editor decisions May 2022
Mostly notes on how to use TinyMCE to meet product requirements
Includes pull request feedback
Co-authored-by: Ben Warzeski <bwarzeski@2u.com>
2022-05-06 11:38:59 -04:00
Bernard Szabo
da6bdad9f0 docs: TNL-9888 ADR for text editor decisions May 2022
Mostly notes on how to use TinyMCE to meet product requirements
Includes pull request feedback
Co-authored-by: Ben Warzeski <bwarzeski@2u.com>
2022-05-06 11:24:18 -04:00
kenclary
121f28c535 Merge pull request #70 from edx/kenclary/TNL-9876
fix: code plugin uses 'HTML' label instead of icon. Fixes TNL-9876.
2022-05-04 18:25:12 -04:00
Ken Clary
91be5db424 fix: code plugin uses 'HTML' label instead of icon. Fixes TNL-9876. 2022-05-04 16:50:07 -04:00
Bernard Szabo
7b6bd31475 docs: fix typo and clarify no feature loss requirement
per Ray Zhou's PR feedback
2022-05-04 15:24:13 -04:00
Raymond Zhou
51f89bdc1e feat: The X Button in the Top Right Doesn't do anything. (#66)
* fix: tests

* fix: onClose test case
2022-05-04 14:20:23 -04:00
Bernard Szabo
5920096306 docs: ADR for text editor decisions May 2022
Mostly how to use and extended tinyMCE
2022-05-04 10:52:44 -04:00
connorhaugh
0d4688ce75 feat: remove codesample add code plugin (#64) 2022-04-19 12:53:33 -04:00
connorhaugh
081129b639 feat: no-op commit update readme (#63)
this is for the release to stage.
2022-04-19 11:54:25 -04:00
Raymond Zhou
5200897e1b Fix change to go back to 'fake' modal (#62)
* fix: rebase

* fix: tests

* fix: remove broken code button

* fix: test case with iff
2022-04-19 11:25:01 -04:00
kenclary
be12d11027 Merge pull request #61 from edx/kenclary/TNL-9874
fix: rearrange and add several tinymce toolbar items. Addresses TNL-9874.
2022-04-18 16:43:00 -04:00
Ken Clary
1999041cdf fix: rearrange and add several tinymce toolbar items. Addresses TNL-9874. 2022-04-18 15:37:47 -04:00
Julia Eskew
51558fd17c Add events sent to Segment for the save and cancel clicks for content editors. (#44)
* feat: Add events sent to Segment for the save and cancel clicks for a content editor.

Co-authored-by: rayzhou-bit <rzhou@edx.org>
2022-04-18 13:44:27 -04:00
kenclary
3620cac421 Merge pull request #60 from edx/kclary/TNL-9855
fix: enable ordered and unordered lists in text editor. Fixes TNL-9855.
2022-04-14 16:05:04 -04:00
Ken Clary
24baf8cbeb fix: enable ordered and unordered lists in text editor. Fixes TNL-9855. 2022-04-14 15:20:31 -04:00
Ben Warzeski
cb1c00bf3c Feat: Video skeleton (#58)
* pt1

* feat: video skeleton

* fix: update tests

* chore: update snapshots and linting

* fix: fix image context button

* feat: re-usable editor pattern

* fix: css and bad hook
2022-04-14 15:01:04 -04:00
bszabo
0edee6b4e0 Merge pull request #57 from edx/TNL-9804-make-small-code-stewardship-changes-to-frontend-lib-content-components
Tnl 9804 make small code stewardship changes to frontend lib content components
2022-04-11 18:12:19 -04:00
Bernard Szabo
0d60cd97a0 refactor: Fix footer aria descriptions
'screensaver' in Aria description messages changed to 'screen reader'
2022-04-11 16:33:19 -04:00
Raymond Zhou
553e4d8c04 fix: editor has double scrollbars (#59) 2022-04-11 12:26:39 -04:00
Bernard Szabo
48fcfb0e00 refactor: Preserve button default messages
description for aria labels may explicitly reference intended screensaver use, but label names and ids should remain unchanged
2022-04-08 16:55:14 -04:00
connorhaugh
33b2c6a660 feat: image context toolbar (#55)
Note: this removes the crop and rotate functionality from the toolbar, as it requires an image_proxy server or tinymce cloud. https://www.tiny.cloud/docs-4x/plugins/imagetools/#imagetools_proxy. We also need to create a follow up ticket to handle right click behavior.
This is that follow up ticket
2022-04-08 13:23:42 -04:00
Raymond Zhou
ef6ea6b617 fix: saving block title (#50)
* fix: saving block title

* fix: ben's suggestions
2022-04-08 12:45:49 -04:00
Bernard Szabo
d79ee29b96 refactor: consistent naming and default message for saveButton label
Prior to this change Aria message and default message were different
2022-04-07 16:12:13 -04:00
Bernard Szabo
26c0c78660 refactor: use consistent ordering for header/footer attributes 2022-04-07 16:12:13 -04:00
kenclary
bc3ef37dcf Merge pull request #53 from edx/kenclary/TNL-9832
fix: remove spurious hard-coded height for img-settings-form-container class. Fixes TNL-9832.
2022-04-07 15:49:06 -04:00
Ken Clary
e3236e9d95 fix: remove spurious hard-coded height for img-settings-form-container class. Fixes TNL-9832. 2022-04-07 15:44:47 -04:00
connorhaugh
c817e17d8a fix: update texteditor spacing (#49)
footer is now locked to bottom of page, and that text editor content scrolls, and that this works on resize of page.
2022-04-07 14:49:15 -04:00
Raymond Zhou
8104aa3152 fix: load block title (#51) 2022-04-07 12:47:35 -04:00
connorhaugh
c3d9109211 fix: remove merge artifiact (#48)
For repo health
2022-04-06 10:50:08 -04:00
Raymond Zhou
45758612a4 feat: empty gallery (#46)
Adds a new option to the behavior for when the gallery has no images in it.
2022-04-05 11:43:40 -04:00
connorhaugh
049b6a9211 Fix: release ci (#47)
* feat: add capabilities

* chore: more tests

* fix: use correct hook source for getDispatch

* fix: fix fetchImages data contract

* fix: make onClose method a callback for ImageUploadModal

* chore: lint and test updates

* chore: clean up propType warning

* fix: upload of file

* fix: error notifcations

* fix: improve test coverage

* fix: lint fixes

* fix: lint fix

* rebase: stept 1

* rebase: step 2

* fix: use correct hook source for getDispatch

* rebase: step 3

* rebase: step 4

* chore: clean up propType warning

* fix: upload of file

* rebase: step 5

* fix: improve test coverage

* fix: lint fixes

* fix: release CI

Co-authored-by: Ben Warzeski <bwarzeski@edx.org>
2022-04-04 09:17:34 -04:00
connorhaugh
fd35c1cb18 Feat: Add Request Alerts and upload file (#43)
Fixes to Upload data type, as well as adding in of two error alerts for upload and fetch.
Co-authored-by: Ben Warzeski <bwarzeski@edx.org>
Co-authored-by: Raymond Zhou <56318341+rayzhou-bit@users.noreply.github.com
2022-04-04 08:33:31 -04:00
Raymond Zhou
28516a0389 fix: text editor save (#45)
* fix: text editor save

* fix: update snapshot
2022-04-01 17:01:12 -04:00
Raymond Zhou
dca18b9b97 chore: i18n complete (#37)
* chore: i18n
2022-03-31 16:14:29 -04:00
Ben Warzeski
0f87a61639 feat: Select img api (#41)
* feat: add capabilities

* chore: more tests

* fix: use correct hook source for getDispatch

* fix: fix fetchImages data contract

* fix: make onClose method a callback for ImageUploadModal

* chore: lint and test updates

* chore: clean up propType warning

Co-authored-by: connorhaugh <chaugh21@amherst.edu>
2022-03-30 15:13:19 -04:00
Ben Warzeski
1a5497a5ae Feat select image modal (#38)
* feat: select image modal

* chore: fix module config
2022-03-24 16:16:38 -04:00
Ben Warzeski
09e9d865c2 Chore: Test coverage hunt (#36)
* chore: add brand mocking in gallery view

* feat: dev gallery app

* chore: link mock block ids to real block type api

* feat: image settings page features

* chore: more tests

* chore: keystore util and more testing

* chore: more tests

* chore: re-install lint plugin...

* chore: lint fixes

* chore: moar tests

* chore: remove brand from module.config and link gallery to edx.org brand
2022-03-24 11:15:32 -04:00
Ben Warzeski
284601d6d2 feat: image dimension lock logic update (#34) 2022-03-22 10:03:26 -04:00
Ben Warzeski
3b8a7780ac I18n example (#33)
* feat: i18n pt 1

* fix: prevent float calculations when updating dimensions
2022-03-17 14:08:46 -04:00
Ben Warzeski
a79da4cb2d feat: useState test util (#31) 2022-03-16 11:27:08 -04:00
Jawayria
9e84e0ecf0 fix: update dependencies (#32)
* fix: update dependencies

Co-authored-by: M Umar Khan <umar.khan@arbisoft.com>
2022-03-16 09:45:35 -04:00
Ben Warzeski
32e4d5f7a1 feat: Image settings feature and design completeness (#30)
* feat: devgallery api mode

* feat: dev gallery app

* chore: link mock block ids to real block type api

* feat: image settings page features

* fix: update tests

* fix: console message cleanup

* fix: test fixes from code walkthrough with ray
2022-03-15 16:32:07 -04:00
connorhaugh
4ae2d1230b feat: insert images into text editor html (#29)
This work adds the functionality that when an image is selected ineither the select image step of the image upload modal, or by using the toolbar inside tinymce, the requisite image is loaded into the settings page, and on the click of the save button, it is inserted into tinymce html.
2022-03-15 10:53:56 -04:00
connorhaugh
5258e93972 Test add requests redux tests (#24)
Add redux tests as well as get save functionality working.
2022-03-07 19:43:46 -05:00
connorhaugh
c4cd0c44ce test: update editor-level tests (#26)
To complete https://openedx.atlassian.net/browse/TNL-9601
2022-03-07 15:03:13 -05:00
Ben Warzeski
ac0d261e89 Gallery app (#27)
* feat: devgallery api mode

* chore: update react version

* feat: dev gallery app

* chore: fix component tests

* chore: lint fixes

* chore: link mock block ids to real block type api
2022-03-07 13:35:28 -05:00
Raymond Zhou
6f0f6296e4 test: cms api tests + extra lint (#25) 2022-03-01 11:32:03 -05:00
Ben Warzeski
9c9d3c8fdf feat: image upload skeleton (#22) 2022-03-01 11:17:03 -05:00
Raymond Zhou
f3d80995c5 test: cms urls (#23) 2022-02-25 19:24:05 -05:00
connorhaugh
7ccba63a85 test: add TextEditor Tests (#21) 2022-02-25 15:06:12 -05:00
Raymond Zhou
042246be86 Test: editor header component (#20)
* test: header tests start

* test: EditableHeader test ready for review 1

* test: editor header tests complete

* test: fixing up nits

* test: ben's corrections
2022-02-25 12:15:13 -05:00
Ben Warzeski
1a1900f213 feat: redux tests pt 1 (#19)
* feat: redux tests pt 1

* chore: add app thunkAction tests

* chore: resolve lint issues

* Update src/editors/data/redux/app/reducer.test.js

Co-authored-by: connorhaugh <49422820+connorhaugh@users.noreply.github.com>

Co-authored-by: connorhaugh <49422820+connorhaugh@users.noreply.github.com>
2022-02-24 16:59:30 -05:00
Ben Warzeski
2b0346fe84 chore: add tests for EditorHeader hooks (#18) 2022-02-23 11:55:53 -05:00
connorhaugh
6ca93a7297 test: add EditorFooter tests (#16)
* test: add EditorFooter tests
2022-02-22 13:24:15 -05:00
Ben Warzeski
d2e89d7b28 chore: rename lms service to cms (#15)
* chore: rename lms service to cms

* fix: remove typo

* fix: remove old selector
2022-02-22 12:59:37 -05:00
Ben Warzeski
362139edd2 feat: mock paragon and i18n library for tests 2022-02-22 12:45:30 -05:00
Ben Warzeski
5a1d71a62c !refactor: Breaking Change refactor use Redux. No release 2022-02-18 13:16:36 -05:00
connorhaugh
eef30348fd feat: add plugins including image-upload (#12)
This adds the image upload "plugin" and all the desired built-in plugins.
Per the following two tickets
https://openedx.atlassian.net/browse/TNL-9367
https://openedx.atlassian.net/browse/TNL-9475

I need to add the requisite plugins, as well as a framework for the image upload modal.
2022-02-14 12:33:49 -05:00
connorhaugh
2f931b5bdb fix: dont encode with uri twice (#10) 2022-01-27 12:39:19 -05:00
connorhaugh
d8c6b8dddd feat: Text Editor and V2 Editor Framework (#9)
Text Editor and V2 Editor Framework. Documentation to come.
2022-01-25 14:04:57 -05:00
kenclary
c93c5e986a Merge pull request #8 from edx/kenclary/release-fix
fix: correct project links in package.json
2021-12-15 11:34:36 -05:00
Ken Clary
bb7360ac93 fix: correct project links in package.json 2021-12-15 11:31:46 -05:00
kenclary
f7935deea0 Merge pull request #7 from edx/kenclary/release-fix
fix: correct(?) configuration of main branch for semantic-release.
2021-12-15 11:01:48 -05:00
Ken Clary
b98fe329af fix: correct(?) configuration of main branch for semantic-release. 2021-12-15 10:52:22 -05:00
kenclary
2bd75952f0 Merge pull request #6 from edx/kenclary/security
fix: update dependencies for security.
2021-12-14 17:22:23 -05:00
Ken Clary
f44017e5f4 fix: update dependencies for security. 2021-12-14 17:12:22 -05:00
kenclary
621f39f8ca Merge pull request #4 from edx/kenclary/release
fix: base semantic-release on main branch.
2021-12-13 11:30:33 -05:00
Ken Clary
159ecc84c3 fix: base semantic-release on main branch. 2021-12-09 16:06:16 -05:00
kenclary
e5a1694c90 Merge pull request #3 from edx/kclary/node
fix: update node to 16, using .nvmrc file, to support later versions of semantic-release.
2021-12-03 16:48:58 -05:00
Ken Clary
b37db23e8f fix: update node to 16, using .nvmrc file, to support later versions of semantic-release. 2021-12-03 12:10:06 -05:00
kenclary
105c29a67a Merge pull request #2 from edx/kenclary-initial
feat: setup repo as NPM package with edX standards.
2021-12-01 20:09:26 -05:00
Ken Clary
a48eb0aaf4 feat: setup repo as NPM package with edX standards. 2021-12-01 12:41:54 -05:00
kenclary
7142f89211 Merge pull request #1 from edx/kenclary-adr
Create 0001-library-adr.rst
2021-11-23 09:02:46 -05:00
kenclary
f547e5ed1f Create 0001-library-adr.rst
Draft of ADR for repo.
2021-11-22 16:52:19 -05:00
David Joy
9ed78de9e2 Initial commit 2021-11-19 14:28:50 -05:00
1946 changed files with 186994 additions and 42741 deletions

20
.env
View File

@@ -1,3 +1,4 @@
APP_ID='authoring'
NODE_ENV='production'
ACCESS_TOKEN_COOKIE_NAME=''
BASE_URL=''
@@ -16,14 +17,31 @@ LOGO_URL=''
LOGO_WHITE_URL=''
LOGOUT_URL=null
MARKETING_SITE_BASE_URL=''
TERMS_OF_SERVICE_URL=''
PRIVACY_POLICY_URL=''
ORDER_HISTORY_URL=''
PUBLISHER_BASE_URL=''
REFRESH_ACCESS_TOKEN_ENDPOINT=''
SEGMENT_KEY=''
SITE_NAME=''
STUDIO_SHORT_NAME='Studio'
SUPPORT_EMAIL=''
SUPPORT_URL=''
USER_INFO_COOKIE_NAME=''
ENABLE_ACCESSIBILITY_PAGE=false
ENABLE_PROGRESS_GRAPH_SETTINGS=false
ENABLE_TEAM_TYPE_SETTING=false
ENABLE_NEW_EDITOR_PAGES=true
ENABLE_UNIT_PAGE=false
ENABLE_ASSETS_PAGE=false
ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN=false
ENABLE_TAGGING_TAXONOMY_PAGES=true
ENABLE_CERTIFICATE_PAGE=true
BBB_LEARN_MORE_URL=''
HOTJAR_APP_ID=''
HOTJAR_VERSION=6
HOTJAR_DEBUG=false
INVITE_STUDENTS_EMAIL_TO=''
ENABLE_HOME_PAGE_COURSE_API_V2=true
ENABLE_CHECKLIST_QUALITY=''
ENABLE_GRADING_METHOD_IN_PROBLEMS=false
LIBRARY_SUPPORTED_BLOCKS="problem,video,html"

View File

@@ -1,6 +1,7 @@
APP_ID='authoring'
NODE_ENV='development'
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
BASE_URL='localhost:2001'
BASE_URL='http://localhost:2001'
CREDENTIALS_BASE_URL='http://localhost:18150'
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
DISCOVERY_API_BASE_URL=
@@ -16,16 +17,34 @@ LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
LOGOUT_URL='http://localhost:18000/logout'
MARKETING_SITE_BASE_URL='http://localhost:18000'
TERMS_OF_SERVICE_URL=
PRIVACY_POLICY_URL=
ORDER_HISTORY_URL='localhost:1996/orders'
PORT=2001
PUBLISHER_BASE_URL=
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
SEGMENT_KEY=null
SITE_NAME='edX'
SITE_NAME='Your Plaform Name Here'
STUDIO_BASE_URL='http://localhost:18010'
SUPPORT_EMAIL='support@example.com'
STUDIO_SHORT_NAME='Studio'
SUPPORT_EMAIL=
SUPPORT_URL='https://support.edx.org'
USER_INFO_COOKIE_NAME='edx-user-info'
ENABLE_ACCESSIBILITY_PAGE=false
ENABLE_PROGRESS_GRAPH_SETTINGS=false
ENABLE_TEAM_TYPE_SETTING=false
ENABLE_NEW_EDITOR_PAGES=true
ENABLE_UNIT_PAGE=false
ENABLE_ASSETS_PAGE=false
ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN=true
ENABLE_CERTIFICATE_PAGE=true
ENABLE_NEW_VIDEO_UPLOAD_PAGE=true
ENABLE_TAGGING_TAXONOMY_PAGES=true
BBB_LEARN_MORE_URL=''
HOTJAR_APP_ID=''
HOTJAR_VERSION=6
HOTJAR_DEBUG=true
INVITE_STUDENTS_EMAIL_TO="someone@domain.com"
ENABLE_HOME_PAGE_COURSE_API_V2=true
ENABLE_CHECKLIST_QUALITY=true
ENABLE_GRADING_METHOD_IN_PROBLEMS=false
LIBRARY_SUPPORTED_BLOCKS="problem,video,html"

View File

@@ -1,5 +1,6 @@
APP_ID='authoring'
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
BASE_URL='localhost:2001'
BASE_URL='http://localhost:2001'
CREDENTIALS_BASE_URL='http://localhost:18150'
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
DISCOVERY_API_BASE_URL='http://localhost:18381'
@@ -22,9 +23,20 @@ REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
SEGMENT_KEY=null
SITE_NAME='edX'
STUDIO_BASE_URL='http://localhost:18010'
STUDIO_SHORT_NAME='Studio'
SUPPORT_EMAIL='support@example.com'
SUPPORT_URL='https://support.edx.org'
USER_INFO_COOKIE_NAME='edx-user-info'
ENABLE_PROGRESS_GRAPH_SETTINGS=false
ENABLE_TEAM_TYPE_SETTING=false
ENABLE_NEW_EDITOR_PAGES=true
ENABLE_UNIT_PAGE=true
ENABLE_ASSETS_PAGE=false
ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN=true
ENABLE_CERTIFICATE_PAGE=true
ENABLE_TAGGING_TAXONOMY_PAGES=true
BBB_LEARN_MORE_URL=''
INVITE_STUDENTS_EMAIL_TO="someone@domain.com"
ENABLE_HOME_PAGE_COURSE_API_V2=true
ENABLE_CHECKLIST_QUALITY=true
ENABLE_GRADING_METHOD_IN_PROBLEMS=false
LIBRARY_SUPPORTED_BLOCKS="problem,video,html"

View File

@@ -1,4 +1,6 @@
coverage/*
dist/
node_modules/
jest.config.js
jest.config.js
env.config.jsx
example.env.config.jsx

View File

@@ -1,6 +1,9 @@
const { createConfig } = require('@edx/frontend-build');
const path = require('path');
// eslint-disable-next-line import/no-extraneous-dependencies
const { createConfig } = require('@openedx/frontend-build');
module.exports = createConfig('eslint',
module.exports = createConfig(
'eslint',
{
rules: {
'jsx-a11y/label-has-associated-control': [2, {
@@ -8,6 +11,25 @@ module.exports = createConfig('eslint',
}],
'template-curly-spacing': 'off',
'react-hooks/exhaustive-deps': 'off',
indent: 'off',
'no-restricted-exports': 'off',
// There is no reason to disallow this syntax anymore; we don't use regenerator-runtime in new browsers
'no-restricted-syntax': 'off',
},
});
settings: {
// Import URLs should be resolved using aliases
'import/resolver': {
webpack: {
config: path.resolve(__dirname, 'webpack.dev.config.js'),
},
},
},
overrides: [
{
files: ['plugins/**/*.test.jsx'],
rules: {
'import/no-extraneous-dependencies': 'off',
},
},
],
},
);

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

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

27
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,27 @@
## Description
Describe what this pull request changes, and why. Include implications for people using this change.
Design decisions and their rationales should be documented in the repo (docstring / ADR), per
Useful information to include:
- Which edX user roles will this change impact? Common user roles are "Learner", "Course Author",
"Developer", and "Operator".
- Include screenshots for changes to the UI (ideally, both "before" and "after" screenshots, if applicable).
- Provide links to the description of corresponding configuration changes. Remember to correctly annotate these
changes.
## Supporting information
Link to other information about the change, such as Jira issues, GitHub issues, or Discourse discussions.
Be sure to check they are publicly readable, or if not, repeat the information here.
## Testing instructions
Please provide detailed step-by-step instructions for testing this change.
## Other information
Include anything else that will help reviewers and consumers understand the change.
- Does this change depend on other changes elsewhere?
- Any special concerns or limitations? For example: deprecations, migrations, security, or accessibility.

View File

@@ -16,4 +16,4 @@ jobs:
secrets:
GITHUB_APP_ID: ${{ secrets.GRAPHQL_AUTH_APP_ID }}
GITHUB_APP_PRIVATE_KEY: ${{ secrets.GRAPHQL_AUTH_APP_PEM }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_ISSUE_BOT_TOKEN }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_ISSUE_BOT_TOKEN }}

View File

@@ -0,0 +1,20 @@
# This workflow runs when a comment is made on the ticket
# If the comment starts with "label: " it tries to apply
# the label indicated in rest of comment.
# If the comment starts with "remove label: ", it tries
# to remove the indicated label.
# Note: Labels are allowed to have spaces and this script does
# not parse spaces (as often a space is legitimate), so the command
# "label: really long lots of words label" will apply the
# label "really long lots of words label"
name: Allows for the adding and removing of labels via comment
on:
issue_comment:
types: [created]
jobs:
add_remove_labels:
uses: openedx/.github/.github/workflows/add-remove-label-on-comment.yml@master

View File

@@ -10,4 +10,4 @@ on:
jobs:
version-check:
uses: openedx/.github/.github/workflows/lockfileversion-check.yml@master
uses: openedx/.github/.github/workflows/lockfileversion-check-v3.yml@master

12
.github/workflows/self-assign-issue.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# This workflow runs when a comment is made on the ticket
# If the comment starts with "assign me" it assigns the author to the
# ticket (case insensitive)
name: Assign comment author to ticket if they say "assign me"
on:
issue_comment:
types: [created]
jobs:
self_assign_by_comment:
uses: openedx/.github/.github/workflows/self-assign-issue.yml@master

View File

@@ -0,0 +1,12 @@
name: Update Browserslist DB
on:
schedule:
- cron: '0 0 * * 1'
workflow_dispatch:
jobs:
update-browserslist:
uses: openedx/.github/.github/workflows/update-browserslist-db.yml@master
secrets:
requirements_bot_github_token: ${{ secrets.requirements_bot_github_token }}

View File

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

9
.gitignore vendored
View File

@@ -20,3 +20,12 @@ temp/babel-plugin-react-intl
/temp
/.vscode
/module.config.js
# Local environment overrides
.env.private
# Messages .json files fetched by atlas
src/i18n/messages/
# environment js config
env.config.jsx

2
.nvmrc
View File

@@ -1 +1 @@
v16
20

35
.stylelintrc.json Normal file
View File

@@ -0,0 +1,35 @@
{
"extends": ["@edx/stylelint-config-edx"],
"rules": {
"selector-pseudo-class-no-unknown": [true, {
"ignorePseudoClasses": ["export"]
}],
"unit-no-unknown": [true, {
"ignoreUnits": ["\\.5"]
}],
"property-no-vendor-prefix": [true, {
"ignoreProperties": ["animation", "filter", "transform", "transition"]
}],
"value-no-vendor-prefix": [true, {
"ignoreValues": ["fill-available"]
}],
"function-no-unknown": null,
"number-leading-zero": "never",
"no-descending-specificity": null,
"selector-class-pattern": null,
"scss/no-global-function-names": null,
"color-hex-case": "upper",
"color-hex-length": "long",
"scss/dollar-variable-empty-line-before": null,
"scss/dollar-variable-colon-space-after": "at-least-one-space",
"at-rule-no-unknown": null,
"scss/at-rule-no-unknown": true,
"scss/at-import-partial-extension": null,
"scss/comment-no-empty": null,
"import-notation": "string",
"property-no-unknown": [true, {
"ignoreProperties": ["xs", "sm", "md", "lg", "xl", "xxl"]
}],
"alpha-value-notation": "number"
}
}

View File

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

2
CODEOWNERS Normal file
View File

@@ -0,0 +1,2 @@
# The following users are the maintainers of all frontend-app-authoring files
* @openedx/2u-tnl

38
Makefile Executable file → Normal file
View File

@@ -1,22 +1,17 @@
transifex_resource = frontend-app-course-authoring
export TRANSIFEX_RESOURCE = ${transifex_resource}
transifex_langs = "ar,fr,es_419,zh_CN"
intl_imports = ./node_modules/.bin/intl-imports.js
transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json
tx_url1 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/translation/en/strings/
tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/source/
# This directory must match .babelrc .
transifex_temp = ./temp/babel-plugin-react-intl
transifex_temp = ./temp/babel-plugin-formatjs
precommit:
npm run lint
npm audit
requirements:
npm install
npm ci
i18n.extract:
# Pulling display strings from .jsx files into .json files...
@@ -34,20 +29,18 @@ detect_changed_source_translations:
# Checking for changed translations...
git diff --exit-code $(i18n)
# Pushes translations to Transifex. You must run make extract_translations first.
push_translations:
# Pushing strings to Transifex...
tx push -s
# Fetching hashes from Transifex...
./node_modules/@edx/reactifex/bash_scripts/get_hashed_strings_v3.sh
# Writing out comments to file...
$(transifex_utils) $(transifex_temp) --comments --v3-scripts-path
# Pushing comments to Transifex...
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
# Pulls translations from Transifex.
pull_translations:
tx pull -f --mode reviewed --languages=$(transifex_langs)
rm -rf src/i18n/messages
mkdir src/i18n/messages
cd src/i18n/messages \
&& atlas pull $(ATLAS_OPTIONS) \
translations/frontend-component-ai-translations/src/i18n/messages:frontend-component-ai-translations \
translations/frontend-platform/src/i18n/messages:frontend-platform \
translations/paragon/src/i18n/messages:paragon \
translations/frontend-component-footer/src/i18n/messages:frontend-component-footer \
translations/frontend-app-course-authoring/src/i18n/messages:frontend-app-course-authoring
$(intl_imports) frontend-component-ai-translations frontend-platform paragon frontend-component-footer frontend-app-course-authoring
# This target is used by Travis.
validate-no-uncommitted-package-lock-changes:
@@ -59,7 +52,8 @@ validate:
make validate-no-uncommitted-package-lock-changes
npm run i18n_extract
npm run lint -- --max-warnings 0
npm run test
npm run types
npm run test:ci
npm run build
.PHONY: validate.ci

View File

@@ -1,39 +1,342 @@
|Build Status| |Codecov| |license|
frontend-app-authoring
######################
frontend-app-course-authoring
=============================
|license-badge| |status-badge| |codecov-badge|
Please tag `@edx/teaching-and-learning <https://github.com/orgs/edx/teams/teaching-and-learning>`_ on any PRs or issues. Thanks.
Prerequisite
Purpose
*******
This implements most of the frontend for **Open edX Studio**, allowing authors to create and edit courses, libraries, and their learning components.
A few parts of Studio still default to the `"legacy" pages defined in edx-platform <https://github.com/openedx/edx-platform/tree/master/cms>`_, but those are rapidly being deprecated and replaced with the React- and Paragon-based pages defined here.
Getting Started
************
Prerequisites
=============
`Tutor`_ is currently recommended as a development environment for the Authoring
MFE. Most likely, it already has this MFE configured; however, you'll need to
make some changes in order to run it in development mode. You can refer
to the `relevant tutor-mfe documentation`_ for details, or follow the quick
guide below.
.. _Tutor: https://github.com/overhangio/tutor
.. _relevant tutor-mfe documentation: https://github.com/overhangio/tutor-mfe#mfe-development
Cloning and Setup
=================
1. Clone your new repo:
.. code-block:: bash
git clone https://github.com/openedx/frontend-app-authoring.git
2. Use node v20.x.
The current version of the micro-frontend build scripts supports node 20.
Using other major versions of node *may* work, but this is unsupported. For
convenience, this repository includes an ``.nvmrc`` file to help in setting the
correct node version via `nvm <https://github.com/nvm-sh/nvm>`_.
3. Stop the Tutor devstack, if it's running: ``tutor dev stop``
4. Next, we need to tell Tutor that we're going to be running this repo in
development mode, and it should be excluded from the ``mfe`` container that
otherwise runs every MFE. Run this:
.. code-block:: bash
tutor mounts add /path/to/frontend-app-authoring
5. Start Tutor in development mode. This command will start the LMS and Studio,
and other required MFEs like ``authn`` and ``account``, but will not start
the Authoring MFE, which we're going to run on the host instead of in a
container managed by Tutor. Run:
.. code-block:: bash
tutor dev start lms cms mfe
Startup
=======
1. Install npm dependencies:
.. code-block:: bash
cd frontend-app-authoring && npm ci
2. Start the dev server:
.. code-block:: bash
npm run dev
Then you can access the app at http://apps.local.openedx.io:2001/course-authoring/home
Troubleshooting
---------------
* If you see an "Invalid Host header" error, then you're probably using a different domain name for your devstack such as
``local.edly.io`` or ``local.overhang.io`` (not the new recommended default, ``local.openedx.io``). In that case, run
these commands to update your devstack's domain names:
.. code-block:: bash
tutor dev stop
tutor config save --set LMS_HOST=local.openedx.io --set CMS_HOST=studio.local.openedx.io
tutor dev launch -I --skip-build
tutor dev stop authoring # We will run this MFE on the host
* If tutor-mfe is not starting the authoring MFE in development mode (eg. `tutor dev start authoring` fails), it may be due to
using a tutor version that expects the MFE name to be frontend-app-course-authoring (the previous name of this repo). To fix
this, you can rename the cloned repo directory to frontend-app-course-authoring. More information can be found in
[this forum post](https://discuss.openedx.org/t/repo-rename-frontend-app-course-authoring-frontend-app-authoring/13930/2)
Features
********
Feature: Pages and Resources Studio Tab
=======================================
Enables a "Pages & Resources" menu item in Studio, under the "Content" menu.
.. image:: ./docs/readme-images/feature-pages-resources.png
Requirements
------------
The following are requirements for this feature to function correctly:
* ``edx-platform`` Waffle flags:
* ``discussions.pages_and_resources_mfe``: must be enabled for the set of users meant to access this feature.
* `frontend-app-learning <https://github.com/openedx/frontend-app-learning>`_: This MFE expects it to be the LMS frontend.
* `frontend-app-discussions <https://github.com/openedx/frontend-app-discussions/>`_: This is what the "Discussions" configuration provided by this feature actually configures. Without it, discussion settings are ignored.
Configuration
-------------
In additional to the standard settings, the following local configuration items are required:
* ``LEARNING_BASE_URL``: points to Learning MFE; necessary so that the `View Live` button works
* ``ENABLE_PROGRESS_GRAPH_SETTINGS``: allow enabling or disabling the learner progress graph course-wide
Feature Description
-------------------
Clicking on the "Pages & Resources" menu item takes the user to the course's ``pages-and-resources`` standalone page in this MFE. (In a devstack, for instance: http://localhost:2001/course/course-v1:edX+DemoX+Demo_Course/pages-and-resources.)
UX-wise, **Pages & Resources** is meant to look like a Studio tab, so reproduces Studio's header.
For a particular course, this page allows one to:
* Configure the new Discussions MFE (making this a requirement for it). This includes:
* Enabling/disabling the feature entirely
* Picking a different discussion provider, while showing a comparison matrix between them:
* edX
* Ed Discussion
* InScribe
* Piazza
* Yellowdig
* Allowing to configure the selected provider
* Enable/Disable learner progress
* Enable/Disable learner notes
* Enable/Disable the learner wiki
* Enable/Disable the LMS calculator
* Go to the textbook management page in Studio (in a devstack: http://localhost:18010/textbooks/course-v1:edX+DemoX+Demo_Course)
* Go to the custom page management page in Studio(in a devstack http://localhost:18010/tabs/course-v1:edX+DemoX+Demo_Course)
Feature: New React XBlock Editors
=================================
.. image:: ./docs/readme-images/feature-problem-editor.png
This allows an operator to enable the use of new React editors for the HTML, Video, and Problem XBlocks, all of which are provided here.
Requirements
------------
* ``edx-platform`` Waffle flags:
* ``new_core_editors.use_new_text_editor``: must be enabled for the new HTML Xblock editor to be used in Studio
* ``new_core_editors.use_new_video_editor``: must be enabled for the new Video Xblock editor to be used in Studio
* ``new_core_editors.use_new_problem_editor``: must be enabled for the new Problem Xblock editor to be used in Studio
Feature Description
-------------------
When a corresponding waffle flag is set, upon editing a block in Studio, the view is rendered by this MFE instead of by the XBlock's authoring view. The user remains in Studio.
Feature: New Proctoring Exams View
==================================
.. image:: ./docs/readme-images/feature-proctored-exams.png
Requirements
------------
* ``edx-platform`` Django settings:
* ``ZENDESK_*``: necessary if automatic ZenDesk ticket creation is desired
* ``edx-platform`` Feature flags:
* ``ENABLE_EXAM_SETTINGS_HTML_VIEW``: this feature flag must be enabled for the link to the settings view to be shown
* `edx-exams <https://github.com/edx/edx-exams>`_: for this feature to work, the ``edx-exams`` IDA must be deployed and its API accessible by the browser
Configuration
-------------
In additional to the standard settings, the following local configuration item is required:
* ``EXAMS_BASE_URL``: URL to the ``edx-exams`` deployment
Feature Description
-------------------
In Studio, a new item ("Proctored Exam Settings") is added to "Other Course Settings" in the course's "Certificates" settings page. When clicked, this takes the author to the corresponding page in the Course Authoring MFE, where one can:
* Enable proctored exams for the course
* Allow opting out of proctored exams
* Select a proctoring provider
* Enable automatic creation of Zendesk tickets for "suspicious" proctored exam attempts
Feature: Advanced Settings
==========================
.. image:: ./docs/readme-images/feature-advanced-settings.png
Requirements
------------
* ``edx-platform`` Waffle flags:
* ``contentstore.new_studio_mfe.use_new_advanced_settings_page``: this feature flag must be enabled for the link to the settings view to be shown. It can be enabled on a per-course basis.
Feature Description
-------------------
In Studio, the "Advanced Settings" page for each enabled course will now be served by this frontend, instead of the UI built into edx-platform. The advanced settings page holds many different settings for the course, such as what features or XBlocks are enabled.
Feature: Files & Uploads
==========================
.. image:: ./docs/readme-images/feature-files-uploads.png
Requirements
------------
* ``edx-platform`` Waffle flags:
* ``contentstore.new_studio_mfe.use_new_files_uploads_page``: this feature flag must be enabled for the link to the Files & Uploads page to go to the MFE. It can be enabled on a per-course basis.
Feature Description
-------------------
In Studio, the "Files & Uploads" page for each enabled course will now be served by this frontend, instead of the UI built into edx-platform. This page allows managing static asset files like PDFs, images, etc. used for the course.
Feature: Course Updates
==========================
.. image:: ./docs/readme-images/feature-course-updates.png
Requirements
------------
* ``edx-platform`` Waffle flags:
* ``contentstore.new_studio_mfe.use_new_updates_page``: this feature flag must be enabled.
Feature: Import/Export Pages
============================
.. image:: ./docs/readme-images/feature-export.png
Requirements
------------
* ``edx-platform`` Waffle flags:
* ``contentstore.new_studio_mfe.use_new_export_page``: this feature flag will change the CMS to link to the new export page.
* ``contentstore.new_studio_mfe.use_new_import_page``: this feature flag will change the CMS to link to the new import page.
Feature: Tagging/Taxonomy Pages
================================
.. image:: ./docs/readme-images/feature-tagging-taxonomy-pages.png
Requirements
------------
* ``edx-platform`` Waffle flags:
* ``new_studio_mfe.use_tagging_taxonomy_list_page``: this feature flag must be enabled.
Configuration
-------------
In additional to the standard settings, the following local configuration items are required:
* ``ENABLE_TAGGING_TAXONOMY_PAGES``: must be enabled (which it is by default) in order to actually enable/show the new
Tagging/Taxonomy functionality.
Feature: Libraries V2/Legacy Tabs
=================================
Configuration
-------------
In additional to the standard settings, the following local configurations can be set to switch between different library modes:
* ``MEILISEARCH_ENABLED``: Studio setting which is enabled when the `meilisearch plugin`_ is installed.
* ``edx-platform`` Waffle flags:
* ``contentstore.new_studio_mfe.disable_legacy_libraries``: this feature flag must be OFF to show legacy Libraries V1
* ``contentstore.new_studio_mfe.disable_new_libraries``: this feature flag must be OFF to show Content Libraries V2
.. _meilisearch plugin: https://github.com/open-craft/tutor-contrib-meilisearch
Developing
**********
`Devstack <https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/latest/installation/index.html>`_. If you start Devstack with ``make dev.up.studio`` that should give you everything you need as a companion to this frontend.
Installation and Startup
------------------------
1. Clone the repo:
``git clone https://github.com/openedx/frontend-app-course-authoring.git``
2. Install npm dependencies:
``cd frontend-app-course-authoring && npm install``
3. Start the dev server:
``npm start``
The dev server is running at `http://localhost:2001 <http://localhost:2001>`_.
If your devstack includes the default Demo course, you can visit the following URLs to see content:
- `Proctored Exam Settings <http://localhost:2001/course/course-v1:edX+DemoX+Demo_Course/proctored-exam-settings>`_
- `Pages and Resources <http://localhost:2001/course/course-v1:edX+DemoX+Demo_Course/pages-and-resources>`_ (work in progress)
- `Pages and Resources <http://localhost:2001/course/course-v1:edX+DemoX+Demo_Course/pages-and-resources>`_
Troubleshooting
========================
* ``npm ERR! gyp ERR! build error`` while running npm install on Macs with M1 processors: Probably due to a compatibility issue of node-canvas with M1.
Run ``brew install pkg-config pixman cairo pango libpng jpeg giflib librsvg`` before ``npm install`` to get the correct versions of the dependencies.
If there is still an error, look for "no package [...] found" in the error message and install missing package via brew.
(https://github.com/Automattic/node-canvas/issues/1733)
Deploying
*********
Production Build
----------------
================
The production build is created with ``npm run build``.
@@ -41,5 +344,94 @@ The production build is created with ``npm run build``.
:target: https://travis-ci.com/edx/frontend-app-course-authoring
.. |Codecov| image:: https://codecov.io/gh/edx/frontend-app-course-authoring/branch/master/graph/badge.svg
:target: https://codecov.io/gh/edx/frontend-app-course-authoring
.. |license| image:: https://img.shields.io/npm/l/@edx/frontend-app-course-authoring.svg
:target: @edx/frontend-app-course-authoring
.. |license| image:: https://img.shields.io/npm/l/@edx/frontend-app-authoring.svg
:target: @edx/frontend-app-authoring
Internationalization
====================
Please see refer to the `frontend-platform i18n howto`_ for documentation on
internationalization.
.. _frontend-platform i18n howto: https://github.com/openedx/frontend-platform/blob/master/docs/how_tos/i18n.rst
Getting Help
************
If you're having trouble, we have discussion forums at
https://discuss.openedx.org where you can connect with others in the community.
Our real-time conversations are on Slack. You can request a `Slack
invitation`_, then join our `community Slack workspace`_. Because this is a
frontend repository, the best place to discuss it would be in the `#wg-frontend
channel`_.
For anything non-trivial, the best path is to open an issue in this repository
with as many details about the issue you are facing as you can provide.
https://github.com/openedx/frontend-app-course-authoring/issues
For more information about these options, see the `Getting Help`_ page.
.. _Slack invitation: https://openedx.org/slack
.. _community Slack workspace: https://openedx.slack.com/
.. _#wg-frontend channel: https://openedx.slack.com/archives/C04BM6YC7A6
.. _Getting Help: https://openedx.org/community/connect
License
*******
The code in this repository is licensed under the AGPLv3 unless otherwise
noted.
Please see `LICENSE <LICENSE>`_ for details.
Contributing
************
Contributions are very welcome. Please read `How To Contribute`_ for details.
.. _How To Contribute: https://openedx.org/r/how-to-contribute
This project is currently accepting all types of contributions, bug fixes,
security fixes, maintenance work, or new features. However, please make sure
to have a discussion about your new feature idea with the maintainers prior to
beginning development to maximize the chances of your change being accepted.
You can start a conversation by creating a new issue on this repo summarizing
your idea.
The Open edX Code of Conduct
****************************
All community members are expected to follow the `Open edX Code of Conduct`_.
.. _Open edX Code of Conduct: https://openedx.org/code-of-conduct/
People
******
The assigned maintainers for this component and other project details may be
found in `Backstage`_. Backstage pulls this data from the ``catalog-info.yaml``
file in this repo.
.. _Backstage: https://open-edx-backstage.herokuapp.com/catalog/default/component/frontend-app-course-authoring
Reporting Security Issues
*************************
Please do not report security issues in public, and email security@openedx.org instead.
.. |license-badge| image:: https://img.shields.io/github/license/openedx/frontend-app-course-authoring.svg
:target: https://github.com/openedx/frontend-app-course-authoring/blob/master/LICENSE
:alt: License
.. |status-badge| image:: https://img.shields.io/badge/Status-Maintained-brightgreen
.. |codecov-badge| image:: https://codecov.io/github/openedx/frontend-app-course-authoring/coverage.svg?branch=master
:target: https://codecov.io/github/openedx/frontend-app-course-authoring?branch=master
:alt: Codecov

18
catalog-info.yaml Normal file
View File

@@ -0,0 +1,18 @@
# This file records information about this repo. Its use is described in OEP-55:
# https://open-edx-proposals.readthedocs.io/en/latest/processes/oep-0055-proc-project-maintainers.html
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: 'frontend-app-authoring'
description: "The frontend (MFE) for Open edX Authoring (aka Studio)"
links:
- url: "https://github.com/openedx/frontend-app-authoring"
title: "Frontend app authoring"
icon: "Web"
annotations:
openedx.org/arch-interest-groups: ""
spec:
owner: group:2u-tnl
type: 'website'
lifecycle: 'production'

View File

@@ -8,3 +8,6 @@ coverage:
default:
target: auto
threshold: 0%
ignore:
- "src/grading-settings/grading-scale/react-ranger.js"
- "src/index.js"

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

24
example.env.config.jsx Normal file
View File

@@ -0,0 +1,24 @@
import WholeCourseTranslation from '@edx/course-app-translation-plugin';
import { PLUGIN_OPERATIONS, DIRECT_PLUGIN } from '@openedx/frontend-plugin-framework';
// Load environment variables from .env file
const config = {
...process.env,
pluginSlots: {
additional_course_plugin: {
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'whole-course-translation-plugin',
type: DIRECT_PLUGIN,
priority: 1,
RenderWidget: WholeCourseTranslation,
},
},
],
},
},
};
export default config;

View File

@@ -1,17 +1,19 @@
const { createConfig } = require('@edx/frontend-build');
const { createConfig } = require('@openedx/frontend-build');
module.exports = createConfig('jest', {
setupFilesAfterEnv: [
'jest-expect-message',
'<rootDir>/src/setupTest.js',
],
coveragePathIgnorePatterns: [
'src/setupTest.js',
'src/i18n',
],
snapshotSerializers: [
'enzyme-to-json/serializer',
],
moduleNameMapper: {
'^lodash-es$': 'lodash',
'^CourseAuthoring/(.*)$': '<rootDir>/src/$1',
},
modulePathIgnorePatterns: [
'/src/pages-and-resources/utils.test.jsx',
],
});

49450
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,27 @@
{
"name": "@edx/frontend-app-course-authoring",
"name": "@edx/frontend-app-authoring",
"version": "0.1.0",
"description": "Frontend application template",
"repository": {
"type": "git",
"url": "git+https://github.com/openedx/frontend-app-course-authoring.git"
"url": "git+https://github.com/openedx/frontend-app-authoring.git"
},
"browserslist": [
"extends @edx/browserslist-config"
],
"scripts": {
"build": "fedx-scripts webpack",
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"lint:fix": "fedx-scripts eslint --ext .js --ext .jsx . --fix",
"snapshot": "fedx-scripts jest --updateSnapshot",
"i18n_extract": "fedx-scripts formatjs extract",
"stylelint": "stylelint \"plugins/**/*.scss\" \"src/**/*.scss\" \"scss/**/*.scss\" --config .stylelintrc.json",
"lint": "npm run stylelint && fedx-scripts eslint --ext .js --ext .jsx --ext .ts --ext .tsx .",
"lint:fix": "npm run stylelint -- --fix && fedx-scripts eslint --fix --ext .js --ext .jsx --ext .ts --ext .tsx .",
"snapshot": "TZ=UTC fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress",
"test": "fedx-scripts jest --coverage --passWithNoTests"
"start:with-theme": "paragon install-theme && npm start && npm install",
"dev": "PUBLIC_PATH=/authoring/ MFE_CONFIG_API_URL='http://localhost:8000/api/mfe_config/v1' fedx-scripts webpack-dev-server --progress --host apps.local.openedx.io",
"test": "TZ=UTC fedx-scripts jest --coverage --passWithNoTests",
"test:ci": "TZ=UTC fedx-scripts jest --silent --coverage --passWithNoTests",
"types": "tsc --noEmit"
},
"husky": {
"hooks": {
@@ -25,59 +30,101 @@
},
"author": "edX",
"license": "AGPL-3.0",
"homepage": "https://github.com/openedx/frontend-app-course-authoring#readme",
"homepage": "https://github.com/openedx/frontend-app-authoring#readme",
"publishConfig": {
"access": "public"
},
"bugs": {
"url": "https://github.com/openedx/frontend-app-course-authoring/issues"
"url": "https://github.com/openedx/frontend-app-authoring/issues"
},
"dependencies": {
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
"@edx/frontend-build": "^11.0.0",
"@edx/frontend-component-footer": "11.1.1",
"@edx/frontend-lib-content-components": "^1.43.0",
"@edx/frontend-platform": "2.5.1",
"@edx/paragon": "20.6.1",
"@fortawesome/fontawesome-svg-core": "1.2.28",
"@fortawesome/free-brands-svg-icons": "5.11.2",
"@fortawesome/free-regular-svg-icons": "5.11.2",
"@fortawesome/free-solid-svg-icons": "5.11.2",
"@fortawesome/react-fontawesome": "0.1.9",
"@reduxjs/toolkit": "1.5.0",
"classnames": "2.2.6",
"core-js": "3.8.1",
"@codemirror/lang-html": "^6.0.0",
"@codemirror/lang-xml": "^6.0.0",
"@codemirror/lint": "^6.2.1",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.3",
"@edx/browserslist-config": "1.2.0",
"@edx/frontend-component-footer": "^14.1.0",
"@edx/frontend-component-header": "^5.6.0",
"@edx/frontend-enterprise-hotjar": "^2.0.0",
"@edx/frontend-platform": "^8.0.3",
"@edx/openedx-atlas": "^0.6.0",
"@openedx-plugins/course-app-calculator": "file:plugins/course-apps/calculator",
"@openedx-plugins/course-app-edxnotes": "file:plugins/course-apps/edxnotes",
"@openedx-plugins/course-app-learning_assistant": "file:plugins/course-apps/learning_assistant",
"@openedx-plugins/course-app-live": "file:plugins/course-apps/live",
"@openedx-plugins/course-app-ora_settings": "file:plugins/course-apps/ora_settings",
"@openedx-plugins/course-app-proctoring": "file:plugins/course-apps/proctoring",
"@openedx-plugins/course-app-progress": "file:plugins/course-apps/progress",
"@openedx-plugins/course-app-teams": "file:plugins/course-apps/teams",
"@openedx-plugins/course-app-wiki": "file:plugins/course-apps/wiki",
"@openedx-plugins/course-app-xpert_unit_summary": "file:plugins/course-apps/xpert_unit_summary",
"@openedx/frontend-build": "^14.0.14",
"@openedx/frontend-plugin-framework": "^1.2.1",
"@openedx/paragon": "^22.8.1",
"@redux-devtools/extension": "^3.3.0",
"@reduxjs/toolkit": "1.9.7",
"@tanstack/react-query": "4.36.1",
"@tinymce/tinymce-react": "^3.14.0",
"classnames": "2.5.1",
"codemirror": "^6.0.0",
"email-validator": "2.0.4",
"formik": "2.2.6",
"fast-xml-parser": "^4.0.10",
"file-saver": "^2.0.5",
"formik": "2.4.6",
"frontend-components-tinymce-advanced-plugins": "^1.0.3",
"jszip": "^3.10.1",
"lodash": "4.17.21",
"moment": "2.29.2",
"prop-types": "15.7.2",
"react": "16.14.0",
"react-dom": "16.14.0",
"react-redux": "7.1.3",
"react-responsive": "8.1.0",
"react-router": "5.1.2",
"react-router-dom": "5.1.2",
"react-transition-group": "4.4.1",
"meilisearch": "^0.41.0",
"moment": "2.30.1",
"moment-shortformat": "^2.1.0",
"npm": "^10.8.1",
"prop-types": "^15.8.1",
"react": "17.0.2",
"react-datepicker": "^4.13.0",
"react-dom": "17.0.2",
"react-error-boundary": "^4.0.13",
"react-helmet": "^6.1.0",
"react-onclickoutside": "^6.13.0",
"react-redux": "7.2.9",
"react-responsive": "9.0.2",
"react-router": "6.27.0",
"react-router-dom": "6.27.0",
"react-select": "5.8.0",
"react-textarea-autosize": "^8.5.3",
"react-transition-group": "4.4.5",
"redux": "4.0.5",
"regenerator-runtime": "0.13.7",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.4.1",
"reselect": "^4.1.5",
"start": "^5.1.0",
"tinymce": "^5.10.4",
"universal-cookie": "^4.0.4",
"uuid": "^3.4.0",
"xmlchecker": "^0.1.0",
"yup": "0.31.1"
},
"devDependencies": {
"@edx/browserslist-config": "1.0.0",
"@edx/frontend-build": "^11.0.0",
"@edx/reactifex": "^1.0.3",
"@testing-library/jest-dom": "5.16.4",
"@testing-library/react": "12.1.1",
"@edx/react-unit-test-utils": "3.0.0",
"@edx/stylelint-config-edx": "2.3.3",
"@edx/typescript-config": "^1.0.1",
"@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "12.1.5",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^13.2.1",
"axios-mock-adapter": "1.20.0",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.6",
"enzyme-to-json": "^3.6.2",
"glob": "7.1.6",
"husky": "3.1.0",
"react-test-renderer": "16.9.0",
"reactifex": "1.1.1"
"@types/lodash": "^4.17.7",
"axios-mock-adapter": "1.22.0",
"eslint-import-resolver-webpack": "^0.13.8",
"fetch-mock-jest": "^1.5.1",
"husky": "7.0.4",
"jest-canvas-mock": "^2.5.2",
"jest-expect-message": "^1.1.3",
"react-test-renderer": "17.0.2",
"redux-mock-store": "^1.5.4"
}
}

View File

@@ -1,12 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { useIntl } from '@edx/frontend-platform/i18n';
import AppSettingsModal from '../app-settings-modal/AppSettingsModal';
import AppSettingsModal from 'CourseAuthoring/pages-and-resources/app-settings-modal/AppSettingsModal';
import messages from './messages';
function CalculatorSettings({ intl, onClose }) {
/**
* Settings widget for the "calculator" Course App.
* @param {{onClose: () => void}} props
*/
const CalculatorSettings = ({ onClose }) => {
const intl = useIntl();
return (
<AppSettingsModal
appId="calculator"
@@ -17,11 +22,10 @@ function CalculatorSettings({ intl, onClose }) {
onClose={onClose}
/>
);
}
};
CalculatorSettings.propTypes = {
intl: intlShape.isRequired,
onClose: PropTypes.func.isRequired,
};
export default injectIntl(CalculatorSettings);
export default CalculatorSettings;

View File

@@ -0,0 +1,17 @@
{
"name": "@openedx-plugins/course-app-calculator",
"version": "0.1.0",
"description": "Calculator configuration for courses using it",
"peerDependencies": {
"@edx/frontend-app-authoring": "*",
"@edx/frontend-platform": "*",
"@openedx/paragon": "*",
"prop-types": "*",
"react": "*"
},
"peerDependenciesMeta": {
"@edx/frontend-app-authoring": {
"optional": true
}
}
}

View File

@@ -1,12 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { useIntl } from '@edx/frontend-platform/i18n';
import AppSettingsModal from '../app-settings-modal/AppSettingsModal';
import AppSettingsModal from 'CourseAuthoring/pages-and-resources/app-settings-modal/AppSettingsModal';
import messages from './messages';
function NotesSettings({ intl, onClose }) {
/**
* Settings widget for the "edxnotes" Course App.
* @param {{onClose: () => void}} props
*/
const NotesSettings = ({ onClose }) => {
const intl = useIntl();
return (
<AppSettingsModal
appId="edxnotes"
@@ -17,11 +22,10 @@ function NotesSettings({ intl, onClose }) {
onClose={onClose}
/>
);
}
};
NotesSettings.propTypes = {
intl: intlShape.isRequired,
onClose: PropTypes.func.isRequired,
};
export default injectIntl(NotesSettings);
export default NotesSettings;

View File

@@ -0,0 +1,17 @@
{
"name": "@openedx-plugins/course-app-edxnotes",
"version": "0.1.0",
"description": "edxnotes configuration for courses using it",
"peerDependencies": {
"@edx/frontend-app-authoring": "*",
"@edx/frontend-platform": "*",
"@openedx/paragon": "*",
"prop-types": "*",
"react": "*"
},
"peerDependenciesMeta": {
"@edx/frontend-app-authoring": {
"optional": true
}
}
}

View File

@@ -0,0 +1,63 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Hyperlink } from '@openedx/paragon';
import AppSettingsModal from 'CourseAuthoring/pages-and-resources/app-settings-modal/AppSettingsModal';
import { useModel } from 'CourseAuthoring/generic/model-store';
import messages from './messages';
const LearningAssistantSettings = ({ onClose }) => {
const appId = 'learning_assistant';
const appInfo = useModel('courseApps', appId);
const intl = useIntl();
// We need to render more than one link, so we use the bodyChildren prop.
const bodyChildren = (
appInfo?.documentationLinks?.learnMoreOpenaiDataPrivacy && appInfo?.documentationLinks?.learnMoreOpenai
? (
<div className="d-flex flex-column">
{appInfo.documentationLinks?.learnMoreOpenaiDataPrivacy && (
<Hyperlink
className="text-primary-500"
destination={appInfo.documentationLinks.learnMoreOpenaiDataPrivacy}
target="_blank"
rel="noreferrer noopener"
>
{intl.formatMessage(messages.learningAssistantOpenAIDataPrivacyLink)}
</Hyperlink>
)}
{appInfo.documentationLinks?.learnMoreOpenai && (
<Hyperlink
className="text-primary-500"
destination={appInfo.documentationLinks.learnMoreOpenai}
target="_blank"
rel="noreferrer noopener"
>
{intl.formatMessage(messages.learningAssistantOpenAILink)}
</Hyperlink>
)}
</div>
)
: null
);
return (
<AppSettingsModal
appId={appId}
title={intl.formatMessage(messages.heading)}
enableAppHelp={intl.formatMessage(messages.enableLearningAssistantHelp)}
enableAppLabel={intl.formatMessage(messages.enableLearningAssistantLabel)}
bodyChildren={bodyChildren}
onClose={onClose}
/>
);
};
LearningAssistantSettings.propTypes = {
onClose: PropTypes.func.isRequired,
};
export default LearningAssistantSettings;

View File

@@ -0,0 +1,59 @@
import React from 'react';
import { screen, waitFor } from '@testing-library/react';
import { RequestStatus } from 'CourseAuthoring/data/constants';
import { render } from 'CourseAuthoring/pages-and-resources/utils.test';
import LearningAssistantSettings from './Settings';
const onClose = () => { };
describe('Learning Assistant Settings', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('renders', async () => {
const initialState = {
models: {
courseApps: {
learning_assistant:
{
id: 'learning_assistant',
enabled: true,
name: 'Learning Assistant',
description: 'Learning Assistant description',
allowedOperations: {
configure: false,
enable: true,
},
documentationLinks: {
learnMoreOpenaiDataPrivacy: 'www.example.com/learn-more-data-privacy',
learnMoreOpenai: 'www.example.com/learn-more',
},
},
},
},
pagesAndResources: {
loadingStatus: RequestStatus.SUCCESSFUL,
},
};
render(
<LearningAssistantSettings
onClose={onClose}
/>,
{
preloadedState: initialState,
},
);
const toggleDescription = 'Reinforce learning concepts by sharing text-based course content '
+ 'with OpenAI (via API) to power an in-course Learning Assistant. Learners can leave feedback about the quality '
+ 'of the AI-powered experience for use by edX to improve the performance of the tool.';
await waitFor(() => expect(screen.getByRole('heading', { name: 'Configure Learning Assistant' })).toBeInTheDocument());
await waitFor(() => expect(screen.getByText(toggleDescription)).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('Learn more about how OpenAI handles data')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('Learn more about OpenAI API data privacy')).toBeInTheDocument());
});
});

View File

@@ -0,0 +1,28 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
heading: {
id: 'course-authoring.pages-resources.learning-assistant.heading',
defaultMessage: 'Configure Learning Assistant',
},
enableLearningAssistantLabel: {
id: 'course-authoring.pages-resources.learning_assistant.enable-learning-assistant.label',
defaultMessage: 'Learning Assistant',
},
enableLearningAssistantHelp: {
id: 'course-authoring.pages-resources.learning_assistant.enable-learning-assistant.help',
defaultMessage: `Reinforce learning concepts by sharing text-based course content with OpenAI (via API) to power
an in-course Learning Assistant. Learners can leave feedback about the quality of the AI-powered experience for
use by edX to improve the performance of the tool.`,
},
learningAssistantOpenAILink: {
id: 'course-authoring.pages-resources.learning_assistant.open-ai.link',
defaultMessage: 'Learn more about how OpenAI handles data',
},
learningAssistantOpenAIDataPrivacyLink: {
id: 'course-authoring.pages-resources.learning_assistant.open-ai.data-privacy.link',
defaultMessage: 'Learn more about OpenAI API data privacy',
},
});
export default messages;

View File

@@ -0,0 +1,19 @@
{
"name": "@openedx-plugins/course-app-learning_assistant",
"version": "0.1.0",
"description": "Learning Assistant configuration for courses using it",
"peerDependencies": {
"@edx/frontend-app-authoring": "*",
"@edx/frontend-platform": "*",
"@openedx/paragon": "*",
"prop-types": "*",
"react": "*",
"yup": "*"
},
"peerDependenciesMeta": {
"@edx/frontend-app-authoring": {
"optional": true
}
}
}

View File

@@ -1,25 +1,26 @@
import React, { useEffect, useState } from 'react';
import { getConfig } from '@edx/frontend-platform';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Form, Hyperlink } from '@edx/paragon';
import { Form, Hyperlink } from '@openedx/paragon';
import PropTypes from 'prop-types';
import messages from './messages';
import { providerNames, bbbPlanTypes } from './constants';
import AppConfigFormDivider from '../discussions/app-config-form/apps/shared/AppConfigFormDivider';
import LiveCommonFields from './LiveCommonFields';
import { useModel } from '../../generic/model-store';
import AppConfigFormDivider from 'CourseAuthoring/pages-and-resources/discussions/app-config-form/apps/shared/AppConfigFormDivider';
import { useModel } from 'CourseAuthoring/generic/model-store';
function BbbSettings({
import { providerNames, bbbPlanTypes } from './constants';
import LiveCommonFields from './LiveCommonFields';
import messages from './messages';
const BbbSettings = ({
intl,
values,
setFieldValue,
}) {
}) => {
const [bbbPlan, setBbbPlan] = useState(values.tierType);
useEffect(() => {
setBbbPlan(values.tierType);
}, [values.tierType]);
const appInfo = useModel('courseApps', 'live');
const app = useModel('liveApps', 'big_blue_button');
const isPiiDisabled = !values.piiSharingEnable;
function getBbbPlanOptions() {
@@ -71,7 +72,7 @@ function BbbSettings({
</Form.Group>
<Hyperlink
destination={appInfo.documentationLinks.learnMoreConfiguration}
destination={getConfig().BBB_LEARN_MORE_URL}
target="_blank"
rel="noopener noreferrer"
showLaunchIcon
@@ -88,11 +89,19 @@ function BbbSettings({
) : (
<>
{bbbPlan === bbbPlanTypes.commercial && <LiveCommonFields values={values} />}
{bbbPlan === bbbPlanTypes.free
&& (
<p data-testid="free-plan-message">
{bbbPlan === bbbPlanTypes.free && (
<span data-testid="free-plan-message">
{intl.formatMessage(messages.freePlanMessage)}
</p>
<Hyperlink
destination="https://bigbluebutton.org/privacy-policy/"
target="_blank"
rel="noopener noreferrer"
showLaunchIcon
className="text-gray-700 ml-1"
>
{intl.formatMessage(messages.privacyPolicy)}
</Hyperlink>
</span>
)}
</>
)}
@@ -100,7 +109,7 @@ function BbbSettings({
</>
);
}
};
BbbSettings.propTypes = {
intl: intlShape.isRequired,

View File

@@ -6,16 +6,19 @@ import {
waitForElementToBeRemoved,
} from '@testing-library/react';
import { Switch } from 'react-router-dom';
import { initializeMockApp, history } from '@edx/frontend-platform';
import ReactDOM from 'react-dom';
import { Routes, Route, MemoryRouter } from 'react-router-dom';
import { initializeMockApp } from '@edx/frontend-platform';
import MockAdapter from 'axios-mock-adapter';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { AppProvider, PageRoute } from '@edx/frontend-platform/react';
import { AppProvider, PageWrap } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import userEvent from '@testing-library/user-event';
import initializeStore from '../../store';
import { executeThunk } from '../../utils';
import initializeStore from 'CourseAuthoring/store';
import { executeThunk } from 'CourseAuthoring/utils';
import PagesAndResourcesProvider from 'CourseAuthoring/pages-and-resources/PagesAndResourcesProvider';
import LiveSettings from './Settings';
import {
generateLiveConfigurationApiResponse,
@@ -23,27 +26,28 @@ import {
initialState,
configurationProviders,
} from './factories/mockApiResponses';
import { fetchLiveConfiguration, fetchLiveProviders } from './data/thunks';
import { providerConfigurationApiUrl, providersApiUrl } from './data/api';
import messages from './messages';
import PagesAndResourcesProvider from '../PagesAndResourcesProvider';
let axiosMock;
let container;
let store;
const liveSettingsUrl = `/course/${courseId}/pages-and-resources/live/settings`;
// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest.
ReactDOM.createPortal = jest.fn(node => node);
const renderComponent = () => {
const wrapper = render(
<IntlProvider locale="en">
<AppProvider store={store}>
<AppProvider store={store} wrapWithRouter={false}>
<PagesAndResourcesProvider courseId={courseId}>
<Switch>
<PageRoute path={liveSettingsUrl}>
<LiveSettings onClose={() => {}} />
</PageRoute>
</Switch>
<MemoryRouter initialEntries={[liveSettingsUrl]}>
<Routes>
<Route path={liveSettingsUrl} element={<PageWrap><LiveSettings onClose={() => {}} /></PageWrap>} />
</Routes>
</MemoryRouter>
</PagesAndResourcesProvider>
</AppProvider>
</IntlProvider>,
@@ -52,11 +56,11 @@ const renderComponent = () => {
};
const mockStore = async ({
usernameSharing = false,
emailSharing = false,
enabled = true,
piiSharingAllowed = true,
isFreeTier = false,
usernameSharing = false,
emailSharing = false,
enabled = true,
piiSharingAllowed = true,
isFreeTier = false,
}) => {
const fetchProviderConfigUrl = `${providersApiUrl}/${courseId}/`;
const fetchLiveConfigUrl = `${providerConfigurationApiUrl}/${courseId}/`;
@@ -80,7 +84,6 @@ describe('BBB Settings', () => {
});
store = initializeStore(initialState);
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
history.push(liveSettingsUrl);
});
test('Plan dropdown to be visible and enabled in UI', async () => {
@@ -103,7 +106,8 @@ describe('BBB Settings', () => {
expect(getAllByRole(dropDown, 'option').length).toBe(noOfOptions);
});
test('Connect to support and PII sharing message is visible and plans selection is disabled, When pii sharing is disabled, ',
test(
'Connect to support and PII sharing message is visible and plans selection is disabled, When pii sharing is disabled, ',
async () => {
await mockStore({ piiSharingAllowed: false });
renderComponent();
@@ -116,7 +120,8 @@ describe('BBB Settings', () => {
);
expect(helpRequestPiiText).toHaveTextContent(messages.piiSharingEnableHelpTextBbb.defaultMessage);
expect(container.querySelector('select[name="tierType"]')).toBeDisabled();
});
},
);
test('free plans message is visible when free plan is selected', async () => {
await mockStore({ emailSharing: true, isFreeTier: true });
@@ -126,7 +131,7 @@ describe('BBB Settings', () => {
const dropDown = container.querySelector('select[name="tierType"]');
userEvent.selectOptions(
dropDown,
getByRole(dropDown, 'option', { name: 'Free' }),
getByRole(dropDown, 'option', { name: 'Free' }),
);
expect(queryByTestId(container, 'free-plan-message')).toBeInTheDocument();
expect(queryByTestId(container, 'free-plan-message')).toHaveTextContent(messages.freePlanMessage.defaultMessage);

View File

@@ -0,0 +1,48 @@
import React from 'react';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import PropTypes from 'prop-types';
import FormikControl from 'CourseAuthoring/generic/FormikControl';
import messages from './messages';
const LiveCommonFields = ({
intl,
values,
}) => (
<>
<p className="pb-2">{intl.formatMessage(messages.formInstructions)}</p>
<FormikControl
name="consumerKey"
value={values.consumerKey}
floatingLabel={intl.formatMessage(messages.consumerKey)}
className="pb-1"
type="input"
/>
<FormikControl
name="consumerSecret"
value={values.consumerSecret}
floatingLabel={intl.formatMessage(messages.consumerSecret)}
className="pb-1"
type="password"
/>
<FormikControl
name="launchUrl"
value={values.launchUrl}
floatingLabel={intl.formatMessage(messages.launchUrl)}
className="pb-1"
type="input"
/>
</>
);
LiveCommonFields.propTypes = {
intl: intlShape.isRequired,
values: PropTypes.shape({
consumerKey: PropTypes.string,
consumerSecret: PropTypes.string,
launchUrl: PropTypes.string,
launchEmail: PropTypes.string,
}).isRequired,
};
export default injectIntl(LiveCommonFields);

View File

@@ -1,25 +1,29 @@
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { camelCase } from 'lodash';
import { SelectableBox, Icon } from '@edx/paragon';
import { Icon } from '@openedx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import PropTypes from 'prop-types';
import * as Yup from 'yup';
import { useNavigate } from 'react-router-dom';
import SelectableBox from 'CourseAuthoring/editors/sharedComponents/SelectableBox';
import AppSettingsModal from 'CourseAuthoring/pages-and-resources/app-settings-modal/AppSettingsModal';
import { useModel } from 'CourseAuthoring/generic/model-store';
import Loading from 'CourseAuthoring/generic/Loading';
import { RequestStatus } from 'CourseAuthoring/data/constants';
import { fetchLiveData, saveLiveConfiguration, saveLiveConfigurationAsDraft } from './data/thunks';
import { selectApp } from './data/slice';
import AppSettingsModal from '../app-settings-modal/AppSettingsModal';
import { useModel } from '../../generic/model-store';
import Loading from '../../generic/Loading';
import { iconsSrc, bbbPlanTypes } from './constants';
import { RequestStatus } from '../../data/constants';
import messages from './messages';
import ZoomSettings from './ZoomSettings';
import BBBSettings from './BBBSettings';
function LiveSettings({
const LiveSettings = ({
intl,
onClose,
}) {
}) => {
const navigate = useNavigate();
const dispatch = useDispatch();
const courseId = useSelector(state => state.courseDetail.courseId);
const availableProviders = useSelector((state) => state.live.appIds);
@@ -57,10 +61,7 @@ function LiveSettings({
is: (provider, tier) => provider === 'zoom' || (provider === 'big_blue_button' && tier === bbbPlanTypes.commercial),
then: Yup.string().required(intl.formatMessage(messages.launchUrlRequired)),
}),
launchEmail: Yup.string().when('provider', {
is: 'zoom',
then: Yup.string().required(intl.formatMessage(messages.launchEmailRequired)),
}),
launchEmail: Yup.string(),
};
const handleProviderChange = (providerId, setFieldValue, values) => {
@@ -70,7 +71,7 @@ function LiveSettings({
};
const handleSettingsSave = async (values) => {
await dispatch(saveLiveConfiguration(courseId, values));
await dispatch(saveLiveConfiguration(courseId, values, navigate));
};
useEffect(() => {
@@ -78,59 +79,55 @@ function LiveSettings({
}, [courseId]);
return (
<>
<AppSettingsModal
appId="live"
title={intl.formatMessage(messages.heading)}
enableAppHelp={intl.formatMessage(messages.enableLiveHelp)}
enableAppLabel={intl.formatMessage(messages.enableLiveLabel)}
learnMoreText={intl.formatMessage(messages.enableLiveLink)}
onClose={onClose}
initialValues={liveConfiguration}
validationSchema={validationSchema}
onSettingsSave={handleSettingsSave}
configureBeforeEnable
enableReinitialize
>
{({ values, setFieldValue }) => (
<AppSettingsModal
appId="live"
title={intl.formatMessage(messages.heading)}
enableAppHelp={intl.formatMessage(messages.enableLiveHelp)}
enableAppLabel={intl.formatMessage(messages.enableLiveLabel)}
learnMoreText={intl.formatMessage(messages.enableLiveLink)}
onClose={onClose}
initialValues={liveConfiguration}
validationSchema={validationSchema}
onSettingsSave={handleSettingsSave}
configureBeforeEnable
enableReinitialize
>
{({ values, setFieldValue }) => (
(status === RequestStatus.IN_PROGRESS) ? (
<Loading />
) : (
<>
{(status === RequestStatus.IN_PROGRESS) ? (
<Loading />
) : (
<>
<h4 className="my-3">{intl.formatMessage(messages.selectProvider)}</h4>
<SelectableBox.Set
type="checkbox"
value={values.provider}
onChange={(event) => handleProviderChange(event.target.value, setFieldValue, values)}
name="provider"
columns={3}
className="mb-3"
>
{availableProviders.map((provider) => (
<SelectableBox value={provider} type="checkbox" key={provider}>
<div className="d-flex flex-column align-items-center">
<Icon src={iconsSrc[`${camelCase(provider)}`]} alt={provider} />
<span>{intl.formatMessage(messages[`appName-${camelCase(provider)}`])}</span>
</div>
</SelectableBox>
))}
</SelectableBox.Set>
{values.provider === 'zoom' ? <ZoomSettings values={values} />
: (
<BBBSettings
values={values}
setFieldValue={setFieldValue}
/>
)}
</>
<h4 className="my-3">{intl.formatMessage(messages.selectProvider)}</h4>
<SelectableBox.Set
type="checkbox"
value={values.provider}
onChange={(event) => handleProviderChange(event.target.value, setFieldValue, values)}
name="provider"
columns={3}
className="mb-3"
>
{availableProviders.map((provider) => (
<SelectableBox value={provider} type="checkbox" key={provider}>
<div className="d-flex flex-column align-items-center">
<Icon src={iconsSrc[`${camelCase(provider)}`]} alt={provider} />
<span>{intl.formatMessage(messages[`appName-${camelCase(provider)}`])}</span>
</div>
</SelectableBox>
))}
</SelectableBox.Set>
{values.provider === 'zoom' ? <ZoomSettings values={values} />
: (
<BBBSettings
values={values}
setFieldValue={setFieldValue}
/>
)}
</>
)}
</AppSettingsModal>
</>
)
)}
</AppSettingsModal>
);
}
};
LiveSettings.propTypes = {
intl: intlShape.isRequired,

View File

@@ -10,15 +10,18 @@ import {
waitForElementToBeRemoved,
} from '@testing-library/react';
import { Switch } from 'react-router-dom';
import { initializeMockApp, history } from '@edx/frontend-platform';
import ReactDOM from 'react-dom';
import { Routes, Route, MemoryRouter } from 'react-router-dom';
import { initializeMockApp } from '@edx/frontend-platform';
import MockAdapter from 'axios-mock-adapter';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { AppProvider, PageRoute } from '@edx/frontend-platform/react';
import { AppProvider, PageWrap } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import initializeStore from '../../store';
import { executeThunk } from '../../utils';
import initializeStore from 'CourseAuthoring/store';
import { executeThunk } from 'CourseAuthoring/utils';
import PagesAndResourcesProvider from 'CourseAuthoring/pages-and-resources/PagesAndResourcesProvider';
import LiveSettings from './Settings';
import {
generateLiveConfigurationApiResponse,
@@ -30,23 +33,25 @@ import {
import { fetchLiveConfiguration, fetchLiveProviders } from './data/thunks';
import { providerConfigurationApiUrl, providersApiUrl } from './data/api';
import messages from './messages';
import PagesAndResourcesProvider from '../PagesAndResourcesProvider';
let axiosMock;
let container;
let store;
const liveSettingsUrl = `/course/${courseId}/pages-and-resources/live/settings`;
// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest.
ReactDOM.createPortal = jest.fn(node => node);
const renderComponent = () => {
const wrapper = render(
<IntlProvider locale="en">
<AppProvider store={store}>
<AppProvider store={store} wrapWithRouter={false}>
<PagesAndResourcesProvider courseId={courseId}>
<Switch>
<PageRoute path={liveSettingsUrl}>
<LiveSettings onClose={() => {}} />
</PageRoute>
</Switch>
<MemoryRouter initialEntries={[liveSettingsUrl]}>
<Routes>
<Route path={liveSettingsUrl} element={<PageWrap><LiveSettings onClose={() => {}} /></PageWrap>} />
</Routes>
</MemoryRouter>
</PagesAndResourcesProvider>
</AppProvider>
</IntlProvider>,
@@ -55,10 +60,10 @@ const renderComponent = () => {
};
const mockStore = async ({
usernameSharing = false,
emailSharing = false,
enabled = true,
piiSharingAllowed = true,
usernameSharing = false,
emailSharing = false,
enabled = true,
piiSharingAllowed = true,
}) => {
const fetchProviderConfigUrl = `${providersApiUrl}/${courseId}/`;
const fetchLiveConfigUrl = `${providerConfigurationApiUrl}/${courseId}/`;
@@ -82,7 +87,6 @@ describe('LiveSettings', () => {
});
store = initializeStore(initialState);
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
history.push(liveSettingsUrl);
});
test('Live Configuration modal is visible', async () => {

View File

@@ -1,43 +1,43 @@
import React from 'react';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import PropTypes from 'prop-types';
import FormikControl from 'CourseAuthoring/generic/FormikControl';
import messages from './messages';
import { providerNames } from './constants';
import LiveCommonFields from './LiveCommonFields';
import FormikControl from '../../generic/FormikControl';
function ZoomsSettings({
intl,
values,
}) {
return (
<>
{(!values.piiSharingEnable && (values.piiSharingEmail || values.piiSharingUsername)) ? (
<p data-testid="request-pii-sharing">
{intl.formatMessage(messages.requestPiiSharingEnable, { provider: providerNames[values.provider] })}
</p>
) : (
<>
{(values.piiSharingEmail || values.piiSharingUsername)
const ZoomSettings = ({
intl,
values,
}) => (
// eslint-disable-next-line react/jsx-no-useless-fragment
<>
{!values.piiSharingEnable ? (
<p data-testid="request-pii-sharing">
{intl.formatMessage(messages.requestPiiSharingEnable, { provider: providerNames[values.provider] })}
</p>
) : (
<>
{(values.piiSharingEmail || values.piiSharingUsername)
&& (
<p data-testid="helper-text">
{intl.formatMessage(messages.providerHelperText, { providerName: providerNames[values.provider] })}
</p>
)}
<LiveCommonFields values={values} />
<FormikControl
name="launchEmail"
value={values.launchEmail}
floatingLabel={intl.formatMessage(messages.launchEmail)}
type="input"
/>
</>
)}
</>
);
}
<LiveCommonFields values={values} />
<FormikControl
name="launchEmail"
value={values.launchEmail}
floatingLabel={intl.formatMessage(messages.launchEmail)}
type="input"
/>
</>
)}
</>
);
ZoomsSettings.propTypes = {
ZoomSettings.propTypes = {
intl: intlShape.isRequired,
values: PropTypes.shape({
consumerKey: PropTypes.string,
@@ -51,4 +51,4 @@ ZoomsSettings.propTypes = {
}).isRequired,
};
export default injectIntl(ZoomsSettings);
export default injectIntl(ZoomSettings);

View File

@@ -5,15 +5,17 @@ import {
waitForElementToBeRemoved,
} from '@testing-library/react';
import { Switch } from 'react-router-dom';
import { initializeMockApp, history } from '@edx/frontend-platform';
import ReactDOM from 'react-dom';
import { Routes, Route, MemoryRouter } from 'react-router-dom';
import { initializeMockApp } from '@edx/frontend-platform';
import MockAdapter from 'axios-mock-adapter';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { AppProvider, PageRoute } from '@edx/frontend-platform/react';
import { AppProvider, PageWrap } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import initializeStore from '../../store';
import { executeThunk } from '../../utils';
import initializeStore from 'CourseAuthoring/store';
import { executeThunk } from 'CourseAuthoring/utils';
import PagesAndResourcesProvider from 'CourseAuthoring/pages-and-resources/PagesAndResourcesProvider';
import LiveSettings from './Settings';
import {
generateLiveConfigurationApiResponse,
@@ -25,23 +27,25 @@ import {
import { fetchLiveConfiguration, fetchLiveProviders } from './data/thunks';
import { providerConfigurationApiUrl, providersApiUrl } from './data/api';
import messages from './messages';
import PagesAndResourcesProvider from '../PagesAndResourcesProvider';
let axiosMock;
let container;
let store;
const liveSettingsUrl = `/course/${courseId}/pages-and-resources/live/settings`;
// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest.
ReactDOM.createPortal = jest.fn(node => node);
const renderComponent = () => {
const wrapper = render(
<IntlProvider locale="en">
<AppProvider store={store}>
<AppProvider store={store} wrapWithRouter={false}>
<PagesAndResourcesProvider courseId={courseId}>
<Switch>
<PageRoute path={liveSettingsUrl}>
<LiveSettings onClose={() => {}} />
</PageRoute>
</Switch>
<MemoryRouter initialEntries={[liveSettingsUrl]}>
<Routes>
<Route path={liveSettingsUrl} element={<PageWrap><LiveSettings onClose={() => {}} /></PageWrap>} />
</Routes>
</MemoryRouter>
</PagesAndResourcesProvider>
</AppProvider>
</IntlProvider>,
@@ -50,10 +54,10 @@ const renderComponent = () => {
};
const mockStore = async ({
usernameSharing = false,
emailSharing = false,
enabled = true,
piiSharingAllowed = true,
usernameSharing = false,
emailSharing = false,
enabled = true,
piiSharingAllowed = true,
}) => {
const fetchProviderConfigUrl = `${providersApiUrl}/${courseId}/`;
const fetchLiveConfigUrl = `${providerConfigurationApiUrl}/${courseId}/`;
@@ -77,11 +81,10 @@ describe('Zoom Settings', () => {
});
store = initializeStore(initialState);
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
history.push(liveSettingsUrl);
});
test('LTI fields are visible when pii sharing is enabled and email or username sharing required', async () => {
await mockStore({ emailSharing: true });
test('LTI fields are visible when pii sharing is enabled', async () => {
await mockStore({ piiSharingAllowed: true });
renderComponent();
const spinner = getByRole(container, 'status');
@@ -103,9 +106,9 @@ describe('Zoom Settings', () => {
});
test(
'Only connect to support message is visible when pii sharing is disabled and email or username sharing is required',
'Only connect to support message is visible when pii sharing is disabled',
async () => {
await mockStore({ emailSharing: true, piiSharingAllowed: false });
await mockStore({ piiSharingAllowed: false });
renderComponent();
const spinner = getByRole(container, 'status');
@@ -129,7 +132,7 @@ describe('Zoom Settings', () => {
test('Provider Configuration should be displayed correctly', async () => {
const apiDefaultResponse = generateLiveConfigurationApiResponse(true, true);
await mockStore({ emailSharing: false, piiSharingAllowed: false });
await mockStore({ piiSharingAllowed: true });
renderComponent();
const spinner = getByRole(container, 'status');

View File

@@ -1,6 +1,6 @@
import {
GoogleMeet, MicrosoftTeams, Zoom, Bbb,
} from '@edx/paragon/icons';
GoogleMeet, MicrosoftTeams, Zoom, Bbb,
} from '@openedx/paragon/icons';
export const iconsSrc = {
googleMeet: GoogleMeet,

View File

@@ -1,6 +1,6 @@
/* eslint-disable no-param-reassign */
import { createSlice } from '@reduxjs/toolkit';
import { RequestStatus } from '../../../data/constants';
import { RequestStatus } from 'CourseAuthoring/data/constants';
const slice = createSlice({
name: 'live',

View File

@@ -1,14 +1,14 @@
import { history } from '@edx/frontend-platform';
import { addModel, addModels, updateModel } from '../../../generic/model-store';
import { addModel, addModels, updateModel } from 'CourseAuthoring/generic/model-store';
import { RequestStatus } from 'CourseAuthoring/data/constants';
import {
getLiveConfiguration,
getLiveProviders,
postLiveConfiguration,
normalizeSettings,
deNormalizeSettings,
getLiveConfiguration,
getLiveProviders,
postLiveConfiguration,
normalizeSettings,
deNormalizeSettings,
} from './api';
import { loadApps, updateStatus, updateSaveStatus } from './slice';
import { RequestStatus } from '../../../data/constants';
function updateLiveSettingsState({
appConfig,
@@ -56,7 +56,7 @@ export function fetchLiveData(courseId) {
};
}
export function saveLiveConfiguration(courseId, config) {
export function saveLiveConfiguration(courseId, config, navigate) {
return async (dispatch) => {
dispatch(updateSaveStatus({ status: RequestStatus.IN_PROGRESS }));
try {
@@ -64,7 +64,7 @@ export function saveLiveConfiguration(courseId, config) {
dispatch(updateLiveSettingsState(apps));
dispatch(updateSaveStatus({ status: RequestStatus.SUCCESSFUL }));
history.push(`/course/${courseId}/pages-and-resources/`);
navigate(`/course/${courseId}/pages-and-resources/`);
} catch (error) {
if (error.response && error.response.status === 403) {
dispatch(updateSaveStatus({ status: RequestStatus.DENIED }));

View File

@@ -36,11 +36,11 @@ export const initialState = {
export const configurationProviders = (
emailSharing,
usernameSharing,
activeProvider = 'zoom',
activeProvider,
hasFreeTier,
) => ({
providers: {
active: activeProvider,
active: activeProvider || 'zoom',
available: {
zoom: {
features: [],
@@ -65,7 +65,7 @@ export const generateLiveConfigurationApiResponse = (
enabled,
piiSharingAllowed,
providerType = 'zoom',
isFreeTier,
isFreeTier = undefined,
) => ({
course_key: courseId,
enabled,

View File

@@ -160,9 +160,14 @@ const messages = defineMessages({
freePlanMessage: {
id: 'authoring.live.freePlanMessage',
defaultMessage: 'The free plan is pre-configured, and no additional configurations are required.',
defaultMessage: 'The free plan is pre-configured, and no additional configurations are required. By selecting the free plan, you are agreeing to Blindside Networks',
description: 'Tells user that free plans requires no additional configurations',
},
privacyPolicy: {
id: 'authoring.live.privacyPolicy',
defaultMessage: 'Privacy Policy.',
description: 'The text of privacy policy hyperlink for free plan',
},
});
export default messages;

View File

@@ -0,0 +1,22 @@
{
"name": "@openedx-plugins/course-app-live",
"version": "0.1.0",
"description": "Live course configuration for courses using it",
"peerDependencies": {
"@edx/frontend-app-authoring": "*",
"@edx/frontend-platform": "*",
"@openedx/paragon": "*",
"@reduxjs/toolkit": "*",
"lodash": "*",
"prop-types": "*",
"react": "*",
"react-redux": "*",
"react-router-dom": "*",
"yup": "*"
},
"peerDependenciesMeta": {
"@edx/frontend-app-authoring": {
"optional": true
}
}
}

View File

@@ -0,0 +1,176 @@
import { useEffect, useState, useRef } from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { useDispatch, useSelector } from 'react-redux';
import {
ActionRow, Alert, Badge, Form, Hyperlink, ModalDialog, StatefulButton,
} from '@openedx/paragon';
import { Info } from '@openedx/paragon/icons';
import { updateModel, useModel } from 'CourseAuthoring/generic/model-store';
import { RequestStatus } from 'CourseAuthoring/data/constants';
import FormSwitchGroup from 'CourseAuthoring/generic/FormSwitchGroup';
import Loading from 'CourseAuthoring/generic/Loading';
import PermissionDeniedAlert from 'CourseAuthoring/generic/PermissionDeniedAlert';
import ConnectionErrorAlert from 'CourseAuthoring/generic/ConnectionErrorAlert';
import { useAppSetting, useIsMobile } from 'CourseAuthoring/utils';
import { getLoadingStatus, getSavingStatus } from 'CourseAuthoring/pages-and-resources/data/selectors';
import { updateSavingStatus } from 'CourseAuthoring/pages-and-resources/data/slice';
import messages from './messages';
const ORASettings = ({ onClose }) => {
const dispatch = useDispatch();
const { formatMessage } = useIntl();
const alertRef = useRef(null);
const updateSettingsRequestStatus = useSelector(getSavingStatus);
const loadingStatus = useSelector(getLoadingStatus);
const isMobile = useIsMobile();
const modalVariant = isMobile ? 'dark' : 'default';
const appId = 'ora_settings';
const appInfo = useModel('courseApps', appId);
const [enableFlexiblePeerGrade, saveSetting] = useAppSetting(
'forceOnFlexiblePeerOpenassessments',
);
const initialFormValues = { enableFlexiblePeerGrade };
const [formValues, setFormValues] = useState(initialFormValues);
const [saveError, setSaveError] = useState(false);
const submitButtonState = updateSettingsRequestStatus === RequestStatus.IN_PROGRESS ? 'pending' : 'default';
const handleSettingsSave = (values) => saveSetting(values.enableFlexiblePeerGrade);
const handleSubmit = async (event) => {
let success = true;
event.preventDefault();
success = success && await handleSettingsSave(formValues);
await setSaveError(!success);
if ((initialFormValues.enableFlexiblePeerGrade !== formValues.enableFlexiblePeerGrade) && success) {
success = await dispatch(updateModel({
modelType: 'courseApps',
model: {
id: appId, enabled: formValues.enableFlexiblePeerGrade,
},
}));
}
!success && alertRef?.current.scrollIntoView(); // eslint-disable-line @typescript-eslint/no-unused-expressions
};
const handleChange = (e) => {
setFormValues({ enableFlexiblePeerGrade: e.target.checked });
};
useEffect(() => {
if (updateSettingsRequestStatus === RequestStatus.SUCCESSFUL) {
dispatch(updateSavingStatus({ status: '' }));
onClose();
}
}, [updateSettingsRequestStatus]);
const renderBody = () => {
switch (loadingStatus) {
case RequestStatus.SUCCESSFUL:
return (
<>
{saveError && (
<Alert variant="danger" icon={Info} ref={alertRef}>
<Alert.Heading>
{formatMessage(messages.errorSavingTitle)}
</Alert.Heading>
{formatMessage(messages.errorSavingMessage)}
</Alert>
)}
<FormSwitchGroup
id="enable-flexible-peer-grade"
name="enableFlexiblePeerGrade"
label={(
<div className="d-flex align-items-center">
{formatMessage(messages.enableFlexPeerGradeLabel)}
{formValues.enableFlexiblePeerGrade && (
<Badge className="ml-2" variant="success" data-testid="enable-badge">
{formatMessage(messages.enabledBadgeLabel)}
</Badge>
)}
</div>
)}
helpText={(
<div>
<p>{formatMessage(messages.enableFlexPeerGradeHelp)}</p>
<span className="py-3">
<Hyperlink
className="text-primary-500 small"
destination={appInfo.documentationLinks?.learnMoreConfiguration}
target="_blank"
rel="noreferrer noopener"
>
{formatMessage(messages.ORASettingsHelpLink)}
</Hyperlink>
</span>
</div>
)}
onChange={handleChange}
checked={formValues.enableFlexiblePeerGrade}
/>
</>
);
case RequestStatus.DENIED:
return <PermissionDeniedAlert />;
case RequestStatus.FAILED:
return <ConnectionErrorAlert />;
default:
return <Loading />;
}
};
return (
<ModalDialog
title={formatMessage(messages.heading)}
isOpen
onClose={onClose}
size="lg"
variant={modalVariant}
hasCloseButton={isMobile}
isFullscreenScroll
isFullscreenOnMobile
>
<Form onSubmit={handleSubmit} data-testid="proctoringForm">
<ModalDialog.Header>
<ModalDialog.Title>
{formatMessage(messages.heading)}
</ModalDialog.Title>
</ModalDialog.Header>
<ModalDialog.Body>
{renderBody()}
</ModalDialog.Body>
<ModalDialog.Footer className="p-4">
<ActionRow>
<ModalDialog.CloseButton variant="tertiary">
{formatMessage(messages.cancelLabel)}
</ModalDialog.CloseButton>
<StatefulButton
labels={{
default: formatMessage(messages.saveLabel),
pending: formatMessage(messages.pendingSaveLabel),
}}
description="Form save button"
data-testid="submissionButton"
disabled={submitButtonState === RequestStatus.IN_PROGRESS}
state={submitButtonState}
type="submit"
/>
</ActionRow>
</ModalDialog.Footer>
</Form>
</ModalDialog>
);
};
ORASettings.propTypes = {
onClose: PropTypes.func.isRequired,
};
export default ORASettings;

View File

@@ -0,0 +1,152 @@
import {
render,
screen,
waitFor,
within,
} from '@testing-library/react';
import ReactDOM from 'react-dom';
import { Routes, Route, MemoryRouter } from 'react-router-dom';
import { initializeMockApp } from '@edx/frontend-platform';
import MockAdapter from 'axios-mock-adapter';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { AppProvider, PageWrap } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import initializeStore from 'CourseAuthoring/store';
import { executeThunk } from 'CourseAuthoring/utils';
import PagesAndResourcesProvider from 'CourseAuthoring/pages-and-resources/PagesAndResourcesProvider';
import { getCourseAppsApiUrl, getCourseAdvancedSettingsApiUrl } from 'CourseAuthoring/pages-and-resources/data/api';
import { fetchCourseApps, fetchCourseAppSettings } from 'CourseAuthoring/pages-and-resources/data/thunks';
import ORASettings from './Settings';
import messages from './messages';
import {
courseId,
inititalState,
} from './factories/mockData';
let axiosMock;
let store;
const oraSettingsUrl = `/course/${courseId}/pages-and-resources/live/settings`;
// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest.
ReactDOM.createPortal = jest.fn(node => node);
const renderComponent = () => (
render(
<IntlProvider locale="en">
<AppProvider store={store} wrapWithRouter={false}>
<PagesAndResourcesProvider courseId={courseId}>
<MemoryRouter initialEntries={[oraSettingsUrl]}>
<Routes>
<Route path={oraSettingsUrl} element={<PageWrap><ORASettings onClose={jest.fn()} /></PageWrap>} />
</Routes>
</MemoryRouter>
</PagesAndResourcesProvider>
</AppProvider>
</IntlProvider>,
)
);
const mockStore = async ({
apiStatus,
enabled,
}) => {
const settings = ['forceOnFlexiblePeerOpenassessments'];
const fetchCourseAppsUrl = `${getCourseAppsApiUrl()}/${courseId}`;
const fetchAdvancedSettingsUrl = `${getCourseAdvancedSettingsApiUrl()}/${courseId}`;
axiosMock.onGet(fetchCourseAppsUrl).reply(
200,
[{
allowed_operations: { enable: false, configure: true },
description: 'setting',
documentation_links: { learnMoreConfiguration: '' },
enabled,
id: 'ora_settings',
name: 'Flexible Peer Grading for ORAs',
}],
);
axiosMock.onGet(fetchAdvancedSettingsUrl).reply(
apiStatus,
{ force_on_flexible_peer_openassessments: { value: enabled } },
);
await executeThunk(fetchCourseApps(courseId), store.dispatch);
await executeThunk(fetchCourseAppSettings(courseId, settings), store.dispatch);
};
describe('ORASettings', () => {
beforeEach(async () => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: false,
roles: [],
},
});
store = initializeStore(inititalState);
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
});
it('Flexible peer grading configuration modal is visible', async () => {
renderComponent();
expect(screen.getByRole('dialog')).toBeVisible();
});
it('Displays "Configure Flexible Peer Grading" heading', async () => {
renderComponent();
const headingElement = screen.getByText(messages.heading.defaultMessage);
expect(headingElement).toBeVisible();
});
it('Displays loading component', () => {
renderComponent();
const loadingElement = screen.getByRole('status');
expect(within(loadingElement).getByText('Loading...')).toBeInTheDocument();
});
it('Displays Connection Error Alert', async () => {
await mockStore({ apiStatus: 404, enabled: true });
renderComponent();
const errorAlert = screen.getByRole('alert');
expect(within(errorAlert).getByText('We encountered a technical error when loading this page.', { exact: false })).toBeVisible();
});
it('Displays Permissions Error Alert', async () => {
await mockStore({ apiStatus: 403, enabled: true });
renderComponent();
const errorAlert = screen.getByRole('alert');
expect(within(errorAlert).getByText('You are not authorized to view this page', { exact: false })).toBeVisible();
});
it('Displays title, helper text and badge when flexible peer grading button is enabled', async () => {
renderComponent();
await mockStore({ apiStatus: 200, enabled: true });
waitFor(() => {
const label = screen.getByText(messages.enableFlexPeerGradeLabel.defaultMessage);
const enableBadge = screen.getByTestId('enable-badge');
expect(label).toBeVisible();
expect(enableBadge).toHaveTextContent('Enabled');
});
});
it('Displays title, helper text and hides badge when flexible peer grading button is disabled', async () => {
renderComponent();
await mockStore({ apiStatus: 200, enabled: false });
const label = screen.getByText(messages.enableFlexPeerGradeLabel.defaultMessage);
const enableBadge = screen.queryByTestId('enable-badge');
expect(label).toBeVisible();
expect(enableBadge).toBeNull();
});
});

View File

@@ -0,0 +1,32 @@
export const courseId = 'course-v1:org+num+run';
export const inititalState = {
courseDetail: {
courseId,
status: 'successful',
},
pagesAndResources: {
courseAppIds: ['ora_settings'],
loadingStatus: 'in-progress',
savingStatus: '',
courseAppsApiStatus: {},
courseAppSettings: {},
},
models: {
courseApps: {
ora_settings: {
id: 'ora_settings',
name: 'Flexible Peer Grading',
enabled: true,
description: 'Enable flexible peer grading',
allowedOperations: {
enable: false,
configure: true,
},
documentationLinks: {
learnMoreConfiguration: '',
},
},
},
},
};

View File

@@ -0,0 +1,54 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
heading: {
id: 'course-authoring.pages-resources.ora.heading',
defaultMessage: 'Configure Flexible Peer Grading',
description: 'Title for the modal dialog header',
},
ORASettingsHelpLink: {
id: 'course-authoring.pages-resources.ora.flex-peer-grading.link',
defaultMessage: 'Learn more about open response assessment settings',
description: 'Descriptive text for the hyperlink to the docs site',
},
enableFlexPeerGradeLabel: {
id: 'course-authoring.pages-resources.ora.flex-peer-grading.label',
defaultMessage: 'Flex Peer Grading',
description: 'Label for form switch',
},
enableFlexPeerGradeHelp: {
id: 'course-authoring.pages-resources.ora.flex-peer-grading.help',
defaultMessage: 'Turn on Flexible Peer Grading for all open response assessments in the course with peer grading.',
description: 'Help text describing what happens when the switch is enabled',
},
enabledBadgeLabel: {
id: 'course-authoring.pages-resources.ora.flex-peer-grading.enabled-badge.label',
defaultMessage: 'Enabled',
description: 'Label for badge that show users that a setting is enabled',
},
cancelLabel: {
id: 'course-authoring.pages-resources.ora.flex-peer-grading.cancel-button.label',
defaultMessage: 'Cancel',
description: 'Label for button that cancels user changes',
},
saveLabel: {
id: 'course-authoring.pages-resources.ora.flex-peer-grading.save-button.label',
defaultMessage: 'Save',
description: 'Label for button that saves user changes',
},
pendingSaveLabel: {
id: 'course-authoring.pages-resources.ora.flex-peer-grading.pending-save-button.label',
defaultMessage: 'Saving',
description: 'Label for button that has pending api save calls',
},
errorSavingTitle: {
id: 'course-authoring.pages-resources.ora.flex-peer-grading.save-error.title',
defaultMessage: 'We couldn\'t apply your changes.',
},
errorSavingMessage: {
id: 'course-authoring.pages-resources.ora.flex-peer-grading.save-error.message',
defaultMessage: 'Please check your entries and try again.',
},
});
export default messages;

View File

@@ -0,0 +1,20 @@
{
"name": "@openedx-plugins/course-app-ora_settings",
"version": "0.1.0",
"description": "Open Response Assessment configuration for courses using it",
"peerDependencies": {
"@edx/frontend-app-authoring": "*",
"@edx/frontend-platform": "*",
"@openedx/paragon": "*",
"prop-types": "*",
"react": "*",
"react-redux": "*",
"yup": "*"
},
"peerDependenciesMeta": {
"@edx/frontend-app-authoring": {
"optional": true
}
}
}

View File

@@ -11,23 +11,25 @@ import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import {
ActionRow, Alert, Badge, Form, Hyperlink, ModalDialog, StatefulButton,
} from '@edx/paragon';
} from '@openedx/paragon';
import ExamsApiService from 'CourseAuthoring/data/services/ExamsApiService';
import StudioApiService from 'CourseAuthoring/data/services/StudioApiService';
import Loading from 'CourseAuthoring/generic/Loading';
import ConnectionErrorAlert from 'CourseAuthoring/generic/ConnectionErrorAlert';
import FormSwitchGroup from 'CourseAuthoring/generic/FormSwitchGroup';
import { useModel } from 'CourseAuthoring/generic/model-store';
import PermissionDeniedAlert from 'CourseAuthoring/generic/PermissionDeniedAlert';
import { useIsMobile } from 'CourseAuthoring/utils';
import { PagesAndResourcesContext } from 'CourseAuthoring/pages-and-resources/PagesAndResourcesProvider';
import StudioApiService from '../../data/services/StudioApiService';
import Loading from '../../generic/Loading';
import ConnectionErrorAlert from '../../generic/ConnectionErrorAlert';
import FormSwitchGroup from '../../generic/FormSwitchGroup';
import { useModel } from '../../generic/model-store';
import PermissionDeniedAlert from '../../generic/PermissionDeniedAlert';
import { useIsMobile } from '../../utils';
import { PagesAndResourcesContext } from '../PagesAndResourcesProvider';
import messages from './messages';
function ProctoringSettings({ intl, onClose }) {
const ProctoringSettings = ({ intl, onClose }) => {
const initialFormValues = {
enableProctoredExams: false,
proctoringProvider: false,
proctortrackEscalationEmail: '',
escalationEmail: '',
allowOptingOut: false,
createZendeskTickets: false,
};
@@ -36,12 +38,14 @@ function ProctoringSettings({ intl, onClose }) {
const [loaded, setLoaded] = useState(false);
const [loadingConnectionError, setLoadingConnectionError] = useState(false);
const [loadingPermissionError, setLoadingPermissionError] = useState(false);
const [allowLtiProviders, setAllowLtiProviders] = useState(false);
const [availableProctoringProviders, setAvailableProctoringProviders] = useState([]);
const [ltiProctoringProviders, setLtiProctoringProviders] = useState([]);
const [courseStartDate, setCourseStartDate] = useState('');
const [saveSuccess, setSaveSuccess] = useState(false);
const [saveError, setSaveError] = useState(false);
const [submissionInProgress, setSubmissionInProgress] = useState(false);
const [showProctortrackEscalationEmail, setShowProctortrackEscalationEmail] = useState(false);
const [showEscalationEmail, setShowEscalationEmail] = useState(false);
const isEdxStaff = getAuthenticatedUser().administrator;
const [formStatus, setFormStatus] = useState({
isValid: true,
@@ -50,14 +54,25 @@ function ProctoringSettings({ intl, onClose }) {
const isMobile = useIsMobile();
const modalVariant = isMobile ? 'dark' : 'default';
const isLtiProvider = (provider) => (
ltiProctoringProviders.some(p => p.name === provider)
);
function getProviderDisplayLabel(provider) {
// if a display label exists for this provider return it
return ltiProctoringProviders.find(p => p.name === provider)?.verbose_name || provider;
}
const { courseId } = useContext(PagesAndResourcesContext);
const courseDetails = useModel('courseDetails', courseId);
const org = courseDetails?.org;
const appInfo = useModel('courseApps', 'proctoring');
const alertRef = React.createRef();
const saveStatusAlertRef = React.createRef();
const proctoringEscalationEmailInputRef = useRef(null);
const submitButtonState = submissionInProgress ? 'pending' : 'default';
function handleChange(event) {
const handleChange = (event) => {
const { target } = event;
const value = target.type === 'checkbox' ? target.checked : target.value;
const { name } = target;
@@ -70,70 +85,96 @@ function ProctoringSettings({ intl, onClose }) {
if (value === 'proctortrack') {
setFormValues({ ...newFormValues, createZendeskTickets: false });
setShowProctortrackEscalationEmail(true);
setShowEscalationEmail(true);
} else if (value === 'software_secure') {
setFormValues({ ...newFormValues, createZendeskTickets: true });
setShowEscalationEmail(false);
} else if (isLtiProvider(value)) {
setFormValues(newFormValues);
setShowEscalationEmail(true);
} else {
if (value === 'software_secure') {
setFormValues({ ...newFormValues, createZendeskTickets: true });
} else {
setFormValues(newFormValues);
}
setShowProctortrackEscalationEmail(false);
setFormValues(newFormValues);
setShowEscalationEmail(false);
}
} else {
setFormValues({ ...formValues, [name]: value });
}
}
};
function setFocusToProctortrackEscalationEmailInput() {
const setFocusToEscalationEmailInput = () => {
if (proctoringEscalationEmailInputRef && proctoringEscalationEmailInputRef.current) {
proctoringEscalationEmailInputRef.current.focus();
}
}
};
function postSettingsBackToServer() {
const dataToPostBack = {
const selectedProvider = formValues.proctoringProvider;
const isLtiProviderSelected = isLtiProvider(selectedProvider);
const studioDataToPostBack = {
proctored_exam_settings: {
enable_proctored_exams: formValues.enableProctoredExams,
proctoring_provider: formValues.proctoringProvider,
// lti providers are managed outside edx-platform, lti_external indicates this
proctoring_provider: isLtiProviderSelected ? 'lti_external' : selectedProvider,
create_zendesk_tickets: formValues.createZendeskTickets,
},
};
if (isEdxStaff) {
dataToPostBack.proctored_exam_settings.allow_proctoring_opt_out = formValues.allowOptingOut;
studioDataToPostBack.proctored_exam_settings.allow_proctoring_opt_out = formValues.allowOptingOut;
}
if (formValues.proctoringProvider === 'proctortrack') {
dataToPostBack.proctored_exam_settings.proctoring_escalation_email = formValues.proctortrackEscalationEmail === '' ? null : formValues.proctortrackEscalationEmail;
studioDataToPostBack.proctored_exam_settings.proctoring_escalation_email = formValues.escalationEmail === '' ? null : formValues.escalationEmail;
}
// only save back to exam service if necessary
setSubmissionInProgress(true);
StudioApiService.saveProctoredExamSettingsData(courseId, dataToPostBack).then(() => {
setSaveSuccess(true);
setSaveError(false);
setSubmissionInProgress(false);
}).catch(() => {
setSaveSuccess(false);
setSaveError(true);
setSubmissionInProgress(false);
});
const saveOperations = [StudioApiService.saveProctoredExamSettingsData(courseId, studioDataToPostBack)];
if (allowLtiProviders && ExamsApiService.isAvailable()) {
const selectedEscalationEmail = formValues.escalationEmail;
saveOperations.push(
ExamsApiService.saveCourseExamConfiguration(
courseId,
{
provider: isLtiProviderSelected ? formValues.proctoringProvider : null,
escalationEmail: (isLtiProviderSelected && selectedEscalationEmail !== '') ? selectedEscalationEmail : null,
},
),
);
}
Promise.all(saveOperations)
.then(() => {
setSaveSuccess(true);
setSaveError(false);
setSubmissionInProgress(false);
}).catch((error) => {
setSaveSuccess(false);
setSaveError(error);
setSubmissionInProgress(false);
});
}
function handleSubmit(event) {
const handleSubmit = (event) => {
event.preventDefault();
const isLtiProviderSelected = isLtiProvider(formValues.proctoringProvider);
if (
formValues.proctoringProvider === 'proctortrack'
&& !EmailValidator.validate(formValues.proctortrackEscalationEmail)
&& !(formValues.proctortrackEscalationEmail === '' && !formValues.enableProctoredExams)
(formValues.proctoringProvider === 'proctortrack' || isLtiProviderSelected)
&& !EmailValidator.validate(formValues.escalationEmail)
&& !(formValues.escalationEmail === '' && !formValues.enableProctoredExams)
) {
if (formValues.proctortrackEscalationEmail === '') {
const errorMessage = intl.formatMessage(messages['authoring.proctoring.escalationemail.error.blank']);
if (formValues.escalationEmail === '') {
const errorMessage = intl.formatMessage(messages['authoring.proctoring.escalationemail.error.blank'], { proctoringProviderName: getProviderDisplayLabel(formValues.proctoringProvider) });
setFormStatus({
isValid: false,
errors: {
formProctortrackEscalationEmail: {
dialogErrorMessage: (<Alert.Link onClick={setFocusToProctortrackEscalationEmailInput} href="#formProctortrackEscalationEmail" data-testid="proctorTrackEscalationEmailErrorLink">{errorMessage}</Alert.Link>),
formEscalationEmail: {
dialogErrorMessage: (
<Alert.Link onClick={setFocusToEscalationEmailInput} href="#formEscalationEmail" data-testid="escalationEmailErrorLink">
{errorMessage}
</Alert.Link>
),
inputErrorMessage: errorMessage,
},
},
@@ -144,8 +185,8 @@ function ProctoringSettings({ intl, onClose }) {
setFormStatus({
isValid: false,
errors: {
formProctortrackEscalationEmail: {
dialogErrorMessage: (<Alert.Link onClick={setFocusToProctortrackEscalationEmailInput} href="#formProctortrackEscalationEmail" data-testid="proctorTrackEscalationEmailErrorLink">{errorMessage}</Alert.Link>),
formEscalationEmail: {
dialogErrorMessage: (<Alert.Link onClick={setFocusToEscalationEmailInput} href="#formEscalationEmail" data-testid="escalationEmailErrorLink">{errorMessage}</Alert.Link>),
inputErrorMessage: errorMessage,
},
},
@@ -154,13 +195,13 @@ function ProctoringSettings({ intl, onClose }) {
} else {
postSettingsBackToServer();
const errors = { ...formStatus.errors };
delete errors.formProctortrackEscalationEmail;
delete errors.formEscalationEmail;
setFormStatus({
isValid: true,
errors,
});
}
}
};
function cannotEditProctoringProvider() {
const currentDate = moment(moment()).format('YYYY-MM-DD[T]hh:mm:ss[Z]');
@@ -186,7 +227,7 @@ function ProctoringSettings({ intl, onClose }) {
disabled={isDisabledOption(provider)}
data-testid={provider}
>
{provider}
{getProviderDisplayLabel(provider)}
</option>
));
}
@@ -206,7 +247,7 @@ function ProctoringSettings({ intl, onClose }) {
);
}
const learnMoreLink = appInfo.documentationLinks?.learnMoreConfiguration && (
const learnMoreLink = appInfo?.documentationLinks?.learnMoreConfiguration && (
<Hyperlink
className="text-primary-500"
destination={appInfo.documentationLinks.learnMoreConfiguration}
@@ -218,16 +259,18 @@ function ProctoringSettings({ intl, onClose }) {
);
function renderContent() {
const isLtiProviderSelected = isLtiProvider(formValues.proctoringProvider);
return (
<>
{!formStatus.isValid && formStatus.errors.formProctortrackEscalationEmail
{!formStatus.isValid && formStatus.errors.formEscalationEmail
&& (
// tabIndex="-1" to make non-focusable element focusable
<Alert
id="proctortrackEscalationEmailError"
id="escalationEmailError"
variant="danger"
tabIndex="-1"
data-testid="proctortrackEscalationEmailError"
data-testid="escalationEmailError"
ref={alertRef}
>
{getFormErrorMessage()}
@@ -290,30 +333,30 @@ function ProctoringSettings({ intl, onClose }) {
</>
)}
{/* PROCTORTRACK ESCALATION EMAIL */}
{showProctortrackEscalationEmail && formValues.enableProctoredExams && (
<Form.Group controlId="formProctortrackEscalationEmail">
{/* ESCALATION EMAIL */}
{showEscalationEmail && formValues.enableProctoredExams && (
<Form.Group controlId="formEscalationEmail">
<Form.Label className="font-weight-bold">
{intl.formatMessage(messages['authoring.proctoring.escalationemail.label'])}
</Form.Label>
<Form.Control
ref={proctoringEscalationEmailInputRef}
type="email"
name="proctortrackEscalationEmail"
name="escalationEmail"
data-testid="escalationEmail"
onChange={handleChange}
value={formValues.proctortrackEscalationEmail}
isInvalid={Object.prototype.hasOwnProperty.call(formStatus.errors, 'formProctortrackEscalationEmail')}
aria-describedby="proctortrackEscalationEmailHelpText"
value={formValues.escalationEmail}
isInvalid={Object.prototype.hasOwnProperty.call(formStatus.errors, 'formEscalationEmail')}
aria-describedby="escalationEmailHelpText"
/>
<Form.Text id="proctortrackEscalationEmailHelpText">
<Form.Text id="escalationEmailHelpText">
{intl.formatMessage(messages['authoring.proctoring.escalationemail.help'])}
</Form.Text>
{Object.prototype.hasOwnProperty.call(formStatus.errors, 'formProctortrackEscalationEmail') && (
{Object.prototype.hasOwnProperty.call(formStatus.errors, 'formEscalationEmail') && (
<Form.Control.Feedback type="invalid">
{
formStatus.errors.formProctortrackEscalationEmail
&& formStatus.errors.formProctortrackEscalationEmail.inputErrorMessage
formStatus.errors.formEscalationEmail
&& formStatus.errors.formEscalationEmail.inputErrorMessage
}
</Form.Control.Feedback>
)}
@@ -321,7 +364,7 @@ function ProctoringSettings({ intl, onClose }) {
)}
{/* ALLOW OPTING OUT OF PROCTORED EXAMS */}
{ isEdxStaff && formValues.enableProctoredExams && (
{ isEdxStaff && formValues.enableProctoredExams && !isLtiProviderSelected && (
<fieldset aria-describedby="allowOptingOutHelpText">
<Form.Group controlId="formAllowingOptingOut">
<Form.Label as="legend" className="font-weight-bold">
@@ -329,6 +372,7 @@ function ProctoringSettings({ intl, onClose }) {
</Form.Label>
<Form.RadioSet
name="allowOptingOut"
data-testid="allowOptingOutRadio"
value={formValues.allowOptingOut.toString()}
onChange={handleChange}
>
@@ -344,7 +388,7 @@ function ProctoringSettings({ intl, onClose }) {
)}
{/* CREATE ZENDESK TICKETS */}
{ isEdxStaff && formValues.enableProctoredExams && (
{ isEdxStaff && formValues.enableProctoredExams && !isLtiProviderSelected && (
<fieldset aria-describedby="createZendeskTicketsText">
<Form.Group controlId="formCreateZendeskTickets">
<Form.Label as="legend" className="font-weight-bold">
@@ -416,21 +460,32 @@ function ProctoringSettings({ intl, onClose }) {
}
function renderSaveError() {
return (
<Alert
variant="danger"
data-testid="saveError"
tabIndex="-1"
ref={saveStatusAlertRef}
onClose={() => setSaveError(false)}
dismissible
>
let errorMessage = (
<FormattedMessage
id="authoring.proctoring.alert.error"
defaultMessage={`
We encountered a technical error while trying to save proctored exam settings.
This might be a temporary issue, so please try again in a few minutes.
If the problem persists, please go to the {support_link} for help.
`}
values={{
support_link: (
<Alert.Link href={getConfig().SUPPORT_URL}>
{intl.formatMessage(messages['authoring.proctoring.support.text'])}
</Alert.Link>
),
}}
/>
);
if (saveError?.response.status === 403) {
errorMessage = (
<FormattedMessage
id="authoring.examsettings.alert.error"
id="authoring.proctoring.alert.error.forbidden"
defaultMessage={`
We encountered a technical error while trying to save proctored exam settings.
This might be a temporary issue, so please try again in a few minutes.
If the problem persists, please go to the {support_link} for help.
You do not have permission to edit proctored exam settings for this course.
If you are a course team member and this problem persists,
please go to the {support_link} for help.
`}
values={{
support_link: (
@@ -440,51 +495,100 @@ function ProctoringSettings({ intl, onClose }) {
),
}}
/>
);
}
return (
<Alert
variant="danger"
data-testid="saveError"
tabIndex="-1"
ref={saveStatusAlertRef}
onClose={() => setSaveError(false)}
dismissible
>
{errorMessage}
</Alert>
);
}
useEffect(
() => {
StudioApiService.getProctoredExamSettingsData(courseId)
.then(
response => {
const proctoredExamSettings = response.data.proctored_exam_settings;
setLoaded(true);
setLoading(false);
setSubmissionInProgress(false);
setCourseStartDate(response.data.course_start_date);
const isProctortrack = proctoredExamSettings.proctoring_provider === 'proctortrack';
setShowProctortrackEscalationEmail(isProctortrack);
setAvailableProctoringProviders(response.data.available_proctoring_providers);
const proctoringEscalationEmail = proctoredExamSettings.proctoring_escalation_email;
useEffect(() => {
Promise.all([
StudioApiService.getProctoredExamSettingsData(courseId),
ExamsApiService.isAvailable() ? ExamsApiService.getCourseExamConfiguration(courseId) : Promise.resolve(),
ExamsApiService.isAvailable() ? ExamsApiService.getAvailableProviders(org) : Promise.resolve(),
])
.then(
([settingsResponse, examConfigResponse, ltiProvidersResponse]) => {
const proctoredExamSettings = settingsResponse.data.proctored_exam_settings;
setLoaded(true);
setLoading(false);
setSubmissionInProgress(false);
setCourseStartDate(settingsResponse.data.course_start_date);
setAvailableProctoringProviders(settingsResponse.data.available_proctoring_providers);
setFormValues({
...formValues,
enableProctoredExams: proctoredExamSettings.enable_proctored_exams,
proctoringProvider: proctoredExamSettings.proctoring_provider,
allowOptingOut: proctoredExamSettings.allow_proctoring_opt_out,
createZendeskTickets: proctoredExamSettings.create_zendesk_tickets,
// The backend API may return null for the proctoringEscalationEmail value, which is the default.
// In order to keep our email input component controlled, we use the empty string as the default
// and perform this conversion during GETs and POSTs.
proctortrackEscalationEmail: proctoringEscalationEmail === null ? '' : proctoringEscalationEmail,
});
},
).catch(
error => {
if (error.response.status === 403) {
setLoadingPermissionError(true);
} else {
setLoadingConnectionError(true);
}
setLoading(false);
setLoaded(false);
setSubmissionInProgress(false);
},
);
}, [],
);
// The list of providers returned by studio settings are the default behavior. If lti_external
// is available as an option display the list of LTI providers returned by the exam service.
// Setting 'lti_external' in studio indicates an LTI provider configured outside of edx-platform.
// This option is not directly selectable.
const proctoringProvidersStudio = settingsResponse.data.available_proctoring_providers;
const proctoringProvidersLti = ltiProvidersResponse?.data || [];
const enableLtiProviders = proctoringProvidersStudio.includes('lti_external');
setAllowLtiProviders(enableLtiProviders);
setLtiProctoringProviders(proctoringProvidersLti);
// flatten provider objects and coalesce values to just the provider key
let availableProviders = proctoringProvidersStudio.filter(value => value !== 'lti_external');
if (enableLtiProviders) {
availableProviders = proctoringProvidersLti.reduce(
(result, provider) => [...result, provider.name],
availableProviders,
);
}
setAvailableProctoringProviders(availableProviders);
let selectedProvider;
if (proctoredExamSettings.proctoring_provider === 'lti_external') {
selectedProvider = examConfigResponse.data.provider;
} else {
selectedProvider = proctoredExamSettings.proctoring_provider;
}
const isProctortrack = selectedProvider === 'proctortrack';
const ltiProviderSelected = proctoringProvidersLti.some(p => p.name === selectedProvider);
if (isProctortrack || ltiProviderSelected) {
setShowEscalationEmail(true);
}
const proctoringEscalationEmail = ltiProviderSelected
? examConfigResponse.data.escalation_email
: proctoredExamSettings.proctoring_escalation_email;
setFormValues({
...formValues,
proctoringProvider: selectedProvider,
enableProctoredExams: proctoredExamSettings.enable_proctored_exams,
allowOptingOut: proctoredExamSettings.allow_proctoring_opt_out,
createZendeskTickets: proctoredExamSettings.create_zendesk_tickets,
// The backend API may return null for the proctoringEscalationEmail value, which is the default.
// In order to keep our email input component controlled, we use the empty string as the default
// and perform this conversion during GETs and POSTs.
escalationEmail: proctoringEscalationEmail === null ? '' : proctoringEscalationEmail,
});
},
).catch(
error => {
if (error.response?.status === 403) {
setLoadingPermissionError(true);
} else {
setLoadingConnectionError(true);
}
setLoading(false);
setLoaded(false);
setSubmissionInProgress(false);
},
);
}, []);
useEffect(() => {
if ((saveSuccess || saveError) && !!saveStatusAlertRef.current) {
@@ -545,7 +649,7 @@ function ProctoringSettings({ intl, onClose }) {
</Form>
</ModalDialog>
);
}
};
ProctoringSettings.propTypes = {
intl: intlShape.isRequired,

View File

@@ -1,37 +1,41 @@
import React from 'react';
import {
render, screen, cleanup, waitFor, waitForElementToBeRemoved, fireEvent, act,
render, screen, cleanup, waitFor, fireEvent, act,
} from '@testing-library/react';
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
// import * as auth from '@edx/frontend-platform/auth';
import MockAdapter from 'axios-mock-adapter';
import { initializeMockApp, mergeConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
import { AppProvider } from '@edx/frontend-platform/react';
import ProctoredExamSettings from './ProctoredExamSettings';
import StudioApiService from '../data/services/StudioApiService';
import ExamsApiService from '../data/services/ExamsApiService';
import initializeStore from '../store';
import StudioApiService from 'CourseAuthoring/data/services/StudioApiService';
import ExamsApiService from 'CourseAuthoring/data/services/ExamsApiService';
import initializeStore from 'CourseAuthoring/store';
import PagesAndResourcesProvider from 'CourseAuthoring/pages-and-resources/PagesAndResourcesProvider';
import ProctoredExamSettings from './Settings';
const courseId = 'course-v1%3AedX%2BDemoX%2BDemo_Course';
const defaultProps = {
courseId: 'course-v1%3AedX%2BDemoX%2BDemo_Course',
courseId,
onClose: () => {},
};
const IntlProctoredExamSettings = injectIntl(ProctoredExamSettings);
let axiosMock;
let store;
const intlWrapper = children => (
<AppProvider store={store}>
<IntlProvider locale="en">
{children}
</IntlProvider>
<PagesAndResourcesProvider courseId={defaultProps.courseId}>
<IntlProvider locale="en">
{children}
</IntlProvider>
</PagesAndResourcesProvider>
</AppProvider>
);
let axiosMock;
describe('ProctoredExamSettings', () => {
function setupApp(isAdmin = true) {
function setupApp(isAdmin = true, org = undefined) {
mergeConfig({
EXAMS_BASE_URL: 'http://exams.testing.co',
}, 'CourseAuthoringConfig');
@@ -44,15 +48,27 @@ describe('ProctoredExamSettings', () => {
roles: [],
},
});
store = initializeStore();
store = initializeStore({
models: {
courseApps: {
proctoring: {},
},
courseDetails: {
[courseId]: {
start: Date(),
},
},
...(org ? { courseDetails: { [courseId]: { org } } } : {}),
},
});
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock.onGet(
`${ExamsApiService.getExamsBaseUrl()}/api/v1/providers`,
`${ExamsApiService.getExamsBaseUrl()}/api/v1/providers${org ? `?org=${org}` : ''}`,
).reply(200, [
{
name: 'test_lti',
verbose_name: 'LTI Provider',
name: 'test_lti',
verbose_name: 'LTI Provider',
},
]);
axiosMock.onGet(
@@ -94,9 +110,7 @@ describe('ProctoredExamSettings', () => {
screen.getByDisplayValue('mockproc');
});
const selectElement = screen.getByDisplayValue('mockproc');
await act(async () => {
fireEvent.change(selectElement, { target: { value: 'proctortrack' } });
});
fireEvent.change(selectElement, { target: { value: 'proctortrack' } });
const zendeskTicketInput = screen.getByTestId('createZendeskTicketsNo');
expect(zendeskTicketInput.checked).toEqual(true);
});
@@ -106,9 +120,7 @@ describe('ProctoredExamSettings', () => {
screen.getByDisplayValue('mockproc');
});
const selectElement = screen.getByDisplayValue('mockproc');
await act(async () => {
fireEvent.change(selectElement, { target: { value: 'software_secure' } });
});
fireEvent.change(selectElement, { target: { value: 'software_secure' } });
const zendeskTicketInput = screen.getByTestId('createZendeskTicketsYes');
expect(zendeskTicketInput.checked).toEqual(true);
});
@@ -118,9 +130,7 @@ describe('ProctoredExamSettings', () => {
screen.getByDisplayValue('mockproc');
});
const selectElement = screen.getByDisplayValue('mockproc');
await act(async () => {
fireEvent.change(selectElement, { target: { value: 'mockproc' } });
});
fireEvent.change(selectElement, { target: { value: 'mockproc' } });
const zendeskTicketInput = screen.getByTestId('createZendeskTicketsYes');
expect(zendeskTicketInput.checked).toEqual(true);
});
@@ -144,9 +154,9 @@ describe('ProctoredExamSettings', () => {
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
await waitFor(() => {
screen.getByLabelText('Enable Proctored Exams');
screen.getByText('Proctored exams');
});
const enabledProctoredExamCheck = screen.getByLabelText('Enable Proctored Exams');
const enabledProctoredExamCheck = screen.getByLabelText('Proctored exams');
expect(enabledProctoredExamCheck.checked).toEqual(false);
expect(screen.queryByText('Allow Opting Out of Proctored Exams')).toBeNull();
expect(screen.queryByDisplayValue('mockproc')).toBeNull();
@@ -157,22 +167,20 @@ describe('ProctoredExamSettings', () => {
it('Hides all other fields when enableProctoredExams toggled to false', async () => {
await waitFor(() => {
screen.getByLabelText('Enable Proctored Exams');
screen.getByText('Proctored exams');
});
expect(screen.queryByText('Allow Opting Out of Proctored Exams')).toBeDefined();
expect(screen.queryByText('Allow opting out of proctored exams')).toBeDefined();
expect(screen.queryByDisplayValue('mockproc')).toBeDefined();
expect(screen.queryByTestId('escalationEmail')).toBeDefined();
expect(screen.queryByTestId('createZendeskTicketsYes')).toBeDefined();
expect(screen.queryByTestId('createZendeskTicketsNo')).toBeDefined();
let enabledProctorExamCheck = screen.getByLabelText('Enable Proctored Exams');
expect(enabledProctorExamCheck.checked).toEqual(true);
await act(async () => {
fireEvent.click(enabledProctorExamCheck, { target: { value: false } });
});
enabledProctorExamCheck = screen.getByLabelText('Enable Proctored Exams');
expect(enabledProctorExamCheck.checked).toEqual(false);
expect(screen.queryByText('Allow Opting Out of Proctored Exams')).toBeNull();
let enabledProctoredExamCheck = screen.getAllByLabelText('Proctored exams', { exact: false })[0];
expect(enabledProctoredExamCheck.checked).toEqual(true);
fireEvent.click(enabledProctoredExamCheck, { target: { value: false } });
enabledProctoredExamCheck = screen.getByLabelText('Proctored exams');
expect(enabledProctoredExamCheck.checked).toEqual(false);
expect(screen.queryByText('Allow opting out of proctored exams')).toBeNull();
expect(screen.queryByDisplayValue('mockproc')).toBeNull();
expect(screen.queryByTestId('escalationEmail')).toBeNull();
expect(screen.queryByTestId('createZendeskTicketsYes')).toBeNull();
@@ -184,16 +192,16 @@ describe('ProctoredExamSettings', () => {
screen.getByDisplayValue('mockproc');
});
const selectElement = screen.getByDisplayValue('mockproc');
await act(async () => {
fireEvent.change(selectElement, { target: { value: 'test_lti' } });
});
expect(screen.queryByTestId('escalationEmail')).toBeNull();
fireEvent.change(selectElement, { target: { value: 'test_lti' } });
expect(screen.queryByTestId('allowOptingOutRadio')).toBeNull();
expect(screen.queryByTestId('createZendeskTicketsYes')).toBeNull();
expect(screen.queryByTestId('createZendeskTicketsNo')).toBeNull();
});
});
describe('Validation with invalid escalation email', () => {
const proctoringProvidersRequiringEscalationEmail = ['proctortrack', 'test_lti'];
beforeEach(async () => {
axiosMock.onGet(
StudioApiService.getProctoredExamSettingsUrl(defaultProps.courseId),
@@ -205,196 +213,166 @@ describe('ProctoredExamSettings', () => {
proctoring_escalation_email: 'test@example.com',
create_zendesk_tickets: true,
},
available_proctoring_providers: ['software_secure', 'proctortrack', 'mockproc'],
available_proctoring_providers: ['software_secure', 'proctortrack', 'mockproc', 'lti_external'],
course_start_date: '2070-01-01T00:00:00Z',
});
axiosMock.onPatch(
ExamsApiService.getExamConfigurationUrl(defaultProps.courseId),
).reply(204, {});
axiosMock.onPost(
StudioApiService.getProctoredExamSettingsUrl(defaultProps.courseId),
).reply(200, {});
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
});
it('Creates an alert when no proctoring escalation email is provided with proctortrack selected', async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
await act(async () => {
proctoringProvidersRequiringEscalationEmail.forEach(provider => {
it(`Creates an alert when no proctoring escalation email is provided with ${provider} selected`, async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
fireEvent.change(selectEscalationEmailElement, { target: { value: '' } });
});
const selectButton = screen.getByTestId('submissionButton');
await act(async () => {
const selectButton = screen.getByTestId('submissionButton');
fireEvent.click(selectButton);
});
// verify alert content and focus management
const escalationEmailError = screen.getByTestId('proctortrackEscalationEmailError');
expect(escalationEmailError.textContent).not.toBeNull();
expect(document.activeElement).toEqual(escalationEmailError);
// verify alert content and focus management
const escalationEmailError = screen.getByTestId('escalationEmailError');
expect(escalationEmailError.textContent).not.toBeNull();
expect(document.activeElement).toEqual(escalationEmailError);
// verify alert link links to offending input
const errorLink = screen.getByTestId('proctorTrackEscalationEmailErrorLink');
await act(async () => {
// verify alert link links to offending input
const errorLink = screen.getByTestId('escalationEmailErrorLink');
fireEvent.click(errorLink);
const escalationEmailInput = screen.getByTestId('escalationEmail');
expect(document.activeElement).toEqual(escalationEmailInput);
});
const escalationEmailInput = screen.getByTestId('escalationEmail');
expect(document.activeElement).toEqual(escalationEmailInput);
});
it('Creates an alert when invalid proctoring escalation email is provided with proctortrack selected', async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
await act(async () => {
it(`Creates an alert when invalid proctoring escalation email is provided with ${provider} selected`, async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectElement = screen.getByDisplayValue('proctortrack');
fireEvent.change(selectElement, { target: { value: provider } });
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
fireEvent.change(selectEscalationEmailElement, { target: { value: 'foo.bar' } });
});
const selectButton = screen.getByTestId('submissionButton');
await act(async () => {
fireEvent.click(selectButton);
});
const proctoringForm = screen.getByTestId('proctoringForm');
fireEvent.submit(proctoringForm);
// verify alert content and focus management
const escalationEmailError = screen.getByTestId('proctortrackEscalationEmailError');
expect(document.activeElement).toEqual(escalationEmailError);
expect(escalationEmailError.textContent).not.toBeNull();
expect(document.activeElement).toEqual(escalationEmailError);
// verify alert content and focus management
const escalationEmailError = screen.getByTestId('escalationEmailError');
expect(document.activeElement).toEqual(escalationEmailError);
expect(escalationEmailError.textContent).not.toBeNull();
expect(document.activeElement).toEqual(escalationEmailError);
// verify alert link links to offending input
const errorLink = screen.getByTestId('proctorTrackEscalationEmailErrorLink');
await act(async () => {
// verify alert link links to offending input
const errorLink = screen.getByTestId('escalationEmailErrorLink');
fireEvent.click(errorLink);
const escalationEmailInput = screen.getByTestId('escalationEmail');
expect(document.activeElement).toEqual(escalationEmailInput);
});
const escalationEmailInput = screen.getByTestId('escalationEmail');
expect(document.activeElement).toEqual(escalationEmailInput);
});
it('Creates an alert when invalid proctoring escalation email is provided with proctoring disabled', async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
await act(async () => {
it('Creates an alert when invalid proctoring escalation email is provided with proctoring disabled', async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
fireEvent.change(selectEscalationEmailElement, { target: { value: 'foo.bar' } });
});
const enableProctoringElement = screen.getByLabelText('Enable Proctored Exams');
await act(async () => fireEvent.click(enableProctoringElement));
const selectButton = screen.getByTestId('submissionButton');
await act(async () => {
const enableProctoringElement = screen.getByText('Proctored exams');
fireEvent.click(enableProctoringElement);
const selectButton = screen.getByTestId('submissionButton');
fireEvent.click(selectButton);
// verify alert content and focus management
const escalationEmailError = screen.getByTestId('escalationEmailError');
expect(document.activeElement).toEqual(escalationEmailError);
expect(escalationEmailError.textContent).not.toBeNull();
expect(document.activeElement).toEqual(escalationEmailError);
});
// verify alert content and focus management
const escalationEmailError = screen.getByTestId('proctortrackEscalationEmailError');
expect(document.activeElement).toEqual(escalationEmailError);
expect(escalationEmailError.textContent).not.toBeNull();
expect(document.activeElement).toEqual(escalationEmailError);
});
it('Has no error when invalid proctoring escalation email is provided with proctoring disabled', async () => {
axiosMock.onPost(
StudioApiService.getProctoredExamSettingsUrl(defaultProps.courseId),
).reply(200, 'success');
axiosMock.onPatch(
`${ExamsApiService.getExamsBaseUrl()}/api/v1/configs/course_id/${defaultProps.courseId}`,
).reply(200, 'success');
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
await act(async () => {
it('Has no error when empty proctoring escalation email is provided with proctoring disabled', async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
fireEvent.change(selectEscalationEmailElement, { target: { value: '' } });
});
const enableProctoringElement = screen.getByLabelText('Enable Proctored Exams');
await act(async () => fireEvent.click(enableProctoringElement));
const selectButton = screen.getByTestId('submissionButton');
await act(async () => {
const enableProctoringElement = screen.getByText('Proctored exams');
fireEvent.click(enableProctoringElement);
const selectButton = screen.getByTestId('submissionButton');
fireEvent.click(selectButton);
// verify there is no escalation email alert, and focus has been set on save success alert
expect(screen.queryByTestId('escalationEmailError')).toBeNull();
await waitFor(() => {
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
});
});
// verify there is no escalation email alert, and focus has been set on save success alert
expect(screen.queryByTestId('proctortrackEscalationEmailError')).toBeNull();
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
});
it('Has no error when valid proctoring escalation email is provided with proctortrack selected', async () => {
axiosMock.onPost(
StudioApiService.getProctoredExamSettingsUrl(defaultProps.courseId),
).reply(200, 'success');
axiosMock.onPatch(
`${ExamsApiService.getExamsBaseUrl()}/api/v1/configs/course_id/${defaultProps.courseId}`,
).reply(200, 'success');
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
await act(async () => {
it(`Has no error when valid proctoring escalation email is provided with ${provider} selected`, async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
fireEvent.change(selectEscalationEmailElement, { target: { value: 'foo@bar.com' } });
});
const selectButton = screen.getByTestId('submissionButton');
await act(async () => {
const selectButton = screen.getByTestId('submissionButton');
fireEvent.click(selectButton);
// verify there is no escalation email alert, and focus has been set on save success alert
expect(screen.queryByTestId('escalationEmailError')).toBeNull();
await waitFor(() => {
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
});
});
// verify there is no escalation email alert, and focus has been set on save success alert
expect(screen.queryByTestId('proctortrackEscalationEmailError')).toBeNull();
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
});
it('Escalation email field hidden when proctoring backend is not Proctortrack', async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const proctoringBackendSelect = screen.getByDisplayValue('proctortrack');
const selectEscalationEmailElement = screen.getByTestId('escalationEmail');
expect(selectEscalationEmailElement.value).toEqual('test@example.com');
await act(async () => {
it(`Escalation email field hidden when proctoring backend is not ${provider}`, async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const proctoringBackendSelect = screen.getByDisplayValue('proctortrack');
const selectEscalationEmailElement = screen.getByTestId('escalationEmail');
expect(selectEscalationEmailElement.value).toEqual('test@example.com');
fireEvent.change(proctoringBackendSelect, { target: { value: 'software_secure' } });
expect(screen.queryByTestId('escalationEmail')).toBeNull();
});
expect(screen.queryByTestId('escalationEmail')).toBeNull();
});
it('Escalation email Field Show when proctoring backend is switched back to Proctortrack', async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const proctoringBackendSelect = screen.getByDisplayValue('proctortrack');
let selectEscalationEmailElement = screen.getByTestId('escalationEmail');
await act(async () => {
it(`Escalation email Field Show when proctoring backend is switched back to ${provider}`, async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const proctoringBackendSelect = screen.getByDisplayValue('proctortrack');
let selectEscalationEmailElement = screen.getByTestId('escalationEmail');
fireEvent.change(proctoringBackendSelect, { target: { value: 'software_secure' } });
});
expect(screen.queryByTestId('escalationEmail')).toBeNull();
await act(async () => {
expect(screen.queryByTestId('escalationEmail')).toBeNull();
fireEvent.change(proctoringBackendSelect, { target: { value: 'proctortrack' } });
expect(screen.queryByTestId('escalationEmail')).toBeDefined();
selectEscalationEmailElement = screen.getByTestId('escalationEmail');
expect(selectEscalationEmailElement.value).toEqual('test@example.com');
});
expect(screen.queryByTestId('escalationEmail')).toBeDefined();
selectEscalationEmailElement = screen.getByTestId('escalationEmail');
expect(selectEscalationEmailElement.value).toEqual('test@example.com');
});
it('Submits form when "Enter" key is hit in the escalation email field', async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
await act(async () => {
it('Submits form when "Enter" key is hit in the escalation email field', async () => {
await waitFor(() => {
screen.getByDisplayValue('proctortrack');
});
const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com');
fireEvent.change(selectEscalationEmailElement, { target: { value: '' } });
});
await act(async () => {
fireEvent.submit(selectEscalationEmailElement);
// if the error appears, the form has been submitted
expect(screen.getByTestId('escalationEmailError')).toBeDefined();
});
// if the error appears, the form has been submitted
expect(screen.getByTestId('proctortrackEscalationEmailError')).toBeDefined();
});
});
@@ -445,6 +423,16 @@ describe('ProctoredExamSettings', () => {
expect(providerOption.hasAttribute('disabled')).toEqual(false);
});
it('Sends the org to the proctoring provider endpoint', async () => {
const isAdmin = false;
const org = 'test-org';
setupApp(isAdmin, org);
mockCourseData(mockGetFutureCourseData);
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
const providerOption = screen.getByTestId('proctortrack');
expect(providerOption.hasAttribute('disabled')).toEqual(false);
});
it('Enables all proctoring provider options if user administrator and it is after start date', async () => {
const isAdmin = true;
setupApp(isAdmin);
@@ -464,8 +452,10 @@ describe('ProctoredExamSettings', () => {
});
it('Does not include lti_external as a selectable option', async () => {
const courseData = mockGetFutureCourseData;
courseData.available_proctoring_providers = ['lti_external', 'proctortrack', 'mockproc'];
const courseData = {
...mockGetFutureCourseData,
available_proctoring_providers: ['lti_external', 'proctortrack', 'mockproc'],
};
mockCourseData(courseData);
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
await waitFor(() => {
@@ -475,8 +465,10 @@ describe('ProctoredExamSettings', () => {
});
it('Includes lti proctoring provider options when lti_external is allowed by studio', async () => {
const courseData = mockGetFutureCourseData;
courseData.available_proctoring_providers = ['lti_external', 'proctortrack', 'mockproc'];
const courseData = {
...mockGetFutureCourseData,
available_proctoring_providers: ['lti_external', 'proctortrack', 'mockproc'],
};
mockCourseData(courseData);
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
await waitFor(() => {
@@ -487,6 +479,19 @@ describe('ProctoredExamSettings', () => {
expect(providerOption.hasAttribute('disabled')).toEqual(false);
});
it('Does not include lti provider options when lti_external is not available in studio', async () => {
const isAdmin = true;
setupApp(isAdmin);
mockCourseData(mockGetFutureCourseData);
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
await waitFor(() => {
screen.getByDisplayValue('mockproc');
});
const providerOption = screen.queryByTestId('test_lti');
expect(providerOption).not.toBeInTheDocument();
});
it('Does not request lti provider options if there is no exam service url configuration', async () => {
mergeConfig({
EXAMS_BASE_URL: null,
@@ -513,7 +518,7 @@ describe('ProctoredExamSettings', () => {
});
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
await waitFor(() => {
screen.getByText('Proctoring Provider');
screen.getByText('Proctoring provider');
});
// make sure test_lti is the selected provider
@@ -594,46 +599,27 @@ describe('ProctoredExamSettings', () => {
).reply(200, 'success');
});
it('Show spinner while saving', async () => {
axiosMock.onPost(
StudioApiService.getProctoredExamSettingsUrl(defaultProps.courseId),
).reply(200, 'success');
it('Disable button while submitting', async () => {
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
const submitButton = screen.getByTestId('submissionButton');
let submitButton = screen.getByTestId('submissionButton');
expect(screen.queryByTestId('saveInProgress')).toBeFalsy();
act(() => {
fireEvent.click(submitButton);
});
fireEvent.click(submitButton);
const submitSpinner = screen.getByTestId('saveInProgress');
expect(submitSpinner).toBeDefined();
await waitForElementToBeRemoved(submitSpinner);
// request studio settings, exam config, and exam service providers
expect(axiosMock.history.get.length).toBe(3);
expect(axiosMock.history.post.length).toBe(1); // studio
expect(axiosMock.history.patch.length).toBe(1); // edx-exams
expect(screen.queryByTestId('saveInProgress')).toBeFalsy();
submitButton = screen.getByTestId('submissionButton');
expect(submitButton).toHaveAttribute('disabled');
});
it('Makes API call successfully with proctoring_escalation_email if proctortrack', async () => {
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
// Make a change to the provider to proctortrack and set the email
const selectElement = screen.getByDisplayValue('mockproc');
await act(async () => {
fireEvent.change(selectElement, { target: { value: 'proctortrack' } });
});
fireEvent.change(selectElement, { target: { value: 'proctortrack' } });
const escalationEmail = screen.getByTestId('escalationEmail');
expect(escalationEmail.value).toEqual('test@example.com');
await act(async () => {
fireEvent.change(escalationEmail, { target: { value: 'proctortrack@example.com' } });
});
fireEvent.change(escalationEmail, { target: { value: 'proctortrack@example.com' } });
expect(escalationEmail.value).toEqual('proctortrack@example.com');
const submitButton = screen.getByTestId('submissionButton');
await act(async () => {
fireEvent.click(submitButton);
});
fireEvent.click(submitButton);
expect(axiosMock.history.post.length).toBe(1);
expect(JSON.parse(axiosMock.history.post[0].data)).toEqual({
proctored_exam_settings: {
@@ -645,11 +631,13 @@ describe('ProctoredExamSettings', () => {
},
});
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
await waitFor(() => {
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
});
});
it('Makes API call successfully without proctoring_escalation_email if not proctortrack', async () => {
@@ -659,9 +647,7 @@ describe('ProctoredExamSettings', () => {
expect(screen.getByDisplayValue('mockproc')).toBeDefined();
const submitButton = screen.getByTestId('submissionButton');
await act(async () => {
fireEvent.click(submitButton);
});
fireEvent.click(submitButton);
expect(axiosMock.history.post.length).toBe(1);
expect(JSON.parse(axiosMock.history.post[0].data)).toEqual({
proctored_exam_settings: {
@@ -672,29 +658,34 @@ describe('ProctoredExamSettings', () => {
},
});
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
await waitFor(() => {
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
});
});
it('Successfully updates exam configuration and studio provider is set to "lti_external" for lti providers', async () => {
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
// Make a change to the provider to proctortrack and set the email
// Make a change to the provider to test_lti and set the email
const selectElement = screen.getByDisplayValue('mockproc');
await act(async () => {
fireEvent.change(selectElement, { target: { value: 'test_lti' } });
});
fireEvent.change(selectElement, { target: { value: 'test_lti' } });
const escalationEmail = screen.getByTestId('escalationEmail');
expect(escalationEmail.value).toEqual('test@example.com');
fireEvent.change(escalationEmail, { target: { value: 'test_lti@example.com' } });
expect(escalationEmail.value).toEqual('test_lti@example.com');
const submitButton = screen.getByTestId('submissionButton');
await act(async () => {
fireEvent.click(submitButton);
});
fireEvent.click(submitButton);
// update exam service config
expect(axiosMock.history.patch.length).toBe(1);
expect(JSON.parse(axiosMock.history.patch[0].data)).toEqual({
provider: 'test_lti',
escalation_email: 'test_lti@example.com',
});
// update studio settings
@@ -708,23 +699,24 @@ describe('ProctoredExamSettings', () => {
},
});
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
await waitFor(() => {
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
});
});
it('Sets exam service provider to null if a non-lti provider is selected', async () => {
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
const submitButton = screen.getByTestId('submissionButton');
await act(async () => {
fireEvent.click(submitButton);
});
fireEvent.click(submitButton);
// update exam service config
expect(axiosMock.history.patch.length).toBe(1);
expect(JSON.parse(axiosMock.history.patch[0].data)).toEqual({
provider: null,
escalation_email: null,
});
expect(axiosMock.history.patch.length).toBe(1);
expect(axiosMock.history.post.length).toBe(1);
@@ -737,11 +729,13 @@ describe('ProctoredExamSettings', () => {
},
});
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
await waitFor(() => {
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
});
});
it('Does not update exam service if lti is not enabled in studio', async () => {
@@ -761,9 +755,7 @@ describe('ProctoredExamSettings', () => {
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
const submitButton = screen.getByTestId('submissionButton');
await act(async () => {
fireEvent.click(submitButton);
});
fireEvent.click(submitButton);
// does not update exam service config
expect(axiosMock.history.patch.length).toBe(0);
// does update studio
@@ -777,11 +769,13 @@ describe('ProctoredExamSettings', () => {
},
});
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
await waitFor(() => {
const errorAlert = screen.getByTestId('saveSuccess');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(errorAlert);
});
});
it('Makes studio API call generated error', async () => {
@@ -791,15 +785,15 @@ describe('ProctoredExamSettings', () => {
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
const submitButton = screen.getByTestId('submissionButton');
await act(async () => {
fireEvent.click(submitButton);
});
fireEvent.click(submitButton);
expect(axiosMock.history.post.length).toBe(1);
const errorAlert = screen.getByTestId('saveError');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'),
);
expect(document.activeElement).toEqual(errorAlert);
await waitFor(() => {
const errorAlert = screen.getByTestId('saveError');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'),
);
expect(document.activeElement).toEqual(errorAlert);
});
});
it('Makes exams API call generated error', async () => {
@@ -809,15 +803,33 @@ describe('ProctoredExamSettings', () => {
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
const submitButton = screen.getByTestId('submissionButton');
await act(async () => {
fireEvent.click(submitButton);
});
fireEvent.click(submitButton);
expect(axiosMock.history.post.length).toBe(1);
const errorAlert = screen.getByTestId('saveError');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'),
);
expect(document.activeElement).toEqual(errorAlert);
await waitFor(() => {
const errorAlert = screen.getByTestId('saveError');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'),
);
expect(document.activeElement).toEqual(errorAlert);
});
});
test('Exams API permission error', async () => {
axiosMock.onPatch(
`${ExamsApiService.getExamsBaseUrl()}/api/v1/configs/course_id/${defaultProps.courseId}`,
).reply(403, 'error');
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
const submitButton = screen.getByTestId('submissionButton');
fireEvent.click(submitButton);
expect(axiosMock.history.post.length).toBe(1);
await waitFor(() => {
const errorAlert = screen.getByTestId('saveError');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('You do not have permission to edit proctored exam settings for this course'),
);
expect(document.activeElement).toEqual(errorAlert);
});
});
it('Manages focus correctly after different save statuses', async () => {
@@ -828,30 +840,30 @@ describe('ProctoredExamSettings', () => {
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
const submitButton = screen.getByTestId('submissionButton');
await act(async () => {
fireEvent.click(submitButton);
});
fireEvent.click(submitButton);
expect(axiosMock.history.post.length).toBe(1);
const errorAlert = screen.getByTestId('saveError');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'),
);
expect(document.activeElement).toEqual(errorAlert);
await waitFor(() => {
const errorAlert = screen.getByTestId('saveError');
expect(errorAlert.textContent).toEqual(
expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'),
);
expect(document.activeElement).toEqual(errorAlert);
});
// now make a call that will allow for a successful save
axiosMock.onPost(
StudioApiService.getProctoredExamSettingsUrl(defaultProps.courseId),
).reply(200, 'success');
await act(async () => {
fireEvent.click(submitButton);
});
fireEvent.click(submitButton);
expect(axiosMock.history.post.length).toBe(2);
const successAlert = screen.getByTestId('saveSuccess');
expect(successAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(successAlert);
await waitFor(() => {
const successAlert = screen.getByTestId('saveSuccess');
expect(successAlert.textContent).toEqual(
expect.stringContaining('Proctored exam settings saved successfully.'),
);
expect(document.activeElement).toEqual(successAlert);
});
});
it('Include Zendesk ticket in post request if user is not an admin', async () => {
@@ -861,17 +873,10 @@ describe('ProctoredExamSettings', () => {
await act(async () => render(intlWrapper(<IntlProctoredExamSettings {...defaultProps} />)));
// Make a change to the proctoring provider
await waitFor(() => {
screen.getByDisplayValue('mockproc');
});
const selectElement = screen.getByDisplayValue('mockproc');
await act(async () => {
fireEvent.change(selectElement, { target: { value: 'proctortrack' } });
});
fireEvent.change(selectElement, { target: { value: 'proctortrack' } });
const submitButton = screen.getByTestId('submissionButton');
await act(async () => {
fireEvent.click(submitButton);
});
fireEvent.click(submitButton);
expect(axiosMock.history.post.length).toBe(1);
expect(JSON.parse(axiosMock.history.post[0].data)).toEqual({
proctored_exam_settings: {

View File

@@ -1,6 +1,16 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
'authoring.proctoring.alert.error': {
id: 'authoring.proctoring.alert.error',
defaultMessage: 'We encountered a technical error while trying to save proctored exam settings. This might be a temporary issue, so please try again in a few minutes. If the problem persists, please go to the {support_link} for help.',
description: 'Alert message for proctoring settings save error.',
},
'authoring.proctoring.alert.forbidden': {
id: 'authoring.proctoring.alert.forbidden',
defaultMessage: 'You do not have permission to edit proctored exam settings for this course. If you are a course team member and this problem persists, please go to the {support_link} for help.',
description: 'Alert message for proctoring settings permission error.',
},
'authoring.proctoring.no': {
id: 'authoring.proctoring.no',
defaultMessage: 'No',
@@ -53,7 +63,7 @@ const messages = defineMessages({
},
'authoring.proctoring.escalationemail.label': {
id: 'authoring.proctoring.escalationemail.label',
defaultMessage: 'Proctortrack escalation email',
defaultMessage: 'Escalation email',
description: 'Label for escalation email text field',
},
'authoring.proctoring.escalationemail.help': {
@@ -63,12 +73,12 @@ const messages = defineMessages({
},
'authoring.proctoring.escalationemail.error.blank': {
id: 'authoring.proctoring.escalationemail.error.blank',
defaultMessage: 'The Proctortrack Escalation Email field cannot be empty if proctortrack is the selected provider.',
defaultMessage: 'The Escalation Email field cannot be empty if {proctoringProviderName} is the selected provider.',
description: 'Error message for missing required email field.',
},
'authoring.proctoring.escalationemail.error.invalid': {
id: 'authoring.proctoring.escalationemail.error.invalid',
defaultMessage: 'The Proctortrack Escalation Email field is in the wrong format and is not valid.',
defaultMessage: 'The Escalation Email field is in the wrong format and is not valid.',
description: 'Error message for a invalid email format.',
},
'authoring.proctoring.allowoptout.label': {

View File

@@ -0,0 +1,20 @@
{
"name": "@openedx-plugins/course-app-proctoring",
"version": "0.1.0",
"description": "Proctoring configuration for courses using it",
"peerDependencies": {
"@edx/frontend-app-authoring": "*",
"@edx/frontend-platform": "*",
"@openedx/paragon": "*",
"classnames": "*",
"email-validator": "*",
"react": "*",
"prop-types": "*",
"moment": "*"
},
"peerDependenciesMeta": {
"@edx/frontend-app-authoring": {
"optional": true
}
}
}

View File

@@ -3,17 +3,17 @@ import PropTypes from 'prop-types';
import React from 'react';
import * as Yup from 'yup';
import { getConfig } from '@edx/frontend-platform';
import FormSwitchGroup from '../../generic/FormSwitchGroup';
import { useAppSetting } from '../../utils';
import AppSettingsModal from '../app-settings-modal/AppSettingsModal';
import FormSwitchGroup from 'CourseAuthoring/generic/FormSwitchGroup';
import { useAppSetting } from 'CourseAuthoring/utils';
import AppSettingsModal from 'CourseAuthoring/pages-and-resources/app-settings-modal/AppSettingsModal';
import messages from './messages';
function ProgressSettings({ intl, onClose }) {
const ProgressSettings = ({ intl, onClose }) => {
const [disableProgressGraph, saveSetting] = useAppSetting('disableProgressGraph');
const showProgressGraphSetting = getConfig().ENABLE_PROGRESS_GRAPH_SETTINGS.toLowerCase() === 'true';
const showProgressGraphSetting = getConfig().ENABLE_PROGRESS_GRAPH_SETTINGS.toString().toLowerCase() === 'true';
const handleSettingsSave = (values) => {
if (showProgressGraphSetting) { saveSetting(!values.enableProgressGraph); }
const handleSettingsSave = async (values) => {
if (showProgressGraphSetting) { await saveSetting(!values.enableProgressGraph); }
};
return (
@@ -31,21 +31,21 @@ function ProgressSettings({ intl, onClose }) {
{
({ handleChange, handleBlur, values }) => (
showProgressGraphSetting && (
<FormSwitchGroup
id="enable-progress-graph"
name="enableProgressGraph"
label={intl.formatMessage(messages.enableGraphLabel)}
helpText={intl.formatMessage(messages.enableGraphHelp)}
onChange={handleChange}
onBlur={handleBlur}
checked={values.enableProgressGraph}
/>
<FormSwitchGroup
id="enable-progress-graph"
name="enableProgressGraph"
label={intl.formatMessage(messages.enableGraphLabel)}
helpText={intl.formatMessage(messages.enableGraphHelp)}
onChange={handleChange}
onBlur={handleBlur}
checked={values.enableProgressGraph}
/>
)
)
}
</AppSettingsModal>
);
}
};
ProgressSettings.propTypes = {
intl: intlShape.isRequired,

View File

@@ -0,0 +1,18 @@
{
"name": "@openedx-plugins/course-app-progress",
"version": "0.1.0",
"description": "Progress configuration for courses using it",
"peerDependencies": {
"@edx/frontend-app-authoring": "*",
"@edx/frontend-platform": "*",
"@openedx/paragon": "*",
"prop-types": "*",
"react": "*",
"yup": "*"
},
"peerDependenciesMeta": {
"@edx/frontend-app-authoring": {
"optional": true
}
}
}

View File

@@ -1,12 +1,13 @@
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button, Form, TransitionReplace } from '@edx/paragon';
import { Button, Form, TransitionReplace } from '@openedx/paragon';
import PropTypes from 'prop-types';
import React, { useState } from 'react';
import { GroupTypes, TeamSizes } from '../../data/constants';
import { GroupTypes, TeamSizes } from 'CourseAuthoring/data/constants';
import CollapsableEditor from '../../generic/CollapsableEditor';
import FormikControl from '../../generic/FormikControl';
import CollapsableEditor from 'CourseAuthoring/generic/CollapsableEditor';
import FormikControl from 'CourseAuthoring/generic/FormikControl';
import messages from './messages';
import { isGroupTypeEnabled } from './utils';
// Maps a team type to its corresponding intl message
const TeamTypeNameMessage = {
@@ -14,6 +15,10 @@ const TeamTypeNameMessage = {
label: messages.groupTypeOpen,
description: messages.groupTypeOpenDescription,
},
[GroupTypes.OPEN_MANAGED]: {
label: messages.groupTypeOpenManaged,
description: messages.groupTypeOpenManagedDescription,
},
[GroupTypes.PUBLIC_MANAGED]: {
label: messages.groupTypePublicManaged,
description: messages.groupTypePublicManagedDescription,
@@ -24,9 +29,9 @@ const TeamTypeNameMessage = {
},
};
function GroupEditor({
const GroupEditor = ({
intl, group, onDelete, onChange, onBlur, fieldNameCommonBase, errors,
}) {
}) => {
const [isDeleting, setDeleting] = useState(false);
const [isOpen, setOpen] = useState(group.id === null);
const initiateDeletion = () => setDeleting(true);
@@ -105,7 +110,7 @@ function GroupEditor({
onChange={onChange}
onBlur={onBlur}
>
{Object.values(GroupTypes).map(groupType => (
{Object.values(GroupTypes).map(groupType => isGroupTypeEnabled(groupType) && (
<Form.Radio
key={groupType}
value={groupType}
@@ -133,7 +138,7 @@ function GroupEditor({
)}
</TransitionReplace>
);
}
};
export const groupShape = PropTypes.shape({
id: PropTypes.string,

View File

@@ -0,0 +1,102 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { useFormikContext } from 'formik';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import GroupEditor from './GroupEditor';
import messages from './messages';
jest.mock('formik', () => ({
...jest.requireActual('formik'),
useFormikContext: jest.fn(),
}));
describe('GroupEditor', () => {
const mockIntl = { formatMessage: jest.fn() };
const mockGroup = {
id: '1',
name: 'Test Group',
description: 'Test Group Description',
type: 'open',
maxTeamSize: 5,
};
const mockProps = {
intl: mockIntl,
fieldNameCommonBase: 'test',
group: mockGroup,
onDelete: jest.fn(),
onChange: jest.fn(),
onBlur: jest.fn(),
errors: {},
};
const renderComponent = (overrideProps = {}) => render(
<IntlProvider locale="en" messages={{}}>
<GroupEditor {...mockProps} {...overrideProps} />
</IntlProvider>,
);
beforeEach(() => {
useFormikContext.mockReturnValue({
touched: {},
errors: {},
handleChange: jest.fn(),
handleBlur: jest.fn(),
setFieldError: jest.fn(),
});
jest.clearAllMocks();
});
test('renders without errors', () => {
renderComponent();
});
test('renders the group name and description', () => {
const { getByText } = renderComponent();
expect(getByText('Test Group')).toBeInTheDocument();
expect(getByText('Test Group Description')).toBeInTheDocument();
});
describe('group types messages', () => {
test('group type open message', () => {
const { getByLabelText, getByText } = renderComponent();
const expandButton = getByLabelText('Expand group editor');
expect(expandButton).toBeInTheDocument();
fireEvent.click(expandButton);
expect(getByText(messages.groupTypeOpenDescription.defaultMessage)).toBeInTheDocument();
});
test('group type public_managed message', () => {
const publicManagedGroupMock = {
id: '2',
name: 'Test Group',
description: 'Test Group Description',
type: 'public_managed',
maxTeamSize: 5,
};
const { getByLabelText, getByText } = renderComponent({ group: publicManagedGroupMock });
const expandButton = getByLabelText('Expand group editor');
expect(expandButton).toBeInTheDocument();
fireEvent.click(expandButton);
expect(getByText(messages.groupTypePublicManagedDescription.defaultMessage)).toBeInTheDocument();
});
test('group type private_managed message', () => {
const privateManagedGroupMock = {
id: '3',
name: 'Test Group',
description: 'Test Group Description',
type: 'private_managed',
maxTeamSize: 5,
};
const { getByLabelText, getByText } = renderComponent({ group: privateManagedGroupMock });
const expandButton = getByLabelText('Expand group editor');
expect(expandButton).toBeInTheDocument();
fireEvent.click(expandButton);
expect(getByText(messages.groupTypePrivateManagedDescription.defaultMessage)).toBeInTheDocument();
});
});
});

View File

@@ -1,25 +1,25 @@
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button, Form } from '@edx/paragon';
import { Add } from '@edx/paragon/icons';
import { Button, Form } from '@openedx/paragon';
import { Add } from '@openedx/paragon/icons';
import { FieldArray } from 'formik';
import PropTypes from 'prop-types';
import React from 'react';
import { v4 as uuid } from 'uuid';
import * as Yup from 'yup';
import { GroupTypes, TeamSizes } from '../../data/constants';
import FormikControl from '../../generic/FormikControl';
import { setupYupExtensions, useAppSetting } from '../../utils';
import AppSettingsModal from '../app-settings-modal/AppSettingsModal';
import { GroupTypes, TeamSizes } from 'CourseAuthoring/data/constants';
import FormikControl from 'CourseAuthoring/generic/FormikControl';
import { setupYupExtensions, useAppSetting } from 'CourseAuthoring/utils';
import AppSettingsModal from 'CourseAuthoring/pages-and-resources/app-settings-modal/AppSettingsModal';
import GroupEditor from './GroupEditor';
import messages from './messages';
setupYupExtensions();
function TeamSettings({
const TeamSettings = ({
intl,
onClose,
}) {
}) => {
const [teamsConfiguration, saveSettings] = useAppSetting('teamsConfiguration');
const blankNewGroup = {
name: '',
@@ -161,7 +161,7 @@ function TeamSettings({
}
</AppSettingsModal>
);
}
};
TeamSettings.propTypes = {
intl: intlShape.isRequired,

View File

@@ -93,6 +93,14 @@ const messages = defineMessages({
id: 'authoring.pagesAndResources.teams.group.types.open',
defaultMessage: 'Open',
},
groupTypeOpenManaged: {
id: 'authoring.pagesAndResources.teams.group.types.open_managed',
defaultMessage: 'Open managed',
},
groupTypeOpenManagedDescription: {
id: 'authoring.pagesAndResources.teams.group.types.open_managed.description',
defaultMessage: 'Only course staff can create teams. Learners can see, join and leave teams.',
},
groupTypeOpenDescription: {
id: 'authoring.pagesAndResources.teams.group.types.open.description',
defaultMessage: 'Learners can create, join, leave, and see other teams',

View File

@@ -0,0 +1,20 @@
{
"name": "@openedx-plugins/course-app-teams",
"version": "0.1.0",
"description": "Teams configuration for courses using it",
"peerDependencies": {
"@edx/frontend-app-authoring": "*",
"@edx/frontend-platform": "*",
"@openedx/paragon": "*",
"formik": "*",
"prop-types": "*",
"react": "*",
"uuid": "*",
"yup": "*"
},
"peerDependenciesMeta": {
"@edx/frontend-app-authoring": {
"optional": true
}
}
}

View File

@@ -0,0 +1,23 @@
/* eslint-disable import/prefer-default-export */
import { getConfig } from '@edx/frontend-platform';
import { GroupTypes } from 'CourseAuthoring/data/constants';
/**
* Check if a group type is enabled by the current configuration.
* This is a temporary workaround to disable the OPEN MANAGED team type until it is fully adopted.
* For more information, see: https://openedx.atlassian.net/wiki/spaces/COMM/pages/3885760525/Open+Managed+Group+Type
* @param {string} groupType - the group type to check
* @returns {boolean} - true if the group type is enabled
*/
export const isGroupTypeEnabled = (groupType) => {
const enabledTypesByDefault = [
GroupTypes.OPEN,
GroupTypes.PUBLIC_MANAGED,
GroupTypes.PRIVATE_MANAGED,
];
const enabledTypesByConfig = {
[GroupTypes.OPEN_MANAGED]: getConfig().ENABLE_OPEN_MANAGED_TEAM_TYPE,
};
return enabledTypesByDefault.includes(groupType) || enabledTypesByConfig[groupType] || false;
};

View File

@@ -0,0 +1,39 @@
import { getConfig } from '@edx/frontend-platform';
import { GroupTypes } from 'CourseAuthoring/data/constants';
import { isGroupTypeEnabled } from './utils';
jest.mock('@edx/frontend-platform', () => ({ getConfig: jest.fn() }));
describe('teams utils', () => {
describe('isGroupTypeEnabled', () => {
beforeEach(() => {
jest.clearAllMocks();
});
test('returns true if the group type is enabled', () => {
getConfig.mockReturnValue({ ENABLE_OPEN_MANAGED_TEAM_TYPE: false });
expect(isGroupTypeEnabled(GroupTypes.OPEN)).toBe(true);
expect(isGroupTypeEnabled(GroupTypes.PUBLIC_MANAGED)).toBe(true);
expect(isGroupTypeEnabled(GroupTypes.PRIVATE_MANAGED)).toBe(true);
});
test('returns false if the OPEN_MANAGED group is not enabled', () => {
getConfig.mockReturnValue({ ENABLE_OPEN_MANAGED_TEAM_TYPE: false });
expect(isGroupTypeEnabled(GroupTypes.OPEN_MANAGED)).toBe(false);
});
test('returns true if the OPEN_MANAGED group is enabled', () => {
getConfig.mockReturnValue({ ENABLE_OPEN_MANAGED_TEAM_TYPE: true });
expect(isGroupTypeEnabled(GroupTypes.OPEN_MANAGED)).toBe(true);
});
test('returns false if the group is invalid', () => {
getConfig.mockReturnValue({ ENABLE_OPEN_MANAGED_TEAM_TYPE: true });
expect(isGroupTypeEnabled('FOO')).toBe(false);
});
test('returns false if the group is null', () => {
getConfig.mockReturnValue({ ENABLE_OPEN_MANAGED_TEAM_TYPE: true });
expect(isGroupTypeEnabled(null)).toBe(false);
});
});
});

View File

@@ -3,12 +3,12 @@ import PropTypes from 'prop-types';
import React from 'react';
import * as Yup from 'yup';
import FormSwitchGroup from '../../generic/FormSwitchGroup';
import { useAppSetting } from '../../utils';
import AppSettingsModal from '../app-settings-modal/AppSettingsModal';
import FormSwitchGroup from 'CourseAuthoring/generic/FormSwitchGroup';
import { useAppSetting } from 'CourseAuthoring/utils';
import AppSettingsModal from 'CourseAuthoring/pages-and-resources/app-settings-modal/AppSettingsModal';
import messages from './messages';
function WikiSettings({ intl, onClose }) {
const WikiSettings = ({ intl, onClose }) => {
const [enablePublicWiki, saveSetting] = useAppSetting('allowPublicWikiAccess');
const handleSettingsSave = (values) => saveSetting(values.enablePublicWiki);
@@ -39,7 +39,7 @@ function WikiSettings({ intl, onClose }) {
}
</AppSettingsModal>
);
}
};
WikiSettings.propTypes = {
intl: intlShape.isRequired,

View File

@@ -26,8 +26,8 @@ const messages = defineMessages({
},
enablePublicWikiHelp: {
id: 'course-authoring.pages-resources.wiki.enable-public-wiki.help',
defaultMessage: `If enabled, edX users can view the course wiki even when
they're not enrolled in the course.`,
defaultMessage: `If enabled, any registered user can view the course wiki
even if they are not enrolled in the course`,
},
});

View File

@@ -0,0 +1,18 @@
{
"name": "@openedx-plugins/course-app-wiki",
"version": "0.1.0",
"description": "Wiki configuration for courses using it",
"peerDependencies": {
"@edx/frontend-app-authoring": "*",
"@edx/frontend-platform": "*",
"@openedx/paragon": "*",
"prop-types": "*",
"react": "*",
"yup": "*"
},
"peerDependenciesMeta": {
"@edx/frontend-app-authoring": {
"optional": true
}
}
}

View File

@@ -0,0 +1,4 @@
Xpert Unit Summaries Configuration Plugin
=========================================
Install this using ``npm install plugins/course-apps/xpert_unit_summary/ --no-save``.

View File

@@ -0,0 +1,45 @@
import React, { useCallback, useContext, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { PagesAndResourcesContext } from 'CourseAuthoring/pages-and-resources/PagesAndResourcesProvider';
import { useNavigate } from 'react-router-dom';
import SettingsModal from './settings-modal/SettingsModal';
import messages from './messages';
import { fetchXpertSettings } from './data/thunks';
const XpertUnitSummarySettings = ({ intl }) => {
const { path: pagesAndResourcesPath, courseId } = useContext(PagesAndResourcesContext);
const dispatch = useDispatch();
const navigate = useNavigate();
useEffect(() => {
dispatch(fetchXpertSettings(courseId));
}, [courseId]);
const handleClose = useCallback(() => {
navigate(pagesAndResourcesPath);
}, [pagesAndResourcesPath]);
return (
<SettingsModal
appId="xpert-unit-summary"
title={intl.formatMessage(messages.heading)}
enableAppHelp={intl.formatMessage(messages.enableXpertUnitSummaryHelp)}
helpPrivacyText={intl.formatMessage(messages.enableXpertUnitSummaryHelpPrivacyLink)}
enableAppLabel={intl.formatMessage(messages.enableXpertUnitSummaryLabel)}
learnMoreText={intl.formatMessage(messages.enableXpertUnitSummaryLink)}
allUnitsEnabledText={intl.formatMessage(messages.allUnitsEnabledByDefault)}
noUnitsEnabledText={intl.formatMessage(messages.noUnitsEnabledByDefault)}
onClose={handleClose}
/>
);
};
XpertUnitSummarySettings.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(XpertUnitSummarySettings);

View File

@@ -0,0 +1,281 @@
import ReactDOM from 'react-dom';
import React from 'react';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import {
getConfig, initializeMockApp, setConfig,
} from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { AppProvider, PageWrap } from '@edx/frontend-platform/react';
import {
queryByTestId, render, waitFor, getByText, fireEvent,
} from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';
import PagesAndResourcesProvider from 'CourseAuthoring/pages-and-resources/PagesAndResourcesProvider';
import initializeStore from 'CourseAuthoring/store';
import { executeThunk } from 'CourseAuthoring/utils';
import XpertUnitSummarySettings from './Settings';
import * as API from './data/api';
import * as Thunks from './data/thunks';
const courseId = 'course-v1:edX+TestX+Test_Course';
let axiosMock;
let store;
let container;
// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest.
ReactDOM.createPortal = jest.fn(node => node);
function renderComponent() {
const wrapper = render(
<AppProvider store={store} wrapWithRouter={false}>
<PagesAndResourcesProvider courseId={courseId}>
<MemoryRouter initialEntries={['/xpert-unit-summary/settings']}>
<Routes>
<Route
path="/xpert-unit-summary/settings"
element={<PageWrap><XpertUnitSummarySettings courseId={courseId} /></PageWrap>}
/>
<Route
path="/"
element={<PageWrap><div /></PageWrap>}
/>
</Routes>
</MemoryRouter>
</PagesAndResourcesProvider>
</AppProvider>,
);
container = wrapper.container;
}
function generateCourseLevelAPIResponse({
success, enabled,
}) {
return {
response: {
success, enabled,
},
};
}
describe('XpertUnitSummarySettings', () => {
beforeEach(() => {
setConfig({
...getConfig(),
BASE_URL: 'http://test.edx.org',
LMS_BASE_URL: 'http://lmstest.edx.org',
CMS_BASE_URL: 'http://cmstest.edx.org',
LOGIN_URL: 'http://support.edx.org/login',
LOGOUT_URL: 'http://support.edx.org/logout',
REFRESH_ACCESS_TOKEN_ENDPOINT: 'http://support.edx.org/access_token',
ACCESS_TOKEN_COOKIE_NAME: 'cookie',
CSRF_TOKEN_API_PATH: '/',
SUPPORT_URL: 'http://support.edx.org',
});
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore({
models: {
courseDetails: {
[courseId]: {
start: Date(),
},
},
},
});
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
});
describe('with successful network connections', () => {
beforeEach(() => {
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: true,
}));
renderComponent();
});
test('Shows switch on if enabled from backend', async () => {
expect(container.querySelector('#enable-xpert-unit-summary-toggle').checked).toBeTruthy();
expect(queryByTestId(container, 'enable-badge')).toBeTruthy();
});
test('Shows switch on if disabled from backend', async () => {
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: false,
}));
renderComponent();
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
expect(container.querySelector('#enable-xpert-unit-summary-toggle').checked).toBeTruthy();
expect(queryByTestId(container, 'enable-badge')).toBeTruthy();
});
test('Shows enable radio selected if enabled from backend', async () => {
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
expect(queryByTestId(container, 'enable-radio').checked).toBeTruthy();
});
test('Shows disable radio selected if enabled from backend', async () => {
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: false,
}));
renderComponent();
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
expect(queryByTestId(container, 'disable-radio').checked).toBeTruthy();
});
});
describe('first time course configuration', () => {
beforeEach(() => {
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
.reply(400, generateCourseLevelAPIResponse({
success: false,
enabled: undefined,
}));
renderComponent();
});
test('Does not show as enabled if configuration does not exist', async () => {
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
expect(container.querySelector('#enable-xpert-unit-summary-toggle').checked).not.toBeTruthy();
expect(queryByTestId(container, 'enable-badge')).not.toBeTruthy();
});
});
describe('saving configuration changes', () => {
beforeEach(() => {
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: false,
}));
axiosMock.onPost(API.getXpertSettingsUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: true,
}));
renderComponent();
});
test('Saving configuration changes', async () => {
jest.spyOn(API, 'postXpertSettings');
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
expect(queryByTestId(container, 'disable-radio').checked).toBeTruthy();
fireEvent.click(queryByTestId(container, 'enable-radio'));
fireEvent.click(getByText(container, 'Save'));
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
expect(API.postXpertSettings).toBeCalled();
});
});
describe('testing configurable gating', () => {
beforeEach(async () => {
axiosMock.onGet(API.getXpertConfigurationStatusUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: true,
}));
jest.spyOn(API, 'getXpertPluginConfigurable');
await executeThunk(Thunks.fetchXpertPluginConfigurable(courseId), store.dispatch);
renderComponent();
});
test('getting Xpert Plugin configurable status', () => {
expect(API.getXpertPluginConfigurable).toBeCalled();
});
});
describe('removing course configuration', () => {
beforeEach(() => {
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: true,
}));
axiosMock.onDelete(API.getXpertSettingsUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: undefined,
}));
renderComponent();
});
test('Deleting course configuration', async () => {
jest.spyOn(API, 'deleteXpertSettings');
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
fireEvent.click(container.querySelector('#enable-xpert-unit-summary-toggle'));
fireEvent.click(getByText(container, 'Save'));
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
expect(API.deleteXpertSettings).toBeCalled();
});
});
describe('resetting course units', () => {
test('reset all units to be enabled', async () => {
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: true,
}));
axiosMock.onPost(API.getXpertSettingsUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: true,
}));
renderComponent();
jest.spyOn(API, 'postXpertSettings');
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
fireEvent.click(queryByTestId(container, 'reset-units'));
expect(API.postXpertSettings).toBeCalledWith(courseId, { reset: true, enabled: true });
});
test('reset all units to be disabled', async () => {
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: false,
}));
axiosMock.onPost(API.getXpertSettingsUrl(courseId))
.reply(200, generateCourseLevelAPIResponse({
success: true,
enabled: false,
}));
renderComponent();
jest.spyOn(API, 'postXpertSettings');
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
fireEvent.click(queryByTestId(container, 'reset-units'));
expect(API.postXpertSettings).toBeCalledWith(courseId, { reset: true, enabled: false });
});
});
});

View File

@@ -0,0 +1,13 @@
export default {
id: 'xpert-unit-summary',
enabled: false,
name: 'Xpert unit summaries',
description: 'Use generative AI to summarize course content and reinforce learning.',
allowedOperations: {
enable: true,
configure: true,
},
documentationLinks: {
learnMoreConfiguration: 'https://openai.com/',
},
};

View File

@@ -0,0 +1,41 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
export function getXpertSettingsUrl(courseId) {
return `${getConfig().STUDIO_BASE_URL}/ai_aside/v1/${courseId}`;
}
export function getXpertConfigurationStatusUrl(courseId) {
return `${getConfig().STUDIO_BASE_URL}/ai_aside/v1/${courseId}/configurable`;
}
export async function getXpertSettings(courseId) {
const { data } = await getAuthenticatedHttpClient()
.get(getXpertSettingsUrl(courseId));
return data;
}
export async function postXpertSettings(courseId, state) {
const { data } = await getAuthenticatedHttpClient()
.post(getXpertSettingsUrl(courseId), {
enabled: state.enabled,
reset: state.reset,
});
return data;
}
export async function getXpertPluginConfigurable(courseId) {
const { data } = await getAuthenticatedHttpClient()
.get(getXpertConfigurationStatusUrl(courseId));
return data;
}
export async function deleteXpertSettings(courseId) {
const { data } = await getAuthenticatedHttpClient()
.delete(getXpertSettingsUrl(courseId));
return data;
}

View File

@@ -0,0 +1,113 @@
import { updateSavingStatus, updateLoadingStatus, updateResetStatus } from 'CourseAuthoring/pages-and-resources/data/slice';
import { RequestStatus } from 'CourseAuthoring/data/constants';
import { addModel, updateModel } from 'CourseAuthoring/generic/model-store';
import {
getXpertSettings, postXpertSettings, getXpertPluginConfigurable, deleteXpertSettings,
} from './api';
export function updateXpertSettings(courseId, state) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.IN_PROGRESS }));
try {
const { response } = await postXpertSettings(courseId, state);
const { success } = response;
if (success) {
dispatch(updateModel({ modelType: 'XpertSettings', model: { id: 'xpert-unit-summary', enabled: state.enabled } }));
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
}
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
return false;
} catch (error) {
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
return false;
}
};
}
export function fetchXpertPluginConfigurable(courseId) {
return async (dispatch) => {
let enabled;
dispatch(updateLoadingStatus({ status: RequestStatus.PENDING }));
try {
const { response } = await getXpertPluginConfigurable(courseId);
enabled = response?.enabled;
} catch (e) {
enabled = undefined;
}
dispatch(addModel({
modelType: 'XpertSettings.enabled',
model: {
id: 'xpert-unit-summary',
enabled,
},
}));
};
}
export function fetchXpertSettings(courseId) {
return async (dispatch) => {
let enabled;
dispatch(updateLoadingStatus({ status: RequestStatus.PENDING }));
try {
const { response } = await getXpertSettings(courseId);
enabled = response?.enabled;
} catch (e) {
enabled = undefined;
}
dispatch(addModel({
modelType: 'XpertSettings',
model: {
id: 'xpert-unit-summary',
enabled,
},
}));
dispatch(updateLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
};
}
export function removeXpertSettings(courseId) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
try {
const { response } = await deleteXpertSettings(courseId);
const { success } = response;
if (success) {
const model = { id: 'xpert-unit-summary', enabled: undefined };
dispatch(updateModel({ modelType: 'XpertSettings', model }));
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
}
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
return false;
} catch (error) {
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
return false;
}
};
}
export function resetXpertSettings(courseId, state) {
return async (dispatch) => {
dispatch(updateResetStatus({ status: RequestStatus.PENDING }));
try {
const { response } = await postXpertSettings(courseId, state);
const { success } = response;
if (success) {
dispatch(updateResetStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
}
dispatch(updateResetStatus({ status: RequestStatus.FAILED }));
return false;
} catch (error) {
dispatch(updateResetStatus({ status: RequestStatus.FAILED }));
return false;
}
};
}

View File

@@ -0,0 +1,34 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
heading: {
id: 'course-authoring.pages-resources.xpert-unit-summary.heading',
defaultMessage: 'Configure Xpert unit summaries',
},
enableXpertUnitSummaryLabel: {
id: 'course-authoring.pages-resources.xpert-unit-summary.enable-xpert-unit-summary.label',
defaultMessage: 'Xpert unit summaries',
},
enableXpertUnitSummaryHelp: {
id: 'course-authoring.pages-resources.xpert-unit-summary.enable-xpert-unit-summary.help',
defaultMessage: 'Reinforce learning concepts by sharing text-based course content with OpenAI (via API) to display unit summaries on-demand for learners. Learners can leave feedback about the quality of the AI-generated summaries for use by edX to improve the performance of the tool.',
},
enableXpertUnitSummaryHelpPrivacyLink: {
id: 'course-authoring.pages-resources.xpert-unit-summary.enable-xpert-unit-summary.help.privacylink',
defaultMessage: 'Learn more about OpenAI API data privacy.',
},
enableXpertUnitSummaryLink: {
id: 'course-authoring.pages-resources.xpert-unit-summary.enable-xpert-unit-summary.link',
defaultMessage: 'Learn more about how OpenAI handles data',
},
allUnitsEnabledByDefault: {
id: 'course-authoring.pages-resources.xpert-unit-summary.all-units-enabled-by-default',
defaultMessage: 'All units enabled by default',
},
noUnitsEnabledByDefault: {
id: 'course-authoring.pages-resources.xpert-unit-summary.no-units-enabled-by-default',
defaultMessage: 'No units enabled by default',
},
});
export default messages;

View File

@@ -0,0 +1,21 @@
{
"name": "@openedx-plugins/course-app-xpert_unit_summary",
"version": "0.1.0",
"description": "Xpert Unit Summaries configuration for courses using it",
"peerDependencies": {
"@edx/frontend-app-authoring": "*",
"@edx/frontend-platform": "*",
"@openedx/paragon": "*",
"formik": "*",
"prop-types": "*",
"yup": "*",
"react": "*",
"react-redux": "*",
"react-router-dom": "*"
},
"peerDependenciesMeta": {
"@edx/frontend-app-authoring": {
"optional": true
}
}
}

View File

@@ -0,0 +1,21 @@
const ResetIcon = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
role="img"
focusable="false"
aria-hidden="true"
transform="scale(-1,1)"
{...props}
>
<path
d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"
fill="currentColor"
/>
</svg>
);
export default ResetIcon;

View File

@@ -0,0 +1,453 @@
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
ActionRow,
Alert,
Badge,
Form,
Icon,
ModalDialog,
OverlayTrigger,
StatefulButton,
Tooltip,
TransitionReplace,
Hyperlink,
} from '@openedx/paragon';
import {
Info, CheckCircleOutline, SpinnerSimple,
} from '@openedx/paragon/icons';
import { Formik } from 'formik';
import PropTypes from 'prop-types';
import React, {
useContext, useEffect, useRef, useState,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import * as Yup from 'yup';
import { RequestStatus } from 'CourseAuthoring/data/constants';
import ConnectionErrorAlert from 'CourseAuthoring/generic/ConnectionErrorAlert';
import FormSwitchGroup from 'CourseAuthoring/generic/FormSwitchGroup';
import Loading from 'CourseAuthoring/generic/Loading';
import { useModel } from 'CourseAuthoring/generic/model-store';
import PermissionDeniedAlert from 'CourseAuthoring/generic/PermissionDeniedAlert';
import { useIsMobile } from 'CourseAuthoring/utils';
import { getLoadingStatus, getSavingStatus, getResetStatus } from 'CourseAuthoring/pages-and-resources/data/selectors';
import { updateSavingStatus, updateResetStatus } from 'CourseAuthoring/pages-and-resources/data/slice';
import AppConfigFormDivider from 'CourseAuthoring/pages-and-resources/discussions/app-config-form/apps/shared/AppConfigFormDivider';
import { PagesAndResourcesContext } from 'CourseAuthoring/pages-and-resources/PagesAndResourcesProvider';
import { updateXpertSettings, resetXpertSettings, removeXpertSettings } from '../data/thunks';
import messages from './messages';
import appInfo from '../appInfo';
import ResetIcon from './ResetIcon';
import './SettingsModal.scss';
const AppSettingsForm = ({
formikProps, children, showForm,
}) => children && (
<TransitionReplace>
{showForm ? (
<React.Fragment key="app-enabled">
{children(formikProps)}
</React.Fragment>
) : (
<React.Fragment key="app-disabled" />
)}
</TransitionReplace>
);
AppSettingsForm.propTypes = {
// Ignore the warning here since we're just passing along the props as-is and the child component should validate
// eslint-disable-next-line react/forbid-prop-types
formikProps: PropTypes.object.isRequired,
showForm: PropTypes.bool.isRequired,
children: PropTypes.func,
};
AppSettingsForm.defaultProps = {
children: null,
};
const SettingsModalBase = ({
intl, title, onClose, variant, isMobile, children, footer,
}) => (
<ModalDialog
title={title}
isOpen
onClose={onClose}
size="lg"
variant={variant}
hasCloseButton={isMobile}
isFullscreenOnMobile
>
<ModalDialog.Header>
<ModalDialog.Title data-testid="modal-title">
{title}
</ModalDialog.Title>
</ModalDialog.Header>
<ModalDialog.Body>
{children}
</ModalDialog.Body>
<ModalDialog.Footer className="p-4">
<ActionRow>
<ModalDialog.CloseButton variant="tertiary">
{intl.formatMessage(messages.cancel)}
</ModalDialog.CloseButton>
{footer}
</ActionRow>
</ModalDialog.Footer>
</ModalDialog>
);
SettingsModalBase.propTypes = {
intl: intlShape.isRequired,
title: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
variant: PropTypes.oneOf(['default', 'dark']).isRequired,
isMobile: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
footer: PropTypes.node,
};
SettingsModalBase.defaultProps = {
footer: null,
};
const ResetUnitsButton = ({
intl,
courseId,
checked,
visible,
}) => {
const resetStatusRequestStatus = useSelector(getResetStatus);
const dispatch = useDispatch();
useEffect(() => {
if (resetStatusRequestStatus === RequestStatus.SUCCESSFUL) {
setTimeout(() => {
dispatch(updateResetStatus({ status: '' }));
}, 2000);
}
}, [resetStatusRequestStatus]);
const handleResetUnits = () => {
dispatch(resetXpertSettings(courseId, { enabled: checked === 'true', reset: true }));
};
const getResetButtonState = () => {
switch (resetStatusRequestStatus) {
case RequestStatus.PENDING:
return 'pending';
case RequestStatus.SUCCESSFUL:
return 'finish';
default:
return 'default';
}
};
if (!visible) { return null; }
const messageKey = checked === 'true' ? 'resetAllUnitsTooltipChecked' : 'resetAllUnitsTooltipUnchecked';
return (
<OverlayTrigger
placement="right"
overlay={(
<Tooltip
id={`tooltip-reset-${checked}`}
className="reset-tooltip"
>
{intl.formatMessage(messages[messageKey])}
</Tooltip>
)}
>
<StatefulButton
className="reset-units-button"
labels={{
default: intl.formatMessage(messages.resetAllUnits),
pending: '',
finish: intl.formatMessage(messages.reset),
}}
icons={{
default: <Icon src={ResetIcon} />,
pending: <Icon src={SpinnerSimple} className="icon-spin" />,
finish: <Icon src={CheckCircleOutline} />,
}}
state={getResetButtonState()}
onClick={handleResetUnits}
disabledStates={['pending', 'finish']}
variant="outline"
data-testid="reset-units"
/>
</OverlayTrigger>
);
};
ResetUnitsButton.propTypes = {
intl: intlShape.isRequired,
courseId: PropTypes.string.isRequired,
checked: PropTypes.oneOf(['true', 'false']).isRequired,
visible: PropTypes.bool,
};
ResetUnitsButton.defaultProps = {
visible: false,
};
const SettingsModal = ({
intl,
appId,
title,
children,
configureBeforeEnable,
initialValues,
validationSchema,
onClose,
onSettingsSave,
enableAppLabel,
enableAppHelp,
learnMoreText,
helpPrivacyText,
enableReinitialize,
allUnitsEnabledText,
noUnitsEnabledText,
}) => {
const { courseId } = useContext(PagesAndResourcesContext);
const loadingStatus = useSelector(getLoadingStatus);
const updateSettingsRequestStatus = useSelector(getSavingStatus);
const alertRef = useRef(null);
const [saveError, setSaveError] = useState(false);
const dispatch = useDispatch();
const submitButtonState = updateSettingsRequestStatus === RequestStatus.IN_PROGRESS ? 'pending' : 'default';
const isMobile = useIsMobile();
const modalVariant = isMobile ? 'dark' : 'default';
const xpertSettings = useModel('XpertSettings', appId);
useEffect(() => {
if (updateSettingsRequestStatus === RequestStatus.SUCCESSFUL) {
dispatch(updateSavingStatus({ status: '' }));
onClose();
}
}, [updateSettingsRequestStatus]);
const handleFormSubmit = async ({ enabled, checked, ...rest }) => {
let success;
const values = { ...rest, enabled: enabled ? checked === 'true' : undefined };
if (enabled) {
success = await dispatch(updateXpertSettings(courseId, values));
} else {
success = await dispatch(removeXpertSettings(courseId));
}
if (onSettingsSave) {
success = success && await onSettingsSave(values);
}
setSaveError(!success);
!success && alertRef?.current.scrollIntoView(); // eslint-disable-line @typescript-eslint/no-unused-expressions
};
const handleFormikSubmit = ({ handleSubmit, errors }) => async (event) => {
// If submitting the form with errors, show the alert and scroll to it.
await handleSubmit(event);
if (Object.keys(errors).length > 0) {
setSaveError(true);
alertRef?.current.scrollIntoView?.(); // eslint-disable-line no-unused-expressions
}
};
const learnMoreLink = appInfo.documentationLinks?.learnMoreConfiguration && (
<div className="py-1">
<Hyperlink
className="text-primary-500"
destination={appInfo.documentationLinks.learnMoreConfiguration}
target="_blank"
rel="noreferrer noopener"
>
{learnMoreText}
</Hyperlink>
</div>
);
const helpPrivacyLink = (
<div className="py-1">
<Hyperlink
className="text-primary-500"
destination="https://openai.com/api-data-privacy"
target="_blank"
rel="noreferrer noopener"
>
{helpPrivacyText}
</Hyperlink>
</div>
);
if (loadingStatus === RequestStatus.SUCCESSFUL) {
return (
<Formik
initialValues={{
enabled: xpertSettings?.enabled !== undefined,
checked: xpertSettings?.enabled?.toString() || 'true',
...initialValues,
}}
validationSchema={
Yup.object()
.shape({
enabled: Yup.boolean(),
checked: Yup.string().oneOf(['true', 'false']),
...validationSchema,
})
}
onSubmit={handleFormSubmit}
enableReinitialize={enableReinitialize}
>
{(formikProps) => (
<Form onSubmit={handleFormikSubmit(formikProps)}>
<SettingsModalBase
title={title}
isOpen
onClose={onClose}
variant={modalVariant}
isMobile={isMobile}
isFullscreenOnMobile
intl={intl}
footer={(
<StatefulButton
labels={{
default: intl.formatMessage(messages.save),
pending: intl.formatMessage(messages.saving),
complete: intl.formatMessage(messages.saved),
}}
state={submitButtonState}
onClick={handleFormikSubmit(formikProps)}
disabled={!formikProps.dirty}
/>
)}
>
{saveError && (
<Alert variant="danger" icon={Info} ref={alertRef}>
<Alert.Heading>
{formikProps.errors.enabled?.title || intl.formatMessage(messages.errorSavingTitle)}
</Alert.Heading>
{formikProps.errors.enabled?.message || intl.formatMessage(messages.errorSavingMessage)}
</Alert>
)}
<FormSwitchGroup
id={`enable-${appId}-toggle`}
name="enabled"
onChange={formikProps.handleChange}
onBlur={formikProps.handleBlur}
checked={formikProps.values.enabled}
label={(
<div className="d-flex align-items-center">
{enableAppLabel}
{formikProps.values.enabled && (
<Badge className="ml-2" variant="success" data-testid="enable-badge">
{intl.formatMessage(messages.enabled)}
</Badge>
)}
</div>
)}
helpText={(
<div>
<p>{enableAppHelp}</p>
{helpPrivacyLink}
{learnMoreLink}
</div>
)}
/>
{(formikProps.values.enabled || configureBeforeEnable) && (
<Form.RadioSet
name="checked"
onChange={formikProps.handleChange}
onBlur={formikProps.handleBlur}
value={formikProps.values.checked}
>
<Form.Radio
className="summary-radio m-2 px-3"
data-testid="enable-radio"
value="true"
>
{allUnitsEnabledText}
<ResetUnitsButton
intl={intl}
courseId={courseId}
checked={formikProps.values.checked}
visible={formikProps.values.checked === 'true'}
/>
</Form.Radio>
<Form.Radio
className="summary-radio m-2 px-3"
data-testid="disable-radio"
value="false"
>
{noUnitsEnabledText}
<ResetUnitsButton
intl={intl}
courseId={courseId}
checked={formikProps.values.checked}
visible={formikProps.values.checked === 'false'}
/>
</Form.Radio>
</Form.RadioSet>
)}
{(formikProps.values.enabled || configureBeforeEnable) && children
&& <AppConfigFormDivider marginAdj={{ default: 0, sm: 0 }} />}
<AppSettingsForm formikProps={formikProps} showForm={formikProps.values.enabled || configureBeforeEnable}>
{children}
</AppSettingsForm>
</SettingsModalBase>
</Form>
)}
</Formik>
);
}
return (
<SettingsModalBase
intl={intl}
title={title}
isOpen
onClose={onClose}
size="sm"
variant={modalVariant}
isMobile={isMobile}
isFullscreenOnMobile
>
{loadingStatus === RequestStatus.IN_PROGRESS && <Loading />}
{loadingStatus === RequestStatus.FAILED && <ConnectionErrorAlert />}
{loadingStatus === RequestStatus.DENIED && <PermissionDeniedAlert />}
</SettingsModalBase>
);
};
SettingsModal.propTypes = {
intl: intlShape.isRequired,
title: PropTypes.string.isRequired,
appId: PropTypes.string.isRequired,
children: PropTypes.func,
onSettingsSave: PropTypes.func,
initialValues: PropTypes.shape({}),
validationSchema: PropTypes.shape({}),
onClose: PropTypes.func.isRequired,
enableAppLabel: PropTypes.string.isRequired,
enableAppHelp: PropTypes.string.isRequired,
learnMoreText: PropTypes.string.isRequired,
helpPrivacyText: PropTypes.string.isRequired,
allUnitsEnabledText: PropTypes.string.isRequired,
noUnitsEnabledText: PropTypes.string.isRequired,
configureBeforeEnable: PropTypes.bool,
enableReinitialize: PropTypes.bool,
};
SettingsModal.defaultProps = {
children: null,
onSettingsSave: null,
initialValues: {},
validationSchema: {},
configureBeforeEnable: false,
enableReinitialize: false,
};
export default injectIntl(SettingsModal);

View File

@@ -0,0 +1,45 @@
@import "~@edx/brand/paragon/variables";
@import "~@openedx/paragon/scss/core/utilities-only";
.summary-radio {
display: flex;
align-items: center;
width: 100%;
border-width: $border-width;
border-color: $border-color;
border-radius: $border-radius;
border-style: solid;
&:has(input:checked) {
border-width: 3px;
border-color: theme-color("primary");
}
> div {
flex: 1;
> label {
min-height: 80px;
flex-wrap: wrap;
justify-content: space-between;
}
}
}
.reset-units-button {
color: $link-color;
border-width: $border-width;
border-color: $border-color;
border-radius: $border-radius;
border-style: solid;
}
.reset-tooltip {
.arrow::before {
border-right-color: #00262B;
}
.tooltip-inner {
background-color: #00262B;
}
}

View File

@@ -0,0 +1,58 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
cancel: {
id: 'course-authoring.pages-resources.app-settings-modal.button.cancel',
defaultMessage: 'Cancel',
},
save: {
id: 'course-authoring.pages-resources.app-settings-modal.button.save',
defaultMessage: 'Save',
},
saving: {
id: 'course-authoring.pages-resources.app-settings-modal.button.saving',
defaultMessage: 'Saving',
},
saved: {
id: 'course-authoring.pages-resources.app-settings-modal.button.saved',
defaultMessage: 'Saved',
},
retry: {
id: 'course-authoring.pages-resources.app-settings-modal.button.retry',
defaultMessage: 'Retry',
},
enabled: {
id: 'course-authoring.pages-resources.app-settings-modal.badge.enabled',
defaultMessage: 'Enabled',
},
disabled: {
id: 'course-authoring.pages-resources.app-settings-modal.badge.disabled',
defaultMessage: 'Disabled',
},
resetAllUnits: {
id: 'course-authoring.pages-resources.app-settings-modal.reset-all-units',
defaultMessage: 'Reset all units',
},
resetAllUnitsTooltipChecked: {
id: 'course-authoring.pages-resources.app-settings-modal.reset-all-units-tooltip.checked',
defaultMessage: 'Immediately reset any unit-level changes and checked "Enable summaries" on all units.',
},
resetAllUnitsTooltipUnchecked: {
id: 'course-authoring.pages-resources.app-settings-modal.reset-all-units-tooltip.unchecked',
defaultMessage: 'Immediately reset any unit-level changes and unchecked "Enable summaries" on all units.',
},
reset: {
id: 'course-authoring.pages-resources.app-settings-modal.reset',
defaultMessage: 'Reset',
},
errorSavingTitle: {
id: 'course-authoring.pages-resources.app-settings-modal.save-error.title',
defaultMessage: 'We couldn\'t apply your changes.',
},
errorSavingMessage: {
id: 'course-authoring.pages-resources.app-settings-modal.save-error.message',
defaultMessage: 'Please check your entries and try again.',
},
});
export default messages;

View File

@@ -1,10 +1,10 @@
<!doctype html>
<html lang="en-us">
<head>
<title>Course Authoring | edX</title>
<title>Course Authoring | <%= process.env.SITE_NAME %></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="<%= process.env.FAVICON_URL %>" type="image/x-icon" />
<link rel="shortcut icon" href="<%=htmlWebpackPlugin.options.FAVICON_URL%>" type="image/x-icon" />
</head>
<body>
<div id="root"></div>

View File

@@ -1,19 +1,24 @@
{
"extends": [
"config:base",
"schedule:daily",
"schedule:weekly",
":rebaseStalePrs",
":semanticCommits"
":semanticCommits",
":dependencyDashboard"
],
"timezone": "America/New_York",
"patch": {
"automerge": true
"automerge": false
},
"rebaseStalePrs": true,
"packageRules": [
{
"matchPackagePatterns": ["@edx"],
"extends": [
"schedule:daily"
],
"matchPackagePatterns": ["@edx", "@openedx"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
"automerge": false
}
]
}

View File

@@ -1,67 +1,74 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import Footer from '@edx/frontend-component-footer';
import { useDispatch, useSelector } from 'react-redux';
import {
useLocation,
} from 'react-router-dom';
import Header from './studio-header/Header';
import { StudioFooter } from '@edx/frontend-component-footer';
import Header from './header';
import { fetchCourseDetail } from './data/thunks';
import { useModel } from './generic/model-store';
import NotFoundAlert from './generic/NotFoundAlert';
import PermissionDeniedAlert from './generic/PermissionDeniedAlert';
import { getCourseAppsApiStatus, getLoadingStatus } from './pages-and-resources/data/selectors';
import { fetchStudioHomeData } from './studio-home/data/thunks';
import { getCourseAppsApiStatus } from './pages-and-resources/data/selectors';
import { RequestStatus } from './data/constants';
import Loading from './generic/Loading';
export default function CourseAuthoringPage({ courseId, children }) {
const CourseAuthoringPage = ({ courseId, children }) => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchCourseDetail(courseId));
}, [courseId]);
useEffect(() => {
dispatch(fetchStudioHomeData());
}, []);
const courseDetail = useModel('courseDetails', courseId);
const courseNumber = courseDetail ? courseDetail.number : null;
const courseOrg = courseDetail ? courseDetail.org : null;
const courseTitle = courseDetail ? courseDetail.name : courseId;
const courseAppsApiStatus = useSelector(getCourseAppsApiStatus);
const inProgress = useSelector(getLoadingStatus) === RequestStatus.IN_PROGRESS;
const courseDetailStatus = useSelector(state => state.courseDetail.status);
const inProgress = courseDetailStatus === RequestStatus.IN_PROGRESS;
const { pathname } = useLocation();
const isEditor = pathname.includes('/editor');
if (courseDetailStatus === RequestStatus.NOT_FOUND && !isEditor) {
return (
<NotFoundAlert />
);
}
if (courseAppsApiStatus === RequestStatus.DENIED) {
return (
<PermissionDeniedAlert />
);
}
const AppHeader = () => (
<Header
courseNumber={courseNumber}
courseOrg={courseOrg}
courseTitle={courseTitle}
courseId={courseId}
/>
);
const AppFooter = () => (
<div className="mt-6">
<Footer />
</div>
);
return (
<div className="bg-light-200">
{/* While V2 Editors are tempoarily served from thier own pages
<div>
{/* While V2 Editors are temporarily served from their own pages
using url pattern containing /editor/,
we shouldn't have the header and footer on these pages.
This functionality will be removed in TNL-9591 */}
{inProgress ? !pathname.includes('/editor/') && <Loading /> : <AppHeader />}
{inProgress ? !isEditor && <Loading />
: (!isEditor && (
<Header
number={courseNumber}
org={courseOrg}
title={courseTitle}
contextId={courseId}
/>
)
)}
{children}
{!inProgress && <AppFooter />}
{!inProgress && !isEditor && <StudioFooter />}
</div>
);
}
};
CourseAuthoringPage.propTypes = {
children: PropTypes.node,
@@ -71,3 +78,5 @@ CourseAuthoringPage.propTypes = {
CourseAuthoringPage.defaultProps = {
children: null,
};
export default CourseAuthoringPage;

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { queryByTestId, render } from '@testing-library/react';
import { render } from '@testing-library/react';
import { getConfig, initializeMockApp } from '@edx/frontend-platform';
import MockAdapter from 'axios-mock-adapter';
@@ -12,6 +12,7 @@ import CourseAuthoringPage from './CourseAuthoringPage';
import PagesAndResources from './pages-and-resources/PagesAndResources';
import { executeThunk } from './utils';
import { fetchCourseApps } from './pages-and-resources/data/thunks';
import { fetchCourseDetail } from './data/thunks';
const courseId = 'course-v1:edX+TestX+Test_Course';
let mockPathname = '/evilguy/';
@@ -23,50 +24,18 @@ jest.mock('react-router-dom', () => ({
}));
let axiosMock;
let store;
let container;
function renderComponent() {
const wrapper = render(
<AppProvider store={store}>
<IntlProvider locale="en">
<CourseAuthoringPage courseId={courseId}>
<PagesAndResources courseId={courseId} />
</CourseAuthoringPage>
</IntlProvider>
</AppProvider>
,
);
container = wrapper.container;
}
const mockStore = async () => {
const apiBaseUrl = getConfig().STUDIO_BASE_URL;
const courseAppsApiUrl = `${apiBaseUrl}/api/course_apps/v1/apps`;
axiosMock.onGet(`${courseAppsApiUrl}/${courseId}`).reply(403, {
response: { status: 403 },
});
await executeThunk(fetchCourseApps(courseId), store.dispatch);
};
describe('DiscussionsSettings', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
});
test('renders permission error in case of 403', async () => {
await mockStore();
renderComponent();
expect(queryByTestId(container, 'permissionDeniedAlert')).toBeInTheDocument();
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
});
describe('Editor Pages Load no header', () => {
@@ -78,18 +47,6 @@ describe('Editor Pages Load no header', () => {
});
await executeThunk(fetchCourseApps(courseId), store.dispatch);
};
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
});
test('renders no loading wheel on editor pages', async () => {
mockPathname = '/editor/';
await mockStoreSuccess();
@@ -121,3 +78,56 @@ describe('Editor Pages Load no header', () => {
expect(wrapper.queryByRole('status')).toBeInTheDocument();
});
});
describe('Course authoring page', () => {
const lmsApiBaseUrl = getConfig().LMS_BASE_URL;
const courseDetailApiUrl = `${lmsApiBaseUrl}/api/courses/v1/courses`;
const mockStoreNotFound = async () => {
axiosMock.onGet(
`${courseDetailApiUrl}/${courseId}?username=abc123`,
).reply(404, {
response: { status: 404 },
});
await executeThunk(fetchCourseDetail(courseId), store.dispatch);
};
const mockStoreError = async () => {
axiosMock.onGet(
`${courseDetailApiUrl}/${courseId}?username=abc123`,
).reply(500, {
response: { status: 500 },
});
await executeThunk(fetchCourseDetail(courseId), store.dispatch);
};
test('renders not found page on non-existent course key', async () => {
await mockStoreNotFound();
const wrapper = render(
<AppProvider store={store}>
<IntlProvider locale="en">
<CourseAuthoringPage courseId={courseId} />
</IntlProvider>
</AppProvider>
,
);
expect(await wrapper.findByTestId('notFoundAlert')).toBeInTheDocument();
});
test('does not render not found page on other kinds of error', async () => {
await mockStoreError();
// Currently, loading errors are not handled, so we wait for the child
// content to be rendered -which happens when request status is no longer
// IN_PROGRESS but also not NOT_FOUND or DENIED- then check that the not
// found alert is not present.
const contentTestId = 'courseAuthoringPageContent';
const wrapper = render(
<AppProvider store={store}>
<IntlProvider locale="en">
<CourseAuthoringPage courseId={courseId}>
<div data-testid={contentTestId} />
</CourseAuthoringPage>
</IntlProvider>
</AppProvider>
,
);
expect(await wrapper.findByTestId(contentTestId)).toBeInTheDocument();
expect(wrapper.queryByTestId('notFoundAlert')).not.toBeInTheDocument();
});
});

View File

@@ -1,11 +1,29 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Switch, useRouteMatch } from 'react-router';
import { PageRoute } from '@edx/frontend-platform/react';
import {
Navigate, Routes, Route, useParams,
} from 'react-router-dom';
import { getConfig } from '@edx/frontend-platform';
import { PageWrap } from '@edx/frontend-platform/react';
import { Textbooks } from 'CourseAuthoring/textbooks';
import CourseAuthoringPage from './CourseAuthoringPage';
import { PagesAndResources } from './pages-and-resources';
import ProctoredExamSettings from './proctored-exam-settings/ProctoredExamSettings';
import EditorContainer from './editors/EditorContainer';
import VideoSelectorContainer from './selectors/VideoSelectorContainer';
import CustomPages from './custom-pages';
import { FilesPage, VideosPage } from './files-and-videos';
import { AdvancedSettings } from './advanced-settings';
import { CourseOutline } from './course-outline';
import ScheduleAndDetails from './schedule-and-details';
import { GradingSettings } from './grading-settings';
import CourseTeam from './course-team/CourseTeam';
import { CourseUpdates } from './course-updates';
import { CourseUnit } from './course-unit';
import { Certificates } from './certificates';
import CourseExportPage from './export-page/CourseExportPage';
import CourseImportPage from './import-page/CourseImportPage';
import { DECODED_ROUTES } from './constants';
import CourseChecklist from './course-checklist';
import GroupConfigurations from './group-configurations';
/**
* As of this writing, these routes are mounted at a path prefixed with the following:
@@ -23,30 +41,98 @@ import EditorContainer from './editors/EditorContainer';
* can move the Header/Footer rendering to this component and likely pull the course detail loading
* in as well, and it'd feel a bit better-factored and the roles would feel more clear.
*/
export default function CourseAuthoringRoutes({ courseId }) {
const { path } = useRouteMatch();
const CourseAuthoringRoutes = () => {
const { courseId } = useParams();
return (
<CourseAuthoringPage courseId={courseId}>
<Switch>
<PageRoute path={`${path}/pages-and-resources`}>
<PagesAndResources courseId={courseId} />
</PageRoute>
<PageRoute path={`${path}/proctored-exam-settings`}>
<ProctoredExamSettings courseId={courseId} />
</PageRoute>
<PageRoute path={`${path}/editor/:blockType/:blockId`}>
{process.env.ENABLE_NEW_EDITOR_PAGES === 'true'
&& (
<EditorContainer
courseId={courseId}
/>
)}
</PageRoute>
</Switch>
<Routes>
<Route
path="/"
element={<PageWrap><CourseOutline courseId={courseId} /></PageWrap>}
/>
<Route
path="course_info"
element={<PageWrap><CourseUpdates courseId={courseId} /></PageWrap>}
/>
<Route
path="assets"
element={<PageWrap><FilesPage courseId={courseId} /></PageWrap>}
/>
<Route
path="videos"
element={getConfig().ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN === 'true' ? <PageWrap><VideosPage courseId={courseId} /></PageWrap> : null}
/>
<Route
path="pages-and-resources/*"
element={<PageWrap><PagesAndResources courseId={courseId} /></PageWrap>}
/>
<Route
path="proctored-exam-settings"
element={<Navigate replace to={`/course/${courseId}/pages-and-resources`} />}
/>
<Route
path="custom-pages/*"
element={<PageWrap><CustomPages courseId={courseId} /></PageWrap>}
/>
{DECODED_ROUTES.COURSE_UNIT.map((path) => (
<Route
key={path}
path={path}
element={<PageWrap><CourseUnit courseId={courseId} /></PageWrap>}
/>
))}
<Route
path="editor/course-videos/:blockId"
element={<PageWrap><VideoSelectorContainer courseId={courseId} /></PageWrap>}
/>
<Route
path="editor/:blockType/:blockId?"
element={<PageWrap><EditorContainer learningContextId={courseId} /></PageWrap>}
/>
<Route
path="settings/details"
element={<PageWrap><ScheduleAndDetails courseId={courseId} /></PageWrap>}
/>
<Route
path="settings/grading"
element={<PageWrap><GradingSettings courseId={courseId} /></PageWrap>}
/>
<Route
path="course_team"
element={<PageWrap><CourseTeam courseId={courseId} /></PageWrap>}
/>
<Route
path="group_configurations"
element={<PageWrap><GroupConfigurations courseId={courseId} /></PageWrap>}
/>
<Route
path="settings/advanced"
element={<PageWrap><AdvancedSettings courseId={courseId} /></PageWrap>}
/>
<Route
path="import"
element={<PageWrap><CourseImportPage courseId={courseId} /></PageWrap>}
/>
<Route
path="export"
element={<PageWrap><CourseExportPage courseId={courseId} /></PageWrap>}
/>
<Route
path="checklists"
element={<PageWrap><CourseChecklist courseId={courseId} /></PageWrap>}
/>
<Route
path="certificates"
element={getConfig().ENABLE_CERTIFICATE_PAGE === 'true' ? <PageWrap><Certificates courseId={courseId} /></PageWrap> : null}
/>
<Route
path="textbooks"
element={<PageWrap><Textbooks courseId={courseId} /></PageWrap>}
/>
</Routes>
</CourseAuthoringPage>
);
}
CourseAuthoringRoutes.propTypes = {
courseId: PropTypes.string.isRequired,
};
export default CourseAuthoringRoutes;

View File

@@ -0,0 +1,117 @@
import React from 'react';
import { AppProvider } from '@edx/frontend-platform/react';
import { initializeMockApp } from '@edx/frontend-platform';
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import CourseAuthoringRoutes from './CourseAuthoringRoutes';
import initializeStore from './store';
const courseId = 'course-v1:edX+TestX+Test_Course';
const pagesAndResourcesMockText = 'Pages And Resources';
const editorContainerMockText = 'Editor Container';
const videoSelectorContainerMockText = 'Video Selector Container';
const customPagesMockText = 'Custom Pages';
let store;
const mockComponentFn = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: () => ({
courseId,
}),
}));
// Mock the TinyMceWidget
jest.mock('./editors/sharedComponents/TinyMceWidget', () => ({
__esModule: true, // Required to mock a default export
default: () => <div>Widget</div>,
Footer: () => <div>Footer</div>,
prepareEditorRef: jest.fn(() => ({
refReady: true,
setEditorRef: jest.fn().mockName('prepareEditorRef.setEditorRef'),
})),
}));
jest.mock('./pages-and-resources/PagesAndResources', () => (props) => {
mockComponentFn(props);
return pagesAndResourcesMockText;
});
jest.mock('./editors/EditorContainer', () => (props) => {
mockComponentFn(props);
return editorContainerMockText;
});
jest.mock('./selectors/VideoSelectorContainer', () => (props) => {
mockComponentFn(props);
return videoSelectorContainerMockText;
});
jest.mock('./custom-pages/CustomPages', () => (props) => {
mockComponentFn(props);
return customPagesMockText;
});
describe('<CourseAuthoringRoutes>', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
});
fit('renders the PagesAndResources component when the pages and resources route is active', () => {
render(
<AppProvider store={store} wrapWithRouter={false}>
<MemoryRouter initialEntries={['/pages-and-resources']}>
<CourseAuthoringRoutes />
</MemoryRouter>
</AppProvider>,
);
expect(screen.getByText(pagesAndResourcesMockText)).toBeVisible();
expect(mockComponentFn).toHaveBeenCalledWith(
expect.objectContaining({
courseId,
}),
);
});
it('renders the EditorContainer component when the course editor route is active', () => {
render(
<AppProvider store={store} wrapWithRouter={false}>
<MemoryRouter initialEntries={['/editor/video/block-id']}>
<CourseAuthoringRoutes />
</MemoryRouter>
</AppProvider>,
);
expect(screen.queryByText(editorContainerMockText)).toBeInTheDocument();
expect(screen.queryByText(pagesAndResourcesMockText)).not.toBeInTheDocument();
expect(mockComponentFn).toHaveBeenCalledWith(
expect.objectContaining({
courseId,
}),
);
});
it('renders the VideoSelectorContainer component when the course videos route is active', () => {
render(
<AppProvider store={store} wrapWithRouter={false}>
<MemoryRouter initialEntries={['/editor/course-videos/block-id']}>
<CourseAuthoringRoutes />
</MemoryRouter>
</AppProvider>,
);
expect(screen.queryByText(videoSelectorContainerMockText)).toBeInTheDocument();
expect(screen.queryByText(pagesAndResourcesMockText)).not.toBeInTheDocument();
expect(mockComponentFn).toHaveBeenCalledWith(
expect.objectContaining({
courseId,
}),
);
});
});

View File

@@ -0,0 +1,16 @@
export default {
content: {
id: 67,
userId: 3,
created: '2024-01-16T13:09:11.540615Z',
purpose: 'clipboard',
status: 'ready',
blockType: 'vertical',
blockTypeDisplay: 'Unit',
olxUrl: 'http://localhost:18010/api/content-staging/v1/staged-content/67/olx',
displayName: 'Introduction: Video and Sequences',
},
sourceUsageKey: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_0270f6de40fc',
sourceContextTitle: 'Demonstration Course',
sourceEditUrl: 'http://localhost:18010/container/block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_0270f6de40fc',
};

View File

@@ -0,0 +1,16 @@
export default {
content: {
id: 69,
userId: 3,
created: '2024-01-16T13:33:21.314439Z',
purpose: 'clipboard',
status: 'ready',
blockType: 'html',
blockTypeDisplay: 'Text',
olxUrl: 'http://localhost:18010/api/content-staging/v1/staged-content/69/olx',
displayName: 'Blank HTML Page',
},
sourceUsageKey: 'block-v1:edX+DemoX+Demo_Course+type@html+block@html1',
sourceContextTitle: 'Demonstration Course',
sourceEditUrl: 'http://localhost:18010/container/block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical1',
};

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