Conversation
Rename to @openedx/frontend-app-notifications, swap fedx-scripts for the openedx CLI, move shell-shared runtime deps (paragon, react, react-router, react-query) to peerDependencies with broadened semver, add @openedx/frontend-base peer dep, and add tsc-alias, turbo, and nodemon as dev deps. Co-Authored-By: Claude <noreply@anthropic.com>
Add tsconfig.json, tsconfig.build.json, app.d.ts, and eslint.config.js; switch babel, jest, and eslint configs to @openedx/frontend-base/tools; replace Makefile with the doc-standard build plus workspace targets; adopt standard .gitignore and minimal .npmignore; drop .env files, .eslintrc.js, .eslintignore, and module.config.js.example. Trivial source touch-ups to satisfy the new lint config: remove two stale import/* disable comments, split two one-line reducers, and add a TODO + react-hooks/rules-of-hooks stopgap in tours/data/hooks.js pointing to Phase 4.5 for the real fix. Co-Authored-By: Claude <noreply@anthropic.com>
Replace all @edx/frontend-platform* imports with @openedx/frontend-base
(flat export surface); rename getConfig -> getSiteConfig; drop the
mergeConfig process.env shim from setupTest.js (Phase 6 replaces it
with mergeSiteConfig + site.config); rename config keys LMS_BASE_URL
-> lmsBaseUrl and ACCOUNT_SETTINGS_URL -> accountSettingsUrl (the
latter still needs wiring; TODO points to Phase 5/9); drop a dead
jest.mock('@src/generic/messages', ...) copy-paste artifact.
SCSS already used Paragon 23 CSS variables and had no @import
statements, so no style work was needed.
Co-Authored-By: Claude <noreply@anthropic.com>
Rename all .js/.jsx under src/ to .ts/.tsx with git mv, type API
boundaries and component props, replace PropTypes with TS interfaces,
and drop prop-types from peerDependencies. Swap the legacy AppContext
read for useAuthenticatedUser, and source ACCOUNT_SETTINGS_URL via
useAppConfig (Phase 11 tracks the ExternalRoute follow-up).
Also lands the minimum scaffolding needed for the test suite to run
under the new stack: site.config.test.tsx, src/__mocks__/{svg,file}.js,
and a mergeSiteConfig(siteConfig) call in setupTest.tsx. Phases 5 and 6
will own the canonical versions of these.
Co-Authored-By: Claude <noreply@anthropic.com>
Rename src/index.tsx to src/NotificationsTray.tsx and add
src/{constants,app,slots,index}.ts so the package now default-exports
a frontend-base App config while keeping the existing component and
hook named exports.
Co-Authored-By: Claude <noreply@anthropic.com>
…Phase 4.5) Replace the custom-hook + fat-context data layer with react-query queries and mutations: - useAppNotifications is a useQuery over getNotificationCounts. - useNotificationList is a useInfiniteQuery with keepPreviousData; pages are flattened for consumers. - useMarkNotificationSeen / useMarkNotificationRead / useMarkAllNotificationsRead are mutations that optimistically patch the list cache and invalidate the counts query. - useNotification is kept as a backwards-compatible facade exposing the three mutation mutateAsync functions. - The tours data layer moves to react-query the same way; the Phase 2 useTourConfiguration stopgap is removed. notificationsContext is slimmed to appName + handleActiveTab; all data state now lives in the react-query cache. Components receive notificationAppData via props and read the notification list / mutations directly from the new hooks. setupTest.tsx exposes createTestQueryClient and each test now wraps its tree in a QueryClientProvider. The obsolete RequestStatus constants module is deleted. Co-Authored-By: Claude <noreply@anthropic.com>
Register the notifications App in site.config.test.tsx and add a site.config.dev.tsx that mounts the shell/header/footer apps plus a stub authenticated page so the bell can be exercised in dev mode. app.scss pulls in the shell stylesheet. Replace the legacy public/index.html template, which still referenced process.env and htmlWebpackPlugin.options (both unavailable under frontend-base), with a minimal static one. Move the dev port from 2002 (taken locally) to 1992. Co-Authored-By: Claude <noreply@anthropic.com>
No PNG imports exist in src/ (Paragon only pulls in SVGs), so the file.js mock and its moduleNameMapper entry are dead weight. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Register the bell as INSERT_BEFORE the authenticated menu in the desktopRight and mobileRight header slots, gated on authenticated: true. Reorder setupTest.tsx to import site.config before @openedx/frontend-base to avoid a circular require: frontend-base -> site.config -> app -> slots -> NotificationsTray -> tours/messages -> defineMessages (still unbound). Co-Authored-By: Claude <noreply@anthropic.com>
BREAKING CHANGE: package renamed from @edx/frontend-plugin-notifications to @openedx/frontend-app-notifications and rewritten against @openedx/frontend-base. Legacy frontend-plugin-framework slot wiring is replaced by an App config that registers the notifications bell against the unified header's desktopRight/mobileRight slots. Data layer migrated to @tanstack/react-query v5; source fully converted to TypeScript.
Rewrite README.rst and catalog-info.yaml for @openedx/frontend-app-notifications, switch .releaserc to release @edx/frontend-plugin-notifications from main and @openedx/frontend-app-notifications as alpha prereleases from frontend-base, and add the frontend-base branch to ci.yml and release.yml triggers. Co-Authored-By: Claude <noreply@anthropic.com>
Replace useAppConfig().ACCOUNT_SETTINGS_URL with getUrlByRouteRole, so the account-settings link is sourced from frontend-base's unified internal/external route lookup. When the resolved URL is relative, use react-router's Link to keep navigation in-SPA; fall back to Hyperlink with target=_blank for absolute URLs. Co-Authored-By: Claude <noreply@anthropic.com>
Move landing page and mocks into dev/ as a self-contained app. Mocks are stateful (mark-seen/mark-read update an in-memory store), cover the CSRF endpoint, and shim window.open so clicks show effects in place. Also set basename so /notifications resolves under PUBLIC_PATH. Co-Authored-By: Claude <noreply@anthropic.com>
The filter used the raw tourName while the lookup ran it through camelToConstant, so no value satisfied both. Use the constant-cased key everywhere and wire the tour into the dev site as a smoke test. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
3 tasks
3 tasks
Matches the learner-dashboard pattern. Drops the other re-exports, since they were originally meant for backward compatibility, but that turned out not to be possible. Co-Authored-By: Claude <noreply@anthropic.com>
|
🎉 This PR is included in version 3.0.0-alpha.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Co-Authored-By: Claude <noreply@anthropic.com>
The shell stylesheet moved from a SCSS file to a JS manifest at @openedx/frontend-base/shell/style. Imports it directly from site.config.dev.tsx and drops the now-redundant site.scss. Mirrors openedx/frontend-base#232. Co-Authored-By: Claude <noreply@anthropic.com>
|
🎉 This PR is included in version 3.0.0-alpha.2 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Adds an npm run build:ci script that exercises the real app graph through webpack, and wires it into the CI workflow after the existing build step. Co-Authored-By: Claude <noreply@anthropic.com>
INSERT_BEFORE the desktopSecondaryLinks widget rather than the auth menu so the bell renders to the left of any secondary nav links (e.g. the help button from frontend-base#245) instead of between those links and the user avatar dropdown. Mobile is left as-is — mobileRight has no secondary-nav cluster to position relative to (just bell + auth menu), so INSERT_BEFORE the mobile auth menu remains correct. Refs openedx/frontend-base#245 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🎉 This PR is included in version 3.0.0-alpha.3 🎉 The release is available on: Your semantic-release bot 📦🚀 |
4 tasks
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This branch renames the package from
@edx/frontend-plugin-notificationsto@openedx/frontend-app-notificationsand migrates it end-to-end to@openedx/frontend-base. The three legacy plugin slots collapse into the unified header'sdesktopRight/mobileRightslots, so Studio and learning pages pick up the notifications bell without extra wiring.Alongside the rename the source is fully converted to TypeScript, the data layer is ported to
@tanstack/react-queryv5 (no more bespoke context-based fetching), the account-settings link is resolved via the newgetUrlByRouteRolemechanism, and a self-contained dev app underdev/ships stateful mocks so the bell can be exercised without a live LMS.Semantic-release is configured to publish alphas from this branch as
@openedx/frontend-app-notifications@3.0.0-alpha.x, keepingmainfree to continue shipping@edx/frontend-plugin-notifications@2.xfor consumers that haven't migrated off frontend-platform yet. The PR is kept in draft while the new package lands in downstream sites and we gather feedback.BREAKING CHANGE: This won't work with legacy MFEs after this.
Testing
In dev mode, this mocks most API calls so that notifications can be seen without actually having any real ones. Start the LMS, run
npm run devand go to:http://apps.local.openedx.io:1992/notifications
Click around the notifications in the top right:
You should also see the example "tour" on the left:
LLM usage notice
Built with assistance from Claude.
Migration plan (historical artifact)
Migrate
frontend-plugin-notifications->@openedx/frontend-app-notificationsContext
frontend-plugin-notifications(published as@edx/frontend-plugin-notifications) is currently a peer-dependency library consumed by Open edX MFEs via@openedx/frontend-plugin-framework. It exports aNotificationsTraycomponent, whichtutor-contrib-platform-notificationsinjects into three legacy slots (header_desktop_secondary_menu.v1,header_learning_help.v1,studio_header_search_button_slot.v1) throughPLUGIN_SLOTS.frontend-base replaces frontend-plugin-framework with a new runtime. Plugins are now
Appconfigs that register widgets againstfrontend-baseslot IDs, and the three legacy slot IDs no longer exist - the new unified header usesorg.openedx.frontend.slot.header.desktopRight.v1and.mobileRight.v1. This plan converts the repo end-to-end followingfrontend-base/docs/how_tos/migrate-frontend-app.md, renames the package to@openedx/frontend-app-notifications, ports the data layer to@tanstack/react-queryv5, and wires the notification bell into the new header via an exportedAppconfig. The matchingtutor-contrib-platform-notificationsupdate is tracked separately and is out of scope for this plan.Decisions locked in (from pre-plan review)
@edx/frontend-plugin-notificationsto@openedx/frontend-app-notifications(both the org prefix andplugin->app). The repo directory name stays as-is.src/Notification/data/hook.jsto@tanstack/react-queryv5. Remove any<QueryClientProvider />- the shell already provides one.NotificationsTray,Notifications,useAppNotifications,useNotification) alongside the defaultAppconfig export.Each phase below is runnable standalone: it lists its inputs, actions, and acceptance checks so that a fresh agent (or the user) can pick it up without prior conversation context.
Phase 0 - Preconditions and peer repos
We install
@openedx/frontend-basefrom npm - no local build required. The sibling checkouts are reference material only:~/src/openedx/master/frontend-base- read-only reference (slot IDs,Appshape, migration doc atdocs/how_tos/migrate-frontend-app.md, example config attest-site/src/example-page/). Do not build.~/src/openedx/master/tutor-contrib-platform-notifications- reference for the legacy slot wiring. The matching Tutor-plugin update is tracked separately and out of scope here.Run this repo's work from
~/src/openedx/master/frontend-plugin-notifications(it should be the current working directory).Reference app for "what a converted plugin looks like":
~/src/openedx/master/frontend-app-learner-dashboard, checked out on branchfrontend-base.Phase 1 - package.json rewrite [DONE - commit a196b02]
Files:
package.json.@edx/frontend-platform,@openedx/frontend-build,@edx/reactifex,husky,glob(keep@openedx/frontend-plugin-frameworkremoval in mind if present; this repo only documents it in README - verify and remove any usage).package-lock.jsonandnode_modules/.peerDependencies(broadened semver):@openedx/paragon: ^23@tanstack/react-query: ^5(introduced by the Phase 4.5 data-layer port)react: ^18,react-dom: ^18react-router: ^6,react-router-dom: ^6@openedx/frontend-baseto peer deps with the dev sentinel:"@openedx/frontend-base": "^1.0.0-alpha || 0.0.0-dev"(only alpha tags are published on npm as of 2026-04; match the converted reference app).scriptswith the standard block from the migration doc (build/clean/dev/i18n_extract/lint/lint:fix/prepack/snapshot/test). UsePORT=1992 PUBLIC_PATH=/notificationsfordev(the legacy 2002 collides with another local MFE).tsc-alias,turbo,nodemon, plus any TS types the source ends up needing.name: rename to@openedx/frontend-app-notifications. Setversionto0.0.0-dev(the convention for frontend-base apps - semantic-release replaces it at publish time).author: "Open edX",license: AGPL-3.0.exports:{ ".": "./dist/index.js", "./app.scss": "./dist/app.scss" }(add the scss export only if anapp.scssends up being shipped - likely not for a plugin-only library).files: ["/dist"].sideEffects: ["*.css", "*.scss"].workspaces: ["packages/*"].atlasTranslations: { path, dependencies: ["@openedx/frontend-base"] }(see Phase 7).npm installfresh.Acceptance:
npm installsucceeds with no peer-dep errors;package.jsoncontains no references tofrontend-build,frontend-platform, orfedx-scripts.Phase 2 - Build/tool config files [DONE - commit e812b0d]
Files:
tsconfig.json,tsconfig.build.json,Makefile,app.d.ts,babel.config.js,jest.config.js,eslint.config.js,.gitignore,.npmignore, delete.eslintrc.js/.eslintignore/module.config.js.example/.env*.tsconfig.jsonextending@openedx/frontend-base/tools/tsconfig.json, with@src/*path alias and include list from the doc.tsconfig.build.jsonextending./tsconfig.json, rootDirsrc, outDirdist, excluding tests/mocks/setupTest.js.app.d.tswith the/// <reference types="@openedx/frontend-base" />triple-slash directive plussite.configand*.svgmodule declarations.babel.config.js:createConfig('babel')from@openedx/frontend-base/tools.jest.config.js:createConfig('test', {...})withsetupFilesAfterEnv: ['<rootDir>/src/setupTest.js'], coverage ignore forsrc/setupTest.jsandsrc/i18n, andmoduleNameMapperfor\\.svg$,\\.png$,^@src/(.*)$.eslint.config.js:createLintConfig({ files: ['src/**/*', 'site.config.*'] })from@openedx/frontend-base/tools.Makefile: replace withcleanandbuildtargets exactly as in the migration doc (tsc -> copy scss/assets -> tsc-alias). Keep the existingpull_translations/extract_translations/i18n.*targets but update them to useopenedx translations:pull(Phase 7). Addbin-link,build-packages,clean-packages,dev-packages,dev-sitetargets from the doc..gitignore: adopt the standard block from the doc (addspackages/,/.turbo,/turbo.json,/*.tgz,src/i18n/transifex_input.json,src/i18n/messages.ts,src/i18n/messages/)..npmignore: reduce tonode_modules..env,.env.development,.env.test(if present),module.config.js.example,.eslintrc.js,.eslintignore.Acceptance:
npm run lintandmake clean && make buildsucceed and producedist/.Phase 3 - Convert source imports and modernize structure [DONE - commit 369aa20]
Files under
src/.@edx/frontend-platform*import with@openedx/frontend-base. In particular:getConfig->getSiteConfig(audit each call site for shape differences).getAuthenticatedHttpClient,snakeCaseObject,camelCaseObject,AppContext,useIntl,defineMessages,FormattedMessage,initializeMockApp-> all from@openedx/frontend-base.AppProvideris used anywhere, rename toSiteProvider. (Unlikely in this repo - double-check.)@importto@usein any SCSS file. Auditcommon/style.scssandNotification/notification.scssfor Paragon variable imports and migrate to Paragon 23 CSS variables where any SCSS variable is referenced.src/plugin-slots(if it ever appears) tosrc/slots. This repo currently has none; skip unless later work introduces them.process.env.*references and move them to config (or hard-code constants where appropriate)..js/.jsxto.ts/.tsxconversion in Phase 3.5; Phase 3 stops at import/SCSS/env rewrites so the TS conversion lands on a stable base.Acceptance:
rg "frontend-platform|frontend-build|fedx-scripts" srcreturns nothing; jest tests still pass (re-run after Phase 3.5).Phase 3.5 - Convert source to TypeScript [DONE - commit e32d3ce]
The repo is only ~30 source files (18
.js+ 12.jsx), small enough to convert wholesale and end up with a fully-typed library. Doing it before Phase 4 means the new entry files land in an already-TS tree, and Phase 4.5'shook.tsport is natural rather than an optional rename.Files: everything under
src/..js->.tsand.jsx->.tsxacrosssrc/. Usegit mvto preserve blame.tsconfig.json(Phase 2 already stages this) and runnpx tsc --noEmitto drive the conversion. Fix errors iteratively rather than silencing withany.Notification/data/api.js-> add response interfaces forgetNotificationCounts,getNotificationsList,markNotificationsAsSeen,markNotificationRead.Notification/data/hook.js-> type the hook return shapes (these will be replaced by react-query types in Phase 4.5, but an interim typing catches regressions).PropTypesto TS interfaces. Drop theprop-typesimport once a component is fully typed; remove theprop-typespeer dep frompackage.jsonwhen no usages remain.NotificationContextand any other contexts get an explicitContext<T>type.src/Notification/tours/*tour step configs: add a union/enum type for tour step IDs so Phase 4 wiring catches typos.jest.mock(...)blocks typically needjest.requireActual<typeof import('...')>(...)spreads to preserve typing.anys (e.g., legacy untyped third-party surfaces), preferunknown+ narrowing, or a named alias liketype TodoTyped = anywith a// TODOso they're greppable.Acceptance:
rg -l "\.jsx?$" srcreturns nothing;npx tsc --noEmitpasses;npm testpasses;prop-typesis removed frompackage.jsonif no component still uses it.Phase 4 - New library entry layout [DONE - commit 7c47255]
Create the standard frontend-base library surface:
src/constants.ts:export const appId = 'org.openedx.frontend.notifications';(finalize ID with user in Phase 11).src/app.ts: the exportedAppconfig (populated in Phase 9 with slot wiring). Minimal stub now.src/providers.ts: empty/no-op unless the existing contexts need to be promoted to app-level providers. The current code uses two React Contexts internally - leave them co-located withNotification/.src/routes.tsx: optional; this plugin does not own routes, so this file may be omitted.src/slots.tsx: the list ofSlotOperationentries this app contributes (populated in Phase 9).src/index.ts: re-exports only. Export theAppconfig as default plus named exports forNotificationsTray,Notifications,useAppNotifications,useNotificationso existing consumers keep working.src/index.tsx->src/NotificationsTray.tsx(or similar, post-Phase 3.5) and have the newsrc/index.tsre-export it. Keep the<StrictMode>wrapper behavior.src/setupTest.ts: addmergeSiteConfig(siteConfig)per the doc sogetSiteConfig()works inside tests.Acceptance:
npm run buildproduces adist/whoseindex.jsexports the App config plus the public components/hooks;npm teststill passes.Phase 4.5 - Port data layer to @tanstack/react-query v5 [DONE - commit 9b36be7]
Files (post-Phase 3.5, all
.ts/.tsx):src/Notification/data/hook.ts,src/Notification/data/api.ts, call sites insrc/Notification/index.tsx,NotificationSections.tsx,NotificationTabs.tsx,tours/data/hooks.ts.queryOptions, object-formuseQuery({ queryKey, queryFn }),useMutation({ mutationFn }),isPendinginstead ofisLoadingfor queries, etc.).useAppNotifications()->useQueryagainstgetNotificationCounts(and whatever seedsshowNotificationsTray); key:['notifications', 'appData'].useNotification()splits into:useNotificationList(appName, page)->useQuerywith key['notifications', 'list', appName, page], callinggetNotificationsList. Paginated; useplaceholderData: keepPreviousDatato keep the tray stable while paging.useMarkNotificationSeen()->useMutationcallingmarkNotificationsAsSeen; on success invalidate['notifications'].useMarkNotificationRead()->useMutationcallingmarkNotificationRead(single-notification and per-app variants). On success, optimistically update the list cache for the affected notification id, then invalidate.notificationsContextfor UI-only state (active tab, popover open, scroll refs). Remove data fields (notifications array, pagination cursor) from the context - components should read them from react-query via the new hooks.<QueryClientProvider />here. The frontend-base shell provides one globally. Tests that mount components in isolation need to wrap with aQueryClientProviderin a test helper (add tosetupTest.jsor arenderWithProvidersutil).axios-mock-adapterassertions where needed with react-query-aware patterns (either mock the API module or mock the HTTP layer and wrap in a freshQueryClientper test withretry: false, gcTime: 0).useAppNotifications/useNotificationas the exported hook names sosrc/index.tsre-exports and the public API remain stable. Their internals change; signatures should stay compatible where feasible.useTourConfigurationintours/data/hooks.ts. It is declaredasyncbut callsuseIntl/useMemoinside (hooks cannot be called from async functions). Phase 2 silenced it with// eslint-disable-next-line react-hooks/rules-of-hooks+ aTODO:comment as a stopgap; the real fix is to drop theasync(it awaits nothing) and collapse the inneruseMemo-returning lambda into a plain hook body. Remove the disable comments and the TODO once fixed.Acceptance:
npm testpasses, the tray still opens and paginates innpm run dev,rg "useState.*notifications|useState.*pagination" srcshows state management has moved out of components for data fetching, andrg "TODO:.*Phase" srcis clean for the 4.5 TODO.Phase 5 - site.config files for tests and dev [DONE - commit ca48e34]
Files:
site.config.test.tsx,site.config.dev.tsx.site.config.test.tsx: minimalSiteConfigper the doc, registering this plugin'sAppso the slot wiring is exercised. Useenvironment: 'test' as SiteConfig['environment'].site.config.dev.tsx: registersshellApp,headerApp,footerAppfrom@openedx/frontend-base, plus this plugin'sApp, plus a simple authenticated test page to see the bell live. Import./app.scssat the top. Point auth URLs athttp://local.openedx.io:8000.app.scssthat@uses@openedx/frontend-base/shell/app.scssand any local styles from the plugin. This is only required to enablenpm run dev; it is not shipped viaexports.Acceptance:
npm run devboots the shell and renders the bell in the authenticated header.Phase 6 - Jest and test mocks [DONE - commit 3d7c6a7]
Files under
src/__mocks__/,src/setupTest.ts, existing*.test.tsxfiles.src/__mocks__/svg.js(module.exports = 'SvgURL';) andsrc/__mocks__/file.js(module.exports = 'FileMock';). Only needed if tests import SVG/PNG; verify and skip mocks whose types aren't actually imported. Keep these as.jsso Jest's module name mapper resolves them without TS transform.jest.mock('@edx/frontend-platform/...')calls in existing tests, replace withjest.mock('@openedx/frontend-base', () => ({ ...jest.requireActual<typeof import('@openedx/frontend-base')>('@openedx/frontend-base'), <mocked exports> }))as per the doc (typed form, since tests are TS after Phase 3.5).initializeMockApp+mergeSiteConfigcalls tosetupTest.tsif tests rely ongetSiteConfig()(the notifications data layer reads config URLs; almost certainly needed).Acceptance:
npm testpasses with coverage.Phase 7 - i18n [DONE - commit 07562b9]
descriptionfields to every message insrc/Notification/messages.jsandsrc/Notification/tours/messages.js(required by the new ESLint config)."translations:pull": "openedx translations:pull"topackage.jsonscripts.atlasTranslationsblock topackage.jsonpointing attranslations/frontend-app-notifications/src/i18n/messageswithdependencies: ["@openedx/frontend-base"].src/i18n/index.jswith:src/i18n/index.ts:export { default } from './messages';src/i18n/messages.d.ts:import type { SiteMessages } from '@openedx/frontend-base'; declare const messages: SiteMessages; export default messages;Makefile'spull_translationstarget to invokenpm run translations:pull -- --atlas-options="$(ATLAS_OPTIONS)".Acceptance:
npm run translations:pullgeneratessrc/i18n/messages.ts; tests still pass.Phase 8 - Workspaces (turbo + nodemon) [DONE - commit 5f74319]
Files:
turbo.site.json,nodemon.json, Makefile additions (already staged in Phase 2),.gitignoreadditions.Copy the exact contents for
turbo.site.jsonandnodemon.jsonfrom the migration doc. Confirm Makefile targetsbuild-packages,clean-packages,dev-packages,dev-site,bin-linkare present. The bin-link target referencespackages/frontend-base/package.json; leave as-is.Acceptance:
mkdir -p packages/frontend-base && sudo mount --bind ../frontend-base packages/frontend-base && npm install && npm run dev:packagesstarts the dev server with a live frontend-base.Phase 9 - Wire NotificationsTray into the frontend-base header [DONE - commit 69a6eb1]
This is the functional replacement for the Tutor
PLUGIN_SLOTSblock.Populate
src/slots.tsxwith two widget registrations:Notes:
header_desktop_secondary_menu.v1,header_learning_help.v1,studio_header_search_button_slot.v1) collapse intodesktopRight.v1+mobileRight.v1because frontend-base has a single unified header with no Studio-specific or learning-specific slots. Studio and course pages inherit the same bell placement for free.INSERT_BEFORErelated IDs against the livefrontend-base/shell/header/app.tsxat the time of execution (file:shell/header/app.tsx, lines 57-64 and 126-132 as of this planning). If anINSERT_BEFORErelative op is unsupported or the target ID moved, fall back toAPPENDand order with widget priority.NotificationsTrayalready handles its own popover via Paragon'sOverlayTrigger- no additional wrapping required.Populate
src/app.ts:And
src/index.ts:Acceptance: In
npm run dev, logging in renders a bell in the header right section on both desktop and mobile layouts; clicking it opens the notifications popover.Phase 10 - Housekeeping on the rename [DONE - commits ccdedf3, c8a1ab2]
Because the npm package name is changing:
README.rstheader, install snippets, and any@edx/frontend-plugin-notificationsstring references to@openedx/frontend-app-notifications.catalog-info.yamlmetadata if it references the old name..github/workflows/release.yml/ semantic-release config if the package name is hard-coded. Confirm the release token still has publish rights for the@openedxscope on npm; if not, flag to the user before the first release.atlasTranslations.pathand any Transifex references to the new name/slug.Phase 11 - Optional follow-ups
Non-blocking improvements that surfaced during the main migration and can be picked up opportunistically. Each entry notes what was done as a stopgap, what the preferred shape is, and a rough acceptance check. Add new entries here when you hit something similar.
ACCOUNT_SETTINGS_URL -> route role [DONE - commit 875f1b1]
Phase 3.5 wired the account-settings link via
useAppConfig().ACCOUNT_SETTINGS_URL, a direct port of the legacy env-driven config. The frontend-base convention for cross-app URLs is the route-role lookup: apps and external URLs alike are tagged with arolestring, resolved viagetUrlByRouteRole(role)which checks bothapps[].routes[].handle.rolesandexternalRoutes. This is the right fit here because the notifications plugin should not care whether the account MFE is deployed as a sibling app or as an external site; only the role matters.src/Notification/index.tsxnow readsgetUrlByRouteRole('org.openedx.frontend.role.account')and appends#notificationsin-component (the hash is a notifications-UI concern, not an account-app concern).site.config.dev.tsxandsite.config.test.tsxregister the role underexternalRoutesinstead of stuffingACCOUNT_SETTINGS_URLinto per-appconfig.tutor-contrib-platform-notificationsneeds the matching switch to populateexternalRoutesinstead of the old env var (tracked in the separate Tutor-plugin update).Acceptance:
rg "ACCOUNT_SETTINGS_URL" srcreturns nothing; the settings icon still links correctly in dev.Verification
Run from the repo root with
@openedx/frontend-baseinstalled from npm (no bind-mount required unless Phase 8 was done):npm installnpm run lintnpm test -- --no-coverage --maxWorkers=8make clean && make build(producesdist/)npm run dev(ornpm run dev:packagesif Phase 8 bind-mount is set up) and in a browser:npm packand install the tgz into a local site checkout to confirm theAppdefault export and named component exports resolve correctly.Critical files at a glance
package.json- Phase 1tsconfig.json,tsconfig.build.json,Makefile,app.d.ts- Phase 2babel.config.js,jest.config.js,eslint.config.js,.gitignore,.npmignore- Phase 2src/**/*.{js,jsx}- import rewrites (Phase 3), then wholesale rename to.ts/.tsxwith typing (Phase 3.5)src/index.tsx-> split intosrc/NotificationsTray.tsx+src/index.ts+src/app.ts+src/slots.tsx+src/constants.ts- Phase 4/9src/Notification/data/hook.ts- react-query port (Phase 4.5)src/Notification/messages.ts,src/Notification/tours/messages.ts- adddescription(Phase 7)src/i18n/index.ts+src/i18n/messages.d.ts- Phase 7site.config.dev.tsx,site.config.test.tsx,src/app.scss- Phase 5turbo.site.json,nodemon.json- Phase 8 (optional)