Skip to content

feat(learning): per-block content feedback slot wiring#83

Open
zamanafzal wants to merge 1 commit into
mainfrom
zafzal/11629-content-feedback-slots
Open

feat(learning): per-block content feedback slot wiring#83
zamanafzal wants to merge 1 commit into
mainfrom
zafzal/11629-content-feedback-slots

Conversation

@zamanafzal

@zamanafzal zamanafzal commented Jul 2, 2026

Copy link
Copy Markdown

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 bundle
  • mitodl/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.

  • New slot components:
    • FeedbackDrawerSlot.jsx — loads the smoot-design feedbackDrawerManager bundle inline (variant: 'slot') into the AskTIM sidebar column.
    • feedbackBundle.js — resolves the bundle path (prod /learn/static/…) and reads FEEDBACK_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 shared notifications_discussions_sidebar.v1 column (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-side ol_openedx_feedback.feedback_enabled CourseWaffleFlag.
  • build_config.yaml — registers the feedback slot files in learning.extra_slot_files and pulls the smoot bundle via extra_npm_bundles.

Submits with the same auth mechanism as AskTIM/AiChatfetch(submitUrl, { credentials: "include" }) reading the CSRF token from a cookie (FEEDBACK_CSRF_COOKIE_NAME, a deployment-prefixed name — not bare csrftoken) into an X-CSRFToken header. The feedback drawer UI ships in @mitodl/smoot-design; the in-iframe trigger ships in ol_openedx_feedback. This PR is the MFE slot wiring only.

TODO before merge

  • Bump @mitodl/smoot-design in build_config.yaml (extra_npm_bundles, currently ^6.12.0) to the published version that includes the feedbackDrawerManager bundle — 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=None needs Secure/HTTPS). The proxy authenticates to mit-learn server-side — injects APISIX's X-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):

  1. LMS trigger (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):
    docker exec -it <lms-container> pip install -e /path/to/open-edx-plugins/src/ol_openedx_feedback   # then restart LMS
    # LMS admin → XBlock Asides Config (/admin/lms_xblock/xblockasidesconfig/) → add entry, check Enabled
    docker exec <lms-container> ./manage.py lms waffle_flag ol_openedx_feedback.feedback_enabled --everyone --create
  2. smoot-design drawer bundle — loaded at runtime (like AskTIM), not an npm dep, so build it and stage it into the MFE static dir from a smoot-design#241 checkout (Node 24):
    nvm use 24 && yarn build:bundles:feedback          # → dist/bundles/feedbackDrawerManager.es.js (+ .map)
    cp dist/bundles/feedbackDrawerManager.es.js dist/bundles/feedbackDrawerManager.es.js.map \
      /path/to/frontend-app-learning/public/static/smoot-design/
    # confirm served: curl -s -o /dev/null -w '%{http_code}\n' http://localhost:2000/static/smoot-design/feedbackDrawerManager.es.js  → 200
  3. mit-learn — stack up with the content_feedback migration applied.

Run:

  1. Start the proxy and confirm the shim works (prime → 200 means it authenticated to mit-learn server-side and is ready):
    cd content-feedback && ./run_feedback_proxy.sh
    curl -s -o /dev/null -w "prime: %{http_code}\n" http://localhost:8899/api/v0/users/me/ -H "Origin: http://localhost:2000"
  2. Set the MFE .env.development, then npm run dev (restart after any env change):
    DEPLOYMENT_NAME=…mitxonline…
    ENABLE_AI_DRAWER_SLOT=true
    FEEDBACK_SUBMIT_URL=http://localhost:8899/api/v0/content_feedback/
    FEEDBACK_CSRF_PRIME_URL=http://localhost:8899/api/v0/users/me/
    FEEDBACK_CSRF_COOKIE_NAME=<local mit-learn CSRF cookie name>
    
  3. Open a course unit → click the Send feedback megaphone → drawer opens inline in the sidebar → pick a reaction + comment → Submit → success.
  4. Verify a row landed:
    docker exec mit-learn-web-1 python manage.py shell -c \
      "from content_feedback.models import ContentFeedback as C; print(C.objects.count())"
  5. Mutual exclusion: opening AskTIM / discussions closes the feedback drawer; navigating units closes it.

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 no FEEDBACK_SLOT_MODE toggle.

  • Learning MFE (this repo): 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_PATH optional (defaults to /learn/static/smoot-design/feedbackDrawerManager.es.js); DEPLOYMENT_NAME includes mitxonline. Plus the smoot version bump (TODO above).
  • LMS: enable XBlock Asides Config + the feedback_enabled waffle flag per course.
  • mit-learn: CORS_ALLOWED_ORIGINS + CSRF_TRUSTED_ORIGINS include the courseware/MFE origin; CSRF_COOKIE_DOMAIN set 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

  • Feedback and AskTIM share the notifications_discussions_sidebar.v1 slot and are mutually exclusive in that column.
  • The .ulmo coordinator variant (SidebarAIDrawerCoordinator.ulmo.jsx) does not yet carry the feedback coordination — parity follow-up if the Ulmo release needs it.
  • The backend endpoint (ContentFeedback model + POST /api/v0/content_feedback/) lives in mit-learn (#3593).

Copilot AI review requested due to automatic review settings July 2, 2026 10:53

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread deployments/mit-ol/mfe_slot_config/legacy/SidebarAIDrawerCoordinator.jsx Outdated
Comment on lines 191 to 212
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]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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]);

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 the feedbackDrawerManager with enrichment + authenticated submit client.
  • Extends SidebarAIDrawerCoordinator.jsx to coordinate AskTIM vs. inline feedback visibility and share sticky-height behavior.
  • Updates learning-mfe-config.env.jsx and build_config.yaml to 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).

Comment thread deployments/mit-ol/mfe_slot_config/legacy/SidebarAIDrawerCoordinator.jsx Outdated
@zamanafzal
zamanafzal force-pushed the zafzal/11629-content-feedback-slots branch 2 times, most recently from 28b8bbe to 396b755 Compare July 14, 2026 07:59
@zamanafzal
zamanafzal force-pushed the zafzal/11629-content-feedback-slots branch 2 times, most recently from 9d22a5a to 54e9dae Compare July 22, 2026 14:13
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants