feat(learning): per-block content feedback slot wiring#83
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a per-block feedback drawer feature that can render either inline within the courseware sidebar column (sharing space with AskTIM) or as a right-side overlay, depending on the FEEDBACK_SLOT_MODE configuration. It adds several new components (FeedbackDrawerManagerSidebar, FeedbackDrawerSlot), a bundle loader (feedbackBundle.js), and a hook for enrichment data (useFeedbackEnrichment.js), while updating the sidebar coordinator and build configurations. The review feedback highlights a critical issue in SidebarAIDrawerCoordinator.jsx where window.postMessage is called using messageOrigin (the LMS origin) as the target origin. Because these messages are intended for the MFE window itself, this will cause browsers to block the messages in production environments where the MFE and LMS are hosted on different domains. The reviewer recommends updating these calls to use window.location.origin and removing messageOrigin from the affected useEffect dependency arrays.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| useEffect(() => { | ||
| if (prevUnitIdRef.current && prevUnitIdRef.current !== unitId && unitId !== null) { | ||
| // Only send close message if drawer is actually open | ||
| if (showAIDrawerRef.current) { | ||
| window.postMessage( | ||
| { | ||
| type: 'smoot-design::ai-drawer-close', | ||
| type: AI_DRAWER_CLOSE_MESSAGE, | ||
| }, | ||
| messageOrigin | ||
| ); | ||
| } | ||
| setShowAIDrawer(false); | ||
| // Auto-close feedback on unit change too (mirrors AskTIM). | ||
| if (FEEDBACK_SLOT_MODE) { | ||
| if (showFeedbackRef.current) { | ||
| window.postMessage({ type: FEEDBACK_CLOSE_MESSAGE }, messageOrigin); | ||
| } | ||
| setShowFeedback(false); | ||
| } | ||
| } | ||
| prevUnitIdRef.current = unitId; | ||
| }, [unitId, messageOrigin]); |
There was a problem hiding this comment.
Update the target origin to window.location.origin for both postMessage calls in this effect, and remove messageOrigin from the dependency array.
useEffect(() => {
if (prevUnitIdRef.current && prevUnitIdRef.current !== unitId && unitId !== null) {
// Only send close message if drawer is actually open
if (showAIDrawerRef.current) {
window.postMessage(
{
type: AI_DRAWER_CLOSE_MESSAGE,
},
window.location.origin
);
}
setShowAIDrawer(false);
// Auto-close feedback on unit change too (mirrors AskTIM).
if (FEEDBACK_SLOT_MODE) {
if (showFeedbackRef.current) {
window.postMessage({ type: FEEDBACK_CLOSE_MESSAGE }, window.location.origin);
}
setShowFeedback(false);
}
}
prevUnitIdRef.current = unitId;
}, [unitId]);There was a problem hiding this comment.
Pull request overview
This PR wires the Learning MFE legacy slot configuration to support per-block “Send feedback” UI by loading the @mitodl/smoot-design feedback drawer manager bundle either inline (in the notifications_discussions_sidebar.v1 sidebar column) or as a right-side overlay, depending on FEEDBACK_SLOT_MODE.
Changes:
- Adds feedback drawer slot components and a bundle loader (
feedbackBundle.js) to initialize thefeedbackDrawerManagerwith enrichment + authenticated submit client. - Extends
SidebarAIDrawerCoordinator.jsxto coordinate AskTIM vs. inline feedback visibility and share sticky-height behavior. - Updates
learning-mfe-config.env.jsxandbuild_config.yamlto mount/register the new slot files in the legacy build pipeline.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| deployments/mit-ol/mfe_slot_config/legacy/useFeedbackEnrichment.js | Provides a stable getter for enrichment fields (course/unit/url) for feedback submissions. |
| deployments/mit-ol/mfe_slot_config/legacy/SidebarAIDrawerCoordinator.jsx | Coordinates AskTIM vs inline feedback slot and shares sticky-height sizing logic. |
| deployments/mit-ol/mfe_slot_config/legacy/learning-mfe-config.env.jsx | Mounts feedback drawer manager via the learning MFE slot config (inline vs overlay by env). |
| deployments/mit-ol/mfe_slot_config/legacy/FeedbackDrawerSlot.jsx | Inline “slot mode” host that loads/initializes the feedback bundle into a container. |
| deployments/mit-ol/mfe_slot_config/legacy/FeedbackDrawerManagerSidebar.jsx | Overlay-mode initializer for the feedback drawer manager bundle. |
| deployments/mit-ol/mfe_slot_config/legacy/feedbackBundle.js | Dynamic import wrapper for the feedback drawer bundle path + message origin helper. |
| deployments/mit-ol/mfe_slot_config/legacy/build_config.yaml | Registers the new slot files for dagger call mfe build-legacy-configured (learning MFE). |
28b8bbe to
396b755
Compare
9d22a5a to
54e9dae
Compare
Wire the Learning-MFE slots for the per-block "Send feedback" drawer in the MIT OL legacy slot config: - New slot components: FeedbackDrawerManagerSidebar (overlay), FeedbackDrawerSlot (inline), feedbackBundle.js (loads the smoot feedbackDrawerManager bundle + submit/CSRF/prime env config), useFeedbackEnrichment (course/unit/url). - SidebarAIDrawerCoordinator coordinates feedback alongside AskTIM in the shared sidebar column (mutually exclusive, shared sticky-height + full-screen). - Submit uses AskTIM's CSRF-cookie fetch pattern; csrfCookieName + csrfPrimeUrl are env-configurable (FEEDBACK_CSRF_COOKIE_NAME / FEEDBACK_CSRF_PRIME_URL). - learning-mfe-config wires it under the AI-drawer slot; build_config registers the slot files.
54e9dae to
4a12e8b
Compare
What are the relevant tickets?
Per-block content feedback — Learning MFE slot wiring for RFC mitodl/hq#11812 (parent mitodl/hq#11629). Supersedes the earlier ol-infrastructure slot wiring (mitodl/ol-infrastructure#4776), which moves here.
Companion PRs:
mitodl/smoot-design#241— feedback drawer UI bundlemitodl/open-edx-plugins#813— in-iframe "Send feedback" trigger (merged)mitodl/mit-learn#3593— backend endpoint + model (deployed)Description (What does it do?)
Wires the Learning MFE slots for the per-block "Send feedback" drawer, in the MIT OL legacy slot config consumed by
dagger call mfe build-legacy-configured.FeedbackDrawerSlot.jsx— loads the smoot-designfeedbackDrawerManagerbundle inline (variant: 'slot') into the AskTIM sidebar column.feedbackBundle.js— resolves the bundle path (prod/learn/static/…) and readsFEEDBACK_SUBMIT_URL/ CSRF cookie + header + prime names.useFeedbackEnrichment.js— merges course name, unit title, and current URL into the submitted record.SidebarAIDrawerCoordinator.jsx— coordinates the feedback drawer alongside AskTIM in the sharednotifications_discussions_sidebar.v1column (mutually exclusive), sharing the sticky-height + full-screen behavior. AskTIM behavior is unchanged.learning-mfe-config.env.jsx— feedback mounts inline with the AI-drawer slot (no separate enable flag, and no presentation toggle — slot is the only presentation). The per-course gate is the LMS-sideol_openedx_feedback.feedback_enabledCourseWaffleFlag.build_config.yaml— registers the feedback slot files inlearning.extra_slot_filesand pulls the smoot bundle viaextra_npm_bundles.Submits with the same auth mechanism as AskTIM/AiChat —
fetch(submitUrl, { credentials: "include" })reading the CSRF token from a cookie (FEEDBACK_CSRF_COOKIE_NAME, a deployment-prefixed name — not barecsrftoken) into anX-CSRFTokenheader. The feedback drawer UI ships in@mitodl/smoot-design; the in-iframe trigger ships inol_openedx_feedback. This PR is the MFE slot wiring only.TODO before merge
@mitodl/smoot-designinbuild_config.yaml(extra_npm_bundles, currently^6.12.0) to the published version that includes thefeedbackDrawerManagerbundle — i.e. after smoot-design#241 is merged and released to npm. Until then RC/prod builds won't ship the feedback bundle.How can this be tested?
Full local E2E, no deploy — using the content-feedback harness (a local-only proxy stands in for APISIX). Full step-by-step + troubleshooting in the harness README: https://github.com/zamanafzal/mitxonline-local-setup/tree/main/content-feedback
Why a proxy? In RC/prod the MFE and mit-learn are served same-site over HTTPS, so the browser carries mit-learn's session + CSRF cookies on the cross-origin submit (APISIX injects identity). Locally the pieces are on different sites over plain HTTP, so those cookies can't flow (
SameSite=NoneneedsSecure/HTTPS). The proxy authenticates to mit-learn server-side — injects APISIX'sX-Userinfo, primes CSRF, owns the session — so the browser needs no mit-learn cookie. It proves the feature, not the prod cross-origin auth.Setup (once):
ol_openedx_feedback) — renders the megaphone in the unit iframe. Install the plugin into the LMS env, enable XBlock Asides, and turn on the waffle flag (all three required):smoot-design#241checkout (Node 24):content_feedbackmigration applied.Run:
200means it authenticated to mit-learn server-side and is ready):.env.development, thennpm run dev(restart after any env change):Deployment settings (RC / production)
No proxy in prod: APISIX plays the proxy's role — it injects identity (
x-userinfo) and the browser carries the mit-learn session + CSRF cookies directly on the cross-origin submit (same-site over HTTPS). Enabling it is config only; no code path differs. Feedback renders inline — there is noFEEDBACK_SLOT_MODEtoggle.ENABLE_AI_DRAWER_SLOT=true;FEEDBACK_SUBMIT_URL=<mit-learn>/api/v0/content_feedback/(RC vs prod host);FEEDBACK_CSRF_PRIME_URL=<mit-learn>/api/v0/users/me/;FEEDBACK_CSRF_COOKIE_NAME=<mit-learn CSRF_COOKIE_NAME for that env>;FEEDBACK_DRAWER_BUNDLE_PATHoptional (defaults to/learn/static/smoot-design/feedbackDrawerManager.es.js);DEPLOYMENT_NAMEincludesmitxonline. Plus the smoot version bump (TODO above).feedback_enabledwaffle flag per course.CORS_ALLOWED_ORIGINS+CSRF_TRUSTED_ORIGINSinclude the courseware/MFE origin;CSRF_COOKIE_DOMAINset so the CSRF/session cookies are same-site with the courseware origin; add the scoped submission throttle (mitodl/hq#12355) before enabling broadly.Additional Context
notifications_discussions_sidebar.v1slot and are mutually exclusive in that column..ulmocoordinator variant (SidebarAIDrawerCoordinator.ulmo.jsx) does not yet carry the feedback coordination — parity follow-up if the Ulmo release needs it.ContentFeedbackmodel +POST /api/v0/content_feedback/) lives in mit-learn (#3593).