Port latest master to frontend-base (incl. Redux-to-React-Query migration)#1641
Port latest master to frontend-base (incl. Redux-to-React-Query migration)#1641arbrandes wants to merge 17 commits intoopenedx:frontend-basefrom
Conversation
Port of master commit 58ec90a. Co-Authored-By: Adolfo R. Brandes <adolfo@axim.org> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port of master commit ffb8a2d. Co-Authored-By: Kyrylo Hudym-Levkovych <kyr.hudym@kyrs-MacBook-Pro.local> Co-Authored-By: Adolfo R. Brandes <adolfo@axim.org> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This change adds a Slot for the login page allowing it to be customised. Since this touched the Login Page, LoginPage and Logistration have also been refactored to move away from redux connect. Adapted for frontend-base: uses Slot from @openedx/frontend-base instead of PluginSlot from @openedx/frontend-plugin-framework, slot files live under src/slots/, and the slot ID follows the frontend-base naming convention (org.openedx.frontend.slot.authn.loginComponent.v1). Co-Authored-By: Adolfo R. Brandes <adolfo@axim.org> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Adolfo R. Brandes <adolfo@axim.org> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Artur Filippovskii <118079918+filippovskii09@users.noreply.github.com> Co-Authored-By: Adolfo R. Brandes <adolfo@axim.org> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Redux + Redux-Saga with React Query (useMutation/useQuery) for server state and React Context for UI/form state across all modules: login, registration, forgot-password, reset-password, progressive- profiling, and common-components. Port of master commits 0d709d1 and 93bd0f2, adapted for @openedx/frontend-base: - getSiteConfig() instead of getConfig() - useAppConfig() for per-app configuration - @tanstack/react-query as peerDependency (shell provides QueryClient) - CurrentAppProvider instead of AppProvider Also fixes EnvironmentTypes circular dependency in site.config.test.tsx by using string literal instead of enum import. Co-Authored-By: Jesus Balderrama <jesus.balderrama.wgu@gmail.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…gestionClick The function expects two string arguments (suggestion, type) but was being called with a single object, corrupting the email suggestion state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Calling navigate() during the render phase triggers state updates in the router, causing React warnings and potential infinite render loops in strict mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…losure validateInput was directly mutating the formErrors state object and then spreading it. When handleSubmit called validateInput twice in succession, the second call operated on stale closure values because React batches state updates. Also fixed handleOnFocus for the same issue. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When shouldRedirect was true and welcomePageContext.nextUrl was truthy, both RedirectLogistration components rendered simultaneously, causing a double redirect attempt. The first block was a subset of the second, so removing it is sufficient. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…_ERROR code LoginContext setFormFields/setErrors are useState setters that accept updater functions, so their types should be Dispatch<SetStateAction<...>> rather than plain function signatures. PASSWORD_RESET_ERROR was checked in ResetPasswordPage but no code path ever set status to that value, making the conditions dead code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The TPA context is effectively static per session. Adding a 5-minute staleTime prevents unnecessary background refetches when navigating between login/register tabs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The query key only included pageId, so if the payload (tpa_hint, query params, etc.) changed while pageId stayed the same, React Query would serve stale cached data. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
It was being called on every data sync effect run, causing a flash of PENDING state even when TPA data was already available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In ProgressiveProfiling, the TPA query fired even when registrationEmbedded was false and the result was ignored. Added an enabled option to useThirdPartyAuthHook and set it to registrationEmbedded in ProgressiveProfiling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## frontend-base #1641 +/- ##
================================================
Coverage ? 92.46%
================================================
Files ? 92
Lines ? 2018
Branches ? 573
================================================
Hits ? 1866
Misses ? 149
Partials ? 3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| setFormErrors(''); | ||
| sendForgotPassword(email, { | ||
| onSuccess: (data, emailUsed) => { | ||
| setStatus('complete'); |
There was a problem hiding this comment.
I know this hardcoded status was already here, but maybe is worth it to import the value from constants.
also in the onError
|
Review assisted by @claude (I used claude to talk through changes, record my thoughts, keep track of where I was throughout the review process, and assist in writing this summary) Reviewed all 17 commits commit-by-commit, comparing each against the master commit(s) it ports. The full review log is in the details block below. OverallThe migration is solid. The Redux→React Query/Context rewrite (commit 8) is the bulk of the change and is largely correct. Commits 9–17 are focused fixes and improvements with no master equivalents — most of them address real bugs or performance issues that also exist on master and are worth porting back. A few things in the PR need follow-up attention, noted below. Follow-ups for this PRDefinitely fix
Worth investigating / is this intentional?
Nits
Items to port back to masterThese bugs/improvements were caught or introduced during the frontend-base port and don't yet exist on master:
Pre-existing gaps (not introduced by this PR)
|
| # | PR Commit SHA | Headline | Master Commit(s) | Status |
|---|---|---|---|---|
| 1 | d5b1df7c |
chore: bump frontend-base to 1.0.0-alpha.14 | (frontend-base-only, no master equiv) | ✅ |
| 2 | b0ee418a |
refactor: replace injectIntl with useIntl | db3d007c, 43a584eb, 5bd6926f, 76e400f0 |
✅ |
| 3 | 046abb77 |
chore: handle forbidden username exceptions on registration | 58ec90ac |
✅ |
| 4 | 80ce7e8c |
fix: username suggestions alignment | ffb8a2d4 |
✅ |
| 5 | 8d32c37e |
feat: add Slot for login page | 0418a04f |
✅ |
| 6 | 00423433 |
fix: add missing space symbol to the 'additional.help.text' field | 4cb79223 |
✅ |
| 7 | 99a193a6 |
fix: adding value length check for full name field | 642853e0 |
✅ |
Group B — Redux→React Query migration (commit 8)
| # | PR Commit SHA | Headline | Master Commit(s) | Status |
|---|---|---|---|---|
| 8 | 2bfa6447 |
feat: migrate from Redux to React Query and React Context | 0d709d15, 93bd0f24 |
✅ |
Group C — Bug fixes found during port review (commits 9–12)
These have no master equivalents.
| # | PR Commit SHA | Headline | Status |
|---|---|---|---|
| 9 | 55b6eb3e |
fix: pass correct arguments to setEmailSuggestionContext | ✅ |
| 10 | 119780a5 |
fix: move navigate() calls from render to useEffect in ResetPasswordPage | ✅ |
| 11 | 06135925 |
fix: use functional state updater in validateInput to prevent stale closure | ✅ |
| 12 | 5f13df70 |
fix: remove duplicate RedirectLogistration in ProgressiveProfiling | ✅ |
Group D — React Query improvements (commits 13–17)
No master equivalents; performance and correctness improvements to the TPA query hook and LoginContext.
| # | PR Commit SHA | Headline | Status |
|---|---|---|---|
| 13 | 537c47b7 |
fix: correct LoginContext setter types and remove dead PASSWORD_RESET_ERROR code | ✅ |
| 14 | ad8afe7b |
perf: add staleTime to third-party auth context query | ✅ |
| 15 | cecbd7c5 |
fix: include payload in TPA query key to prevent stale cache | ✅ |
| 16 | 0a787708 |
fix: call setThirdPartyAuthContextBegin only on mount in LoginPage | ✅ |
| 17 | 4e7f95be |
perf: add enabled flag to TPA query hook to skip unnecessary fetches | ✅ |
Status legend: 🔲 Not started · 🔍 In progress · ✅ Approved ·
Reviews
Commit 1 — d5b1df7c: chore: bump frontend-base to 1.0.0-alpha.14 ✅
Type: frontend-base-only change (no master equivalent)
Master refs: n/a
Reviewed by: @brian-smith-tcril (GitHub web UI)
Notes: Straightforward package.json version bump plus package-lock.json update. Makes sense.
Commit 2 — b0ee418a: refactor: replace injectIntl with useIntl ✅
Type: Port of master commits
Master refs: db3d007c, 43a584eb, 5bd6926f, 76e400f0
Things to check:
- All non-skipped files from the 4 master commits are present in the PR commit, adapted for frontend-base
- The 3
src/recommendations/files are the only intentional skips
Master commit file breakdown:
db3d007c — refactor: Replace of injectIntl with useIntl
@brian-smith-tcril: compared master commit to PR commit diff
Note: master commit also removes import React from 'react' in these files, but frontend-base already didn't have those imports — expected difference.
src/register/RegistrationFields/CountryField/CountryField.test.jsx✅src/register/RegistrationFields/EmailField/EmailField.test.jsx✅src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx✅src/register/RegistrationFields/NameField/NameField.test.jsx✅src/register/RegistrationFields/TermsOfServiceField/TermsOfService.test.jsx✅src/register/RegistrationFields/UsernameField/UsernameField.test.jsx✅
43a584eb — refactor: Replace of injectIntl with useIntl (#1538)
@brian-smith-tcril: compared master commit to PR commit diff
Note: same import React difference — expected.
src/common-components/tests/FormField.test.jsx✅src/forgot-password/tests/ForgotPasswordPage.test.jsx✅src/login/LoginPage.jsx✅import { useEffect, useMemo, useState } from 'react'already existed on frontend-base; master commit adds it — expected difference
src/login/tests/AccountActivationMessage.test.jsx✅src/login/tests/LoginPage.test.jsx✅
5bd6926f — refactor: Replace of injectIntl with useIntl (#1539)
@brian-smith-tcril: compared master commit to PR commit diff
Note: same import React difference — expected.
src/login/tests/ChangePasswordPrompt.test.jsx✅src/login/tests/LoginFailure.test.jsx✅src/logistration/Logistration.test.jsx✅- Master has a test
'should do nothing when user clicks on the same tab (login/register) again'that is absent from the frontend-base version — confirmed this was already absent from the initialrefactor: migrate to frontend-basecommit (8f8531a2), so it's a pre-existing gap, not introduced by this PR
- Master has a test
src/progressive-profiling/tests/ProgressiveProfiling.test.jsx✅- Master has 11 additional tests absent from frontend-base — confirmed already absent from initial
refactor: migrate to frontend-basecommit (8f8531a2), pre-existing gap, not introduced by this PR:should set empty host property value for non-embedded experienceshould submit user profile details on form submissionshould redirect to recommendations page if recommendations are enabledshould not redirect to recommendations page if user is on its way to enroll in a courseshould set host property value to host where iframe is embedded for on ramp experienceshould have onMouseDown handlers on submit and skip buttons to prevent default behaviorshould update form values through onChange handlersshould call sendTrackEvent when form interactions occurshould call analytics functions on component mountshould call setThirdPartyAuthContextSuccess in embedded modeshould not call third party auth functions when not in embedded mode
- Master has 11 additional tests absent from frontend-base — confirmed already absent from initial
src/recommendations/RecommendationsPageLayouts/SmallLayout.test.jsx⏭️ (skip — nosrc/recommendations/on frontend-base)src/reset-password/tests/ResetPasswordPage.test.jsx✅
76e400f0 — refactor: Replace of injectIntl with useIntl (#1540)
@brian-smith-tcril: compared master commit to PR commit diff
Note: same import React difference — expected.
src/recommendations/tests/RecommendationsList.test.jsx⏭️ (skip — nosrc/recommendations/on frontend-base)src/recommendations/tests/RecommendationsPage.test.jsx⏭️ (skip — nosrc/recommendations/on frontend-base)src/register/RegistrationPage.test.jsx✅src/register/components/tests/ConfigurableRegistrationForm.test.jsx✅src/register/components/tests/RegistrationFailure.test.jsx✅src/register/components/tests/ThirdPartyAuth.test.jsx✅
Commit 3 — 046abb77: chore: handle forbidden username exceptions on registration ✅
Type: Port of master commit
Master refs: 58ec90ac
Master commit file breakdown:
58ec90ac — chore: Handle forbidden username exceptions on registration (#1545)
@brian-smith-tcril: compared master commit to PR commit diff
src/register/components/RegistrationFailure.jsx✅src/register/data/constants.js✅src/register/messages.jsx✅
@brian-smith-tcril: identical to master commit
Commit 4 — 80ce7e8c: fix: username suggestions alignment ✅
Type: Port of master commit
Master refs: ffb8a2d4
Master commit file breakdown:
ffb8a2d4 — fix: username suggestions alignment (#1584)
@brian-smith-tcril: quick skim of master commit and PR commit diff looked mostly identical; Claude compared diffs and confirmed
src/register/RegistrationFields/UsernameField/UsernameField.jsx✅src/register/data/sagas.js✅ (expected frontend-base import consolidation;e.response?.statusoptional chaining vse.response && e.response.status)src/sass/_registration.scss✅
Commit 5 — 8d32c37e: feat: add Slot for login page ✅
Type: Port of master commit
Master refs: 0418a04f
Master commit file breakdown:
0418a04f — feat: Add plugin slot for login page
@brian-smith-tcril: compared master commit to PR commit diff
package.json⏭️ (skip — master addsfrontend-plugin-framework, not used with frontend-base)package-lock.json⏭️ (same)src/login/LoginPage.jsx✅ (@brian-smith-tcril + Claude: net changes identical; diffs look different only because some imports were already in their final positions on frontend-base and didn't need moving)src/login/tests/LoginPage.test.jsx✅src/logistration/Logistration.jsx✅ (@brian-smith-tcril: only difference is import path../plugin-slots/LoginComponentSlot→../slots/LoginComponentSlot; Claude confirmed by comparing diffs)src/logistration/Logistration.test.jsx✅src/plugin-slots/LoginComponentSlot/README.md✅ (master usesplugin-slots/; frontend-base usesslots/)src/plugin-slots/LoginComponentSlot/component_with_prefix.png✅ (copied from master — should be replaced with frontend-base screenshots once example code exists)src/plugin-slots/LoginComponentSlot/default_component.png✅ (same)src/plugin-slots/LoginComponentSlot/index.jsx✅ (pluginProps→ plainpropsmakes sense for frontend-baseSlotAPI; follow-up note added for TypeScript conversion)src/recommendations/data/tests/hooks.test.jsx⏭️ (skip — nosrc/recommendations/on frontend-base)
Commit 6 — 00423433: fix: add missing space symbol to the 'additional.help.text' field ✅
Type: Port of master commit
Master refs: 4cb79223
Master commit file breakdown:
4cb79223 — fix: add missing space symbol to the 'additional.help.text' field (#1521)
@brian-smith-tcril: compared master commit to PR commit diff
src/forgot-password/ForgotPasswordPage.jsx✅src/forgot-password/messages.js✅
@brian-smith-tcril: identical to master commit
Commit 7 — 99a193a6: fix: adding value length check for full name field ✅
Type: Port of master commit
Master refs: 642853e0
Master commit file breakdown:
642853e0 — fix: adding value length check for full name field (#1561)
@brian-smith-tcril: compared master commit to PR commit diff
src/common-components/tests/ThirdPartyAuthAlert.test.jsx✅ (identical; @brian-smith-tcril: not a fan of snapshot tests here — see follow-ups)src/common-components/tests/__snapshots__/ThirdPartyAuthAlert.test.jsx.snap✅ (identical)src/register/RegistrationFields/NameField/NameField.test.jsx✅ (identical; @brian-smith-tcril: usesfireEventinstead ofuserEvent— see follow-ups)src/register/RegistrationFields/NameField/validator.js✅ (identical)src/register/messages.jsx✅ (identical)
Commit 8 — 2bfa6447: feat: migrate from Redux to React Query and React Context
Type: Port of master commits (large)
Master refs: 0d709d15, 93bd0f24
Master commit file breakdown (0d709d15):
Comparing master commit to PR commit diff. Given the size, @brian-smith-tcril and Claude are reviewing together — Claude comparing diffs, @brian-smith-tcril spot-checking and providing judgment calls.
Other:
package.json✅- Both master and frontend-base remove:
@redux-devtools/extension,redux-logger,redux-mock-store,redux-saga,redux-thunk,reselect react-reduxandreduxwere already absent from frontend-base dependencies before this commit, so no removal needed (master removes them from regular deps)@tanstack/react-queryadded as regular dependency on master; added aspeerDependencyon frontend-base (shell providesQueryClient) — expectedreact-reduxandreduxremoved frompeerDependencieson frontend-base ✅ (no peerDep changes on master)- devDependency differences: master adds
@edx/typescript-configand@testing-library/jest-dom; frontend-base adds@types/jest— expected given different tooling
- Both master and frontend-base remove:
package-lock.json✅ (assume updated appropriately to match package.json)src/MainApp.jsx/src/Main.tsx✅- Master: removes
configureStorefromAppProvider, wraps app inQueryClientProvider - Frontend-base equivalent (
Main.tsx): removesReduxProvider+configureStore; noQueryClientProviderneeded sinceCurrentAppProvider(via the shell) handles it
- Master: removes
src/constants.ts⏭️ (master addsexport const appId = 'org.openedx.frontend.app.authn'; already present on frontend-base with identical content; absent from commit 8. Author noted it was missing on master but didn't explain why it's needed — not clear to @brian-smith-tcril either.)tsconfig.json⏭️ (master adds TypeScript config; frontend-base already doing TypeScript the frontend-base way — differentextends,include, andexcludeare all expected; absent from commit 8)
Deleted — Redux/Saga infrastructure (confirmed deleted in commit 8):
src/common-components/data/actions.js🗑️ ✅src/common-components/data/reducers.js🗑️ ✅src/common-components/data/sagas.js🗑️ ✅src/common-components/data/selectors.js🗑️ ✅src/common-components/data/tests/reducer.test.js🗑️ ✅src/common-components/data/tests/sagas.test.js🗑️ ✅src/data/configureStore.js🗑️ ✅src/data/reducers.js🗑️ ✅src/data/sagas.js🗑️ ✅src/data/tests/reduxUtils.test.js🗑️ ✅src/data/utils/reduxUtils.js🗑️ ✅src/forgot-password/data/actions.js🗑️ ✅src/forgot-password/data/reducers.js🗑️ ✅src/forgot-password/data/sagas.js🗑️ ✅src/forgot-password/data/selectors.js🗑️ ✅src/forgot-password/data/tests/reducers.test.js🗑️ ✅src/forgot-password/data/tests/sagas.test.js🗑️ ✅src/login/data/actions.js🗑️ ✅src/login/data/reducers.js🗑️ ✅src/login/data/sagas.js🗑️ ✅src/login/data/tests/reducers.test.js🗑️ ✅src/login/data/tests/sagas.test.js🗑️ ✅src/progressive-profiling/data/actions.js🗑️ ✅src/progressive-profiling/data/reducers.js🗑️ ✅src/progressive-profiling/data/sagas.js🗑️ ✅src/progressive-profiling/data/selectors.js🗑️ ✅src/register/data/actions.js🗑️ ✅src/register/data/reducers.js🗑️ ✅src/register/data/sagas.js🗑️ ✅src/register/data/selectors.js🗑️ ✅src/register/data/tests/reducers.test.js🗑️ ✅src/register/data/tests/sagas.test.js🗑️ ✅src/reset-password/data/actions.js🗑️ ✅src/reset-password/data/reducers.js🗑️ ✅src/reset-password/data/sagas.js🗑️ ✅src/reset-password/data/selectors.js🗑️ ✅src/reset-password/data/tests/sagas.test.js🗑️ ✅
Added — React Query infrastructure:
src/common-components/components/ThirdPartyAuthContext.tsx✅ (identical to master except TS interface property separators: master uses;, commit 8 uses,)src/common-components/components/ThirdPartyAuthContext.test.tsx✅ (@testing-library/jest-domnot imported,toBeInTheDocument()→toBeTruthy()— see follow-ups)src/common-components/data/apiHook.ts✅ (identical to master)src/common-components/data/queryKeys.ts✅ (identical to master)src/forgot-password/data/apiHook.ts✅ (expected import consolidation; TS interface;→,;ApiError extends Errorinstead of bareinterface— enables optional chaining onerror.response?.statusand removeserror as Errorcast)src/forgot-password/data/apiHook.test.ts✅ (expected import consolidation to@openedx/frontend-base+ matchingjest.mockupdate)src/forgot-password/data/api.test.ts✅ (expected import consolidation;LMS_BASE_URL→lmsBaseUrl;mockConfiggetsas ReturnType<typeof getSiteConfig>type assertion; minor trailing comma and comment differences)src/login/components/LoginContext.tsx✅ (identical to master except TS interface property separators:;→,)src/login/components/LoginContext.test.tsx✅ (@testing-library/jest-domnot imported,toBeInTheDocument()→toBeTruthy()— see follow-ups)src/login/data/apiHook.ts✅ (expected import consolidation; TS interface;→,)src/login/data/apiHook.test.ts✅ (expected import consolidation + combinedjest.mock)src/login/data/api.test.ts✅ (expected import consolidation;LMS_BASE_URL→lmsBaseUrl;mockConfigtype assertion;getUrlByRouteRoleadded to mock)src/progressive-profiling/components/ProgressiveProfilingContext.tsx✅ (identical to master except TS interface property separators:;→,)src/progressive-profiling/data/apiHook.ts✅ (TS interface property separators:;→,)src/progressive-profiling/data/apiHook.test.ts✅ (mockContextValuehas extra fields beyond what's exercised;mockResolvedValueOnce(undefined)vs master'smockResolvedValueOnce({ success: true })—undefinedis more accurate sincepatchAccountreturnsvoid)src/progressive-profiling/data/api.test.ts✅ (import consolidation,getConfig/LMS_BASE_URL→getSiteConfig/lmsBaseUrl,mockConfigtyped asReturnType<typeof getSiteConfig>, trailing commas)src/register/components/RegisterContext.tsx✅ (TS;→,;CLEAR_REGISTRATION_BACKEND_ERRORdestructuring → spread+delete;Array<T>→T[]forsetRegistrationErrortype;backendValidationscheck order reversed —registrationErrorbeforevalidations(see master follow-up); addsuseRegisterContextOptionalnot on master — used byPasswordFieldwhich is shared across flows whereRegisterProvidermay not be present)src/register/components/RegisterContext.test.tsx✅ (jest-dom absent, see follow-up →toBeTruthy(); adds test explicitly coveringregistrationErrorpriority overvalidationsinbackendValidations)src/register/data/apiHook.ts✅ (import consolidation;interface { [key: string]: unknown }→type X = Record<string, unknown>; TS;→,;ApiError extends Errorpattern simplifieserror.responsechecks in bothonErrorhandlers)src/register/data/apiHook.test.ts✅ (import consolidation; combinedjest.mockblock)src/register/data/api.test.ts✅ (import consolidation, combinedjest.mockblock,getConfig/LMS_BASE_URL→getSiteConfig/lmsBaseUrl, trailing commas)src/register/types.ts✅ (TS;→,;Array<T>→T[])src/reset-password/data/apiHook.ts✅ (import consolidation; TS;→,;ApiError extends Errorpattern;ResetPasswordPayload.paramswidened toRecord<string, string | boolean>— not on master tip, see follow-up)src/reset-password/data/apiHook.test.ts✅ (import consolidation, combinedjest.mock, trailing commas; both master and commit 8 use booleanis_account_recoveryin test payloads)src/reset-password/data/api.test.ts✅ (import consolidation, combinedjest.mock,getConfig/LMS_BASE_URL→getSiteConfig/lmsBaseUrl,mockConfigtyped asReturnType<typeof getSiteConfig>, trailing commas)
Renamed on master (service.js → api.ts), renamed in commit 8:
src/common-components/data/api.ts✅ (function refactor and.catchremoval identical to master;??→||to align with master)src/forgot-password/data/api.ts✅ (identical to master except master removes an eslint disable that was already absent on frontend-base)src/progressive-profiling/data/api.ts✅ (identical to master except master removes an eslint disable that was already absent on frontend-base)src/reset-password/data/api.ts✅ (identical to master except: master removes two eslint disables already absent on frontend-base;true→'true'forURLSearchParams.append()— type-correct for TypeScript, behaviorally identical)
Renamed on master (service.js → api.ts), delete+add in commit 8 (too different for git to detect as rename):
src/login/data/api.ts✅ (function refactor +.catchremoval matches master;getUrlByRouteRolefor default redirect was already inservice.js;??→||andredirectUrlvar inlined intocamelCaseObjectto align with master)src/register/data/api.ts✅ (registerRequest→registerNewUserApi, url extraction, and named export format match master;??→||to align with master;.catchintentionally kept in both functions)
Modified — pure import React removal on master, already absent on frontend-base; confirmed none of these files appear in the PR commit (skip):
src/base-container/components/default-layout/DefaultLayout.test.jsx⏭️src/base-container/components/default-layout/LargeLayout.jsx⏭️src/base-container/components/default-layout/MediumLayout.jsx⏭️src/base-container/components/default-layout/SmallLayout.jsx⏭️src/base-container/components/welcome-page-layout/LargeLayout.jsx⏭️src/base-container/components/welcome-page-layout/MediumLayout.jsx⏭️src/base-container/components/welcome-page-layout/SmallLayout.jsx⏭️src/base-container/index.jsx⏭️src/base-container/tests/BaseContainer.test.jsx⏭️src/common-components/EmbeddedRegistrationRoute.jsx⏭️src/common-components/EnterpriseSSO.jsx⏭️src/common-components/InstitutionLogistration.jsx⏭️src/common-components/NotFoundPage.jsx⏭️src/common-components/SocialAuthProviders.jsx⏭️src/common-components/ThirdPartyAuth.jsx⏭️src/common-components/ThirdPartyAuthAlert.jsx⏭️src/common-components/Zendesk.jsx⏭️src/common-components/tests/SocialAuthProviders.test.jsx⏭️src/common-components/tests/ThirdPartyAuthAlert.test.jsx⏭️src/common-components/tests/UnAuthOnlyRoute.test.jsx⏭️src/field-renderer/FieldRenderer.jsx⏭️src/field-renderer/tests/FieldRenderer.test.jsx⏭️src/login/AccountActivationMessage.jsx⏭️src/register/RegistrationFields/TermsOfServiceField/TermsOfService.jsx⏭️src/plugin-slots/LoginComponentSlot/index.jsx⏭️
Modified — import React removal plus minor other changes on master, absent from commit 8 (skip):
src/forgot-password/ForgotPasswordAlert.jsx⏭️ (removesimport React from 'react'+ indentation fix on master; absent from commit 8)src/reset-password/ResetPasswordFailure.jsx⏭️ (removesimport React from 'react'+ indentation fix on master; absent from commit 8)
Modified — pure import React, { X } → import { X } on master, already absent on frontend-base; confirmed none of these files appear in the PR commit (skip):
src/common-components/FormGroup.jsx⏭️src/index.jsx⏭️src/login/LoginFailure.jsx⏭️src/register/RegistrationFields/HonorCodeField/HonorCode.jsx⏭️src/register/components/ConfigurableRegistrationForm.jsx⏭️
Modified — import React, { X } → import { X } plus minor other changes on master (eslint comment / indentation), absent from commit 8 (skip):
src/login/ChangePasswordPrompt.jsx⏭️ (import React, { useEffect, useState }→import { useEffect, useState }; also adds@typescript-eslint/no-unused-varsto eslint comment)src/register/components/RegistrationFailure.jsx⏭️ (import React, { useEffect }→import { useEffect }; also fixes indentation on abreakstatement)
Modified — minor other changes only on master, absent from commit 8 (skip):
src/common-components/RedirectLogistration.jsx⏭️ (blank line removal only on master; absent from commit 8)src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx⏭️ (switches eslint disable fromno-unused-varsto@typescript-eslint/no-unused-varson master; absent from commit 8)
Modified — Redux export removals from index/barrel files, identical to master:
src/common-components/index.jsx✅ (removesreducer,saga,storeNameexports)src/data/utils/index.js✅ (removesAsyncActionTypeexport)src/forgot-password/index.js✅ (removesreducer,FORGOT_PASSWORD,saga,forgotPasswordResultSelectorexports)src/progressive-profiling/index.js✅ (removesreducer,sagaexports)src/register/index.js✅ (removesreducer,saga,storeNameexports)src/reset-password/index.js✅ (removesreducer,RESET_PASSWORD,saga,storeNameexports)
Modified — Redux export removals from index/barrel files, differs from master:
src/login/index.js✅ (master removesreducer,saga; commit 8 also removesexport const storeName = 'login'+ blank line — expected,storeNamenot needed on frontend-base)
Modified — components and tests:
src/common-components/PasswordField.jsx✅ (import consolidation;useRegisterContext→useRegisterContextOptionalwithnoopFnfallbacks; see follow-up re: context coupling)src/common-components/tests/FormField.test.jsx✅ (Redux/store setup removed;RegisterProvidernot needed in wrapper sincePasswordFielduses optional context; mock factory approach simplifiesbeforeEachto justmockClear(); import consolidation)src/forgot-password/ForgotPasswordPage.jsx✅ (Redux→React Query migration identical; import consolidation;useAppConfig()correctly extracted to component level on frontend-base)src/forgot-password/tests/ForgotPasswordPage.test.jsx✅ (import consolidation;mergeConfig→mergeAppConfig(appId, ...);CurrentAppProvideradded to wrapper;mockedNavigatorassertion usesstringContaining; test rewrites largely identical to master; usesfireEvent— see follow-ups)src/login/LoginPage.jsx✅ (identical migration to master; minor import ordering adjustment forAccountActivationMessage)src/login/tests/LoginPage.test.jsx✅ (import consolidation;CurrentAppProvider/mergeAppConfigwrapper setup; some tests renamed; several tests removed — see follow-ups; somefireEventusage — see follow-ups)src/logistration/Logistration.jsx✅ (identical migration to master)src/logistration/Logistration.test.jsx✅ (import consolidation; Redux/store setup removed;CurrentAppProviderin wrapper;mergeAppConfigper-test; heading assertions not updated to match master's tab/button roles — see follow-up; test fixture data gaps — see follow-up; 3 tests missing — see follow-up)src/progressive-profiling/ProgressiveProfiling.jsx✅ (identical migration to master;mockLoggingService→loggingServicebug fix;appConfig.Xstyle foruseAppConfig()fields)src/progressive-profiling/tests/ProgressiveProfiling.test.jsx✅ (import consolidation; Redux/store setup removed;CurrentAppProviderin wrapper; extramergeConfigcalls on master not needed in commit 8;sendPageEventmissing — see follow-up;fireEventusage — see follow-up; recommendations tests absent — expected; all other tests present in both)src/register/RegistrationFields/CountryField/CountryField.jsx✅ (identical migration to master; minor cosmetic formatting onvalidateCountryFieldandhandleOnChangecalls)src/register/RegistrationFields/CountryField/CountryField.test.jsx✅ (identical migration to master;CurrentAppProviderin wrapper;BrowserRouter→MemoryRouter— see follow-up;mergeAppConfigreplaces pre-existingmergeConfig)src/register/RegistrationFields/EmailField/EmailField.jsx✅ (identical migration to master)src/register/RegistrationFields/EmailField/EmailField.test.jsx✅ (identical migration to master;CurrentAppProviderin wrapper;BrowserRouter→MemoryRouter— see follow-up;fireEventin new tests — see follow-up)src/register/RegistrationFields/NameField/NameField.jsx✅ (identical migration to master)src/register/RegistrationFields/NameField/NameField.test.jsx✅ (identical migration to master;CurrentAppProviderin wrapper;BrowserRouter→MemoryRouter— see follow-up;fireEventin new/updated tests — see follow-up)src/register/RegistrationFields/UsernameField/UsernameField.jsx✅ (identical migration to master)src/register/RegistrationFields/UsernameField/UsernameField.test.jsx✅ (identical migration to master;CurrentAppProviderin wrapper;BrowserRouter→MemoryRouter— see follow-up; test names updated to drop "in redux";fireEventin new tests — see follow-up)src/register/RegistrationPage.jsx✅ (identical migration to master;eslint-disable-line react-hooks/exhaustive-depsadded — see follow-up)src/register/RegistrationPage.test.jsx✅ (migration mostly matches master; several follow-ups logged — see below)src/register/components/tests/ConfigurableRegistrationForm.test.jsx✅ (migration mostly matches master; several follow-ups logged — see below)src/register/components/tests/RegistrationFailure.test.jsx✅ (migration matches master;MemoryRouteranduseThirdPartyAuthHookmock noted in follow-ups)src/register/components/tests/ThirdPartyAuth.test.jsx✅ (migration mostly matches master; follow-ups logged — see below)src/register/data/utils.js— not modified in commit 8; master commit has indentation fixes and||=refactoring (no functional change). Worth porting for consistency.src/reset-password/ResetPasswordPage.jsx✅ (identical migration to master;getConfig().SITE_NAME→getSiteConfig().siteNameexpected)src/reset-password/tests/ResetPasswordPage.test.jsx✅ (migration matches master;toBeTruthy()vstoBeInTheDocument()andfireEventusage noted in follow-ups)
Skipped — src/recommendations/:
src/recommendations/ProductCard/BaseCard/index.jsx⏭️src/recommendations/ProductCard/Footer/index.jsx⏭️src/recommendations/ProductCard/index.jsx⏭️src/recommendations/RecommendationsList.jsx⏭️src/recommendations/RecommendationsPage.jsx⏭️src/recommendations/RecommendationsPageLayouts/LargeLayout.jsx⏭️src/recommendations/RecommendationsPageLayouts/SmallLayout.test.jsx⏭️src/recommendations/tests/RecommendationsList.test.jsx⏭️src/recommendations/tests/RecommendationsPage.test.jsx⏭️
Master commit file breakdown (93bd0f24):
fix: prioritize registration errors over inline validations in backendValidations
src/register/components/RegisterContext.tsx✅ (priority order fix already present in commit 8)src/register/components/RegisterContext.test.tsx✅ (newshould prioritize registrationError over validations for backendValidationstest already present in commit 8)
Commit 9 — 55b6eb3e: fix: pass correct arguments to setEmailSuggestionContext ✅
Type: Bug fix introduced during port (no master equivalent)
src/register/RegistrationFields/EmailField/EmailField.jsx✅ (setEmailSuggestionContext({ suggestion: '', type: '' })→setEmailSuggestionContext('', '')— function expects two string args, not a single object)src/register/RegistrationFields/EmailField/EmailField.test.jsx✅ (adds assertion verifying correct call signature)
Commit 10 — 119780a5: fix: move navigate() calls from render to useEffect in ResetPasswordPage ✅
Type: Bug fix introduced during port (no master equivalent)
src/reset-password/ResetPasswordPage.jsx✅ (movesnavigate()calls from render phase intouseEffect— avoids React warnings and potential infinite render loops in strict mode)
Commit 11 — 06135925: fix: use functional state updater in validateInput to prevent stale closure ✅
Type: Bug fix introduced during port (no master equivalent)
src/reset-password/ResetPasswordPage.jsx✅ (replaces directformErrorsmutation + spread with functional updater(prev) => ({ ...prev, [name]: fieldError })invalidateInputandhandleOnFocus; simplifies return value to!fieldError)
Commit 12 — 5f13df70: fix: remove duplicate RedirectLogistration in ProgressiveProfiling ✅
Type: Pre-existing bug caught during port (no master equivalent)
src/progressive-profiling/ProgressiveProfiling.jsx✅ (removes redundantRedirectLogistrationgated onshouldRedirect && welcomePageContext.nextUrl; the broadershouldRedirectblock below covers the same condition — it's not clear from the existing code or PR feat: param based redirection #993 context whyeducationLevel/userIdwould intentionally be omitted for thenextUrlcase)
Commit 13 — 537c47b7: fix: correct LoginContext setter types and remove dead PASSWORD_RESET_ERROR code ✅
Type: Fix (no master equivalent)
src/login/components/LoginContext.tsx✅ (setFormFields/setErrorstypes corrected from plain function signatures toDispatch<SetStateAction<...>>)src/reset-password/ResetPasswordPage.jsx✅ (PASSWORD_RESET_ERRORremoved — no code path setsstatusto that value; dead code inuseEffectcondition and navigate call)src/reset-password/tests/ResetPasswordPage.test.jsx✅ (comment update only)
Commit 14 — ad8afe7b: perf: add staleTime to third-party auth context query ✅
Type: Perf improvement (no master equivalent)
src/common-components/data/apiHook.ts✅ (addsstaleTime: 5 * 60 * 1000— TPA context is effectively static per session, prevents unnecessary background refetches)
Commit 15 — cecbd7c5: fix: include payload in TPA query key to prevent stale cache ✅
Type: Bug fix (no master equivalent)
src/common-components/data/apiHook.ts✅ (passespayloadtoThirdPartyAuthQueryKeys.byPage)src/common-components/data/queryKeys.ts✅ (byPageupdated to accept optionalpayloadand include it in the key)
Commit 16 — 0a787708: fix: call setThirdPartyAuthContextBegin only on mount in LoginPage ✅
Type: Bug fix (no master equivalent)
src/login/LoginPage.jsx✅ (movessetThirdPartyAuthContextBegin()from data syncuseEffectto mount-onlyuseEffectalongsidesendPageEvent; removes it from the sync effect's deps array)
Commit 17 — 4e7f95be: perf: add enabled flag to TPA query hook to skip unnecessary fetches ✅
Type: Perf improvement (no master equivalent)
src/common-components/data/apiHook.ts✅ (adds optionalenabledparam, defaulting totrue)src/progressive-profiling/ProgressiveProfiling.jsx✅ (passes{ enabled: registrationEmbedded }to skip TPA fetch when not in embedded mode)
Follow-ups
-
injectIntlstill exported from frontend-base: This PR reduces the number of places that needinjectIntlby migrating touseIntl, but frontend-base still exports it. Worth deprecating/removing that export in a follow-up. -
LoginComponentSlotshould be TypeScript:src/slots/LoginComponentSlot/index.jsxis JSX with PropTypes; should be converted to TypeScript to match frontend-base conventions. -
LoginComponentSlotREADME and screenshots: The master version includes example code and screenshots; the frontend-base version has screenshots copied from master but no example code. Once example code is added, the screenshots should be retaken from frontend-base. -
MAX_FULL_NAME_LENGTHhardcoded on frontend: @brian-smith-tcril commented on the master PR that the max length should come from the backend rather than being hardcoded client-side. The original feedback (avoid magic numbers) was addressed by extracting it into a named constant, but it's still a hardcoded value. The backend validation exists (edx-platform PR #34577) but the frontend doesn't read the limit from it. -
fireEventvsuserEventin tests: New/updated tests inNameField.test.jsx,ForgotPasswordPage.test.jsx,LoginPage.test.jsx,ProgressiveProfiling.test.jsx,EmailField.test.jsx,UsernameField.test.jsx, andResetPasswordPage.test.jsxusefireEventinstead of the preferreduserEvent.CountryField.test.jsxalso has pre-existingfireEventusage. Worth updating in a follow-up. -
ThirdPartyAuthAlertsnapshot test:ThirdPartyAuthAlert.test.jsxuses a snapshot test which is not ideal. Worth replacing with more explicit assertions in a follow-up. -
@testing-library/jest-domnot in frontend-base dependencies: Master adds@testing-library/jest-domas a devDependency; it's absent from frontend-base entirely. As a result, new test files usetoBeTruthy()instead oftoBeInTheDocument(), giving weaker test failure messages. AffectsThirdPartyAuthContext.test.tsx,LoginContext.test.tsx,RegisterContext.test.tsx, andResetPasswordPage.test.jsx. Worth adding jest-dom to frontend-base so tests can use the more expressive matchers. -
(nit) Over-specified mock context in
progressive-profiling/data/apiHook.test.ts:mockContextValueincludes all fields of the context interface (isLoading,showError,success,setLoading,clearState) even though onlysetShowError,setSuccess, andsetSubmitStateare exercised in assertions. I find that mocks that only include what's needed make the intent clearer. -
PasswordFieldcontext coupling:PasswordFieldis a shared component used across login, registration, and reset-password flows, but it reaches directly intoRegisterContextto getsetValidationsSuccess,setValidationsFailure,validationApiRateLimited, andclearRegistrationBackendError. On master this throws outside aRegisterProvider; the frontend-base version works around it withuseRegisterContextOptional()andnoopFnfallbacks. ThenoopFnboilerplate is a sign that this isn't a great pattern — using an optional context in a shared component shouldn't require manual setup like this. -
should dismiss reset password banner on form submissionremoved inlogin/tests/LoginPage.test.jsx: This test is present on master but removed in commit 8. Worth verifying the removal is intentional. -
should handle successful loginmissing fromlogin/tests/LoginPage.test.jsxin commit 8: Master has a test covering successful login with redirect (previously namedshould redirect to url returned by login endpoint after successful authentication); it appears to be absent from commit 8. Worth verifying whether this was intentionally dropped or accidentally omitted. -
should handle SSO login successmissing fromlogin/tests/LoginPage.test.jsxin commit 8: This test was added on master but appears to be absent from commit 8. Worth verifying whether it was intentionally dropped or accidentally omitted. -
should handle successful authentication via SSOmissing fromlogin/tests/LoginPage.test.jsxin commit 8: This test is present on master but appears to be absent from commit 8. Worth verifying whether it was intentionally dropped or accidentally omitted. -
Missing tests in frontend-base: Several tests present on master are absent from the frontend-base branch, dropped during the initial
refactor: migrate to frontend-basemigration. These are pre-existing gaps unrelated to this PR but worth addressing:src/logistration/Logistration.test.jsx:should do nothing when user clicks on the same tab (login/register) againsrc/progressive-profiling/tests/ProgressiveProfiling.test.jsx: 11 tests (see Commit 2 notes for full list)
-
Heading assertions not updated in
Logistration.test.jsx: Master updated sign-in/register assertions to use tab/button roles inshould render login/register headings when SHOW_REGISTRATION_LINKS is disabledandshould render only login page when public account creation is disabled; commit 8 still uses oldscreen.getByRole('heading', { level: 3 })assertions in both tests. Worth updating. -
3 tests missing from
Logistration.test.jsxin commit 8: The following tests are present on master but absent from commit 8:should call authService getCsrfTokenService on component mount,should send correct page events for login and register when handling institution login,should handle institution login with string parameters correctly. Worth adding. -
BrowserRouter→MemoryRouterin test wrappers: Commit 8 switches fromBrowserRoutertoMemoryRouterinCountryField.test.jsx,EmailField.test.jsx,NameField.test.jsx,UsernameField.test.jsx,RegistrationPage.test.jsx,ConfigurableRegistrationForm.test.jsx, andRegistrationFailure.test.jsx. Worth investigating whether this is intentional. -
SESSION_COOKIE_DOMAIN: ''added tomergeAppConfiginRegistrationPage.test.jsx: Not present on master. Worth investigating whether this is required for tests to pass on frontend-base and why. -
ENABLE_DYNAMIC_REGISTRATION_FIELDS: trueadded tomergeAppConfiginRegistrationPage.test.jsx: Present in commit 8'sdescribe('Test Registration Page')block but not in master'smergeConfig. Worth investigating whether this is needed and if it should be added to master as well. -
Hardcoded cookie name in
should check user retention cookieinRegistrationPage.test.jsx: Master usesgetConfig().USER_RETENTION_COOKIE_NAMEdynamically; commit 8 hardcodes'authn-returning-user=true'. Worth updating to usegetSiteConfig()dynamically to match master's intent. -
should redirect to dashboard if features flags are configured but no optional fields are configuredincompletely ported in commit 8 (RegistrationPage.test.jsx): Master is async and updates assertions to check the cookie and that a div exists (waitFor(() => expect(document.cookie)...)+expect(container.querySelector('div')).toBeTruthy()); commit 8 is sync and still uses the oldexpect(window.location.href).toBe(dashboardUrl)assertion. Worth updating. -
should redirect to progressive profiling page if optional fields are configuredincompletely ported in commit 8 (RegistrationPage.test.jsx): Similar issues — master is async withonSuccesscallback wiring andwaitForon the cookie with a dynamicgetConfig().USER_RETENTION_COOKIE_NAMEreference; commit 8 is sync and hardcodes'authn-returning-user=true'. Worth updating. -
should call internal state setters on successful registrationis async on master but sync in commit 8 (RegistrationPage.test.jsx): Master is async withwaitForasserting on the cookie; commit 8 is sync and asserts directly onsetRegistrationResultbeing called. Worth investigating whether the async/cookie assertion approach should be ported. -
Duplicate test name resolved differently in
RegistrationPage.test.jsx: Master has two tests both namedshould redirect to url returned in registration result after successful account creation(one sync, one async usingonSuccesscallback wiring). Commit 8 renames the second toshould wire up onSuccess callback for registration mutationand makes it non-async. Worth investigating whether this divergence is intentional or addressed in a later commit. -
honor_coderemoved fromformPayloadinshould show error if email and confirm email fields do not match on submit clickinConfigurableRegistrationForm.test.jsxon frontend-base but not on master: Worth investigating whether this removal is intentional and whether master should be updated to match. -
Late
getLocaleimport witheslint-disable-next-line import/firstinThirdPartyAuth.test.jsx:getLocaleis imported afterjest.mock('@openedx/frontend-base', ...)rather than being included in the main import block. Worth investigating why this is split out and whether it can be consolidated. -
(nit)
useThirdPartyAuthHookmocked inConfigurableRegistrationForm.test.jsxandRegistrationFailure.test.jsxbut never referenced in any test: Commit 8 adds a top-level mock for../../../common-components/data/apiHooksolely to prevent the real hook from running. Master doesn't need it. Worth confirming this is intentional and considering whether it can be removed or consolidated. -
eslint-disable-line react-hooks/exhaustive-depsadded toregistrationResultuseEffect inRegistrationPage.jsx: Not present on master. Worth investigating whether the deps array is actually correct or whether this is suppressing a legitimate warning. -
sendPageEventnot imported inProgressiveProfiling.test.jsx: Master addssendPageEventto the analytics imports and asserts on it inshould call analytics functions on component mount; commit 8 doesn't import it and the assertion is absent. Worth adding. -
(nit) Repeated inline
useThirdPartyAuthContextmock inLogistration.test.jsx: Several tests that need secondary providers repeat the fulluseThirdPartyAuthContext.mockReturnValue(...)inline. Master uses a sharedsecondaryProvidersconstant for this. A shared mock object or helper could reduce the duplication. -
Test fixture data not fully ported in
Logistration.test.jsx: Master upgrades test fixtures with more complete data —secondaryProvidersgetsiconClass,iconImage, and real-looking URLs withsaml-test_universityas the id, and the defaultThirdPartyAuthContextmock includes a fullprovidersarray (Facebook) and populatedsecondaryProviders. Commit 8 still uses minimal fixtures with emptyproviders/secondaryProvidersarrays in the default mock and old minimal data (id: 'saml-test',/dummy-authURLs) inlined per-test. Worth updating.
Master only
-
TPA query fires unconditionally in
ProgressiveProfiling.jsx:useThirdPartyAuthHookis called even whenregistrationEmbeddedis false and the result is ignored. Fixed in commit 17 (4e7f95be) on frontend-base by adding anenabledoption to the hook and passingregistrationEmbeddedas the flag; worth porting to master. -
setThirdPartyAuthContextBegincalled on every data sync inlogin/LoginPage.jsx: It's in the data syncuseEffect(which runs on every TPA data update), causing a flash of PENDING state when TPA data is already cached. Fixed in commit 16 (0a787708) on frontend-base by moving it to the mount-onlyuseEffect; worth porting to master. -
payloadnot included in TPA query key incommon-components/data/queryKeys.ts:byPageonly keys onpageId, so payload changes (e.g.tpa_hint, query params) whilepageIdstays the same would serve stale cached data. Fixed in commit 15 (cecbd7c5) on frontend-base; worth porting to master. -
staleTimemissing from TPA context query incommon-components/data/apiHook.ts: The TPA context is effectively static per session; addingstaleTime: 5 * 60 * 1000prevents unnecessary background refetches when navigating between login/register tabs. Added in commit 14 (ad8afe7b) on frontend-base; worth porting to master. -
PASSWORD_RESET_ERRORdead code inResetPasswordPage.jsx: No code path setsstatustoPASSWORD_RESET_ERROR, making the checks in theuseEffectcondition and navigate call dead code. Removed in commit 13 (537c47b7) on frontend-base; worth removing on master too. -
Duplicate
RedirectLogistrationinProgressiveProfiling.jsx: A redundantRedirectLogistrationblock gated onshouldRedirect && welcomePageContext.nextUrlhas existed since PR feat: param based redirection #993. The broadershouldRedirectblock below it covers the same condition. It's not clear from existing code or PR context whyeducationLevel/userIdwould intentionally be omitted for thenextUrlcase. Fixed in commit 12 (5f13df70) on frontend-base; worth porting the removal to master. -
Duplicate
should show single sign on provider buttontest inThirdPartyAuth.test.jsx: Master has two tests with the same name; commit 8 (2bfa6447) consolidates to one. Worth removing the duplicate on master. -
mockResolvedValueOnce({ success: true })inprogressive-profiling/data/apiHook.test.ts: Master resolvespatchAccountwith a{ success: true }object, butpatchAccountreturnsvoid. The frontend-base version correctly usesundefined. Worth fixing on master. -
backendValidationspriority order inregister/components/RegisterContext.tsx:Master checksFixed on master instate.validationsbeforestate.registrationError.93bd0f24(which is included in commit 8 (2bfa6447) scope). Both master and commit 8 now giveregistrationErrorpriority. ✅ -
ResetPasswordPayload.paramstype inreset-password/data/apiHook.ts: Commit 8 (2bfa6447) widensparamsfromRecord<string, string>toRecord<string, string | boolean>, but master tip still has the narrower type. Test payloads in both master and commit 8 use boolean values foris_account_recovery, whilereset-password/data/api.tsconverts it to a string viaurl.searchParams.append('is_account_recovery', 'true'). Worth tracing actual call sites to determine the correct type — the test data alone isn't sufficient to confirm which is right.
Description
As per the title, the intent here is to port latest master into the frontend-base branch. However, during the porting process several issues or opportunities for enhancement were identified: this PR contains corresponding changes, each in a separate commit for possible use in future backports.
Summary
masterto thefrontend-basebranch, adapting all imports, config access, and component patterns to@openedx/frontend-base0d709d15), eliminating all Redux, Redux-Saga, and Reselect dependenciesvalidateInput,navigate()during render, wrong args tosetEmailSuggestionContext, doubleRedirectLogistration)staleTimeon TPA query, payload in query key,enabledflag for conditional fetches, mount-onlysetThirdPartyAuthContextBeginLoginContextsetter types and removes deadPASSWORD_RESET_ERRORcodeTally
Pre-migration ports (7 commits):
frontend-basealpha bump,injectIntl-to-useIntlrefactor, forbidden username handling, username suggestions alignment, Login page Slot, missing space fix, name field length check.Redux-to-React-Query migration (1 large commit): Replaces Redux + Redux-Saga with React Query + React Context across all modules (login, registration, forgot-password, reset-password, progressive-profiling, common-components). 123 files changed, ~8.5K insertions, ~5.8K deletions.
Bug fixes (5 commits): All bugs were pre-existing in the master migration commit and were found during review.
React Query improvements (3 commits): Performance and correctness improvements to the TPA query hook and
LoginContexttypes.LLM usage notice
Built with assistance from Claude models (mostly Opus 4.6, at one point with the "[1m]" beta context window).
Work Log
Pre-migration ports
Seven commits were cherry-picked and adapted from
master:frontend-basealpha bump —@openedx/frontend-basefrom alpha.13 to alpha.14injectIntltouseIntl— Ported 4 master commits (db3d007c,43a584eb,5bd6926f,76e400f0), removing theinjectIntlHOC from 20 files. Skipped files insrc/recommendations/(doesn't exist on this branch).FORBIDDEN_USERNAMEconstant, switch case inRegistrationFailure, and i18n message (with properdescriptionfield per frontend-base convention).LoginPageandLogistrationfromconnect()to hooks. UsedSlotfrom@openedx/frontend-base(notPluginSlot), with slot IDorg.openedx.frontend.slot.authn.loginComponent.v1.className="mx-1"to span.MAX_FULL_NAME_LENGTH = 255validation.Redux-to-React-Query migration
Ported master commit
0d709d15(and fix93bd0f24) in 5 phases:Phase 1 — New data layer (additive): Created API files (
api.ts), hook files (apiHook.ts), query keys, context providers, and types for all 6 modules. Added@tanstack/react-queryas a peerDependency. 31 new tests passing.Phase 2 — Common components & login: Migrated
PasswordField(uses optionalRegisterContextpattern since it's shared across flows),LoginPage, andLogistrationto React Query/Context. Deleted Redux files for login and common-components. RewroteLoginPage.test.jsxandLogistration.test.jsx. Resolved circular dependency insite.config.test.tsxby using string'test'instead ofEnvironmentTypes. 409 tests passing.Phase 3 — Registration: Migrated
RegistrationPageand all field components (CountryField,EmailField,NameField,UsernameField). Deleted all registration Redux files. Rewrote 8 test files (110 tests). 399 tests passing.Phase 4 — Password flows, progressive profiling & final Redux removal: Migrated
ForgotPasswordPage,ResetPasswordPage, andProgressiveProfiling. Deleted all remaining Redux infrastructure (configureStore.js, root reducer/sagas,reduxUtils.js). RemovedReduxProviderfromMain.tsx. Cleanedredux,redux-saga,reselect,redux-logger,redux-mock-store,@redux-devtools/extensionfrom dependencies. 410 tests passing.Phase 5 — Fix + cleanup: Applied
93bd0f24fix (registration error priority over inline validations). Deleted orphanedprogressive-profiling/data/service.js. 411 tests passing.Bug fixes (post-migration review)
A full review compared the port against the master commit, the frontend-base migration guide, and React Query best practices. All bugs found were pre-existing in master commit
0d709d15:setEmailSuggestionContextwrong args — Called with{suggestion, type}object instead of two string args. One-line fix.navigate()during render —ResetPasswordPagecallednavigate()in the render phase. Wrapped inuseEffect.validateInput— Direct state mutation offormErrorsobject. Replaced with functionalsetFormErrorsupdater.RedirectLogistration— Both redirect components could render simultaneously inProgressiveProfiling. Made them mutually exclusive.React Query improvements
staleTimeon TPA query — TPA context is effectively static per session; addedstaleTimeto prevent unnecessary background refetches.tpa_hintor other params change.enabledflag on TPA query — Prevents unnecessary fetches when the query result won't be used.setThirdPartyAuthContextBegin— Separated from data sync effect to prevent flash of PENDING state on re-renders.LoginContextsetter types — Fixed types from(fields: T) => voidtoReact.Dispatch<React.SetStateAction<T>>. Removed deadPASSWORD_RESET_ERRORcode path.