Skip to content

[codex] add rich product description editor#396

Merged
BleedingDev merged 5 commits into
masterfrom
codex/rich-product-description-editor
May 7, 2026
Merged

[codex] add rich product description editor#396
BleedingDev merged 5 commits into
masterfrom
codex/rich-product-description-editor

Conversation

@BleedingDev
Copy link
Copy Markdown
Collaborator

@BleedingDev BleedingDev commented May 7, 2026

Problem

Product descriptions were still edited through Medusa's default plain textarea, which makes rich HTML content hard to maintain and easy to corrupt. This is especially risky for product descriptions because users can accidentally replace structured content with raw text from the default Edit Product drawer.

The content plugin also needed small compatibility fixes in this app so its admin widgets and routes can run reliably inside the Medusa admin shell.

Changes

  • Configure medusa-plugin-content for the Medusa backend and patch the plugin where needed for this project.
  • Add a focused product description widget in the product detail view using MDXEditor as a WYSIWYG editor.
  • Save rich product descriptions back to Medusa's native product.description field as sanitized product-safe HTML.
  • Convert existing HTML descriptions into editable Markdown for MDXEditor, including headings, lists, links, blockquotes, and common inline formatting.
  • Hide Medusa's native Description textarea in the default product Edit drawer so it cannot overwrite rich content.
  • Suppress MDXEditor toolbar and floating link popovers while the product Edit drawer is open, so they do not render above the modal/backdrop.
  • Expand storefront safe HTML rendering to allow the rich tags used by product descriptions, including headings, links, code/pre, blockquotes, and tables.

Validation

  • bunx biome check --write apps/medusa-be/src/admin/widgets/product-description-editor.tsx apps/medusa-be/src/admin/widgets/product-description-editor.css apps/frontend-demo/src/components/safe-html-content.tsx apps/medusa-be/package.json package.json
  • bunx nx run medusa-be:build
  • NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=<local key> bunx nx run frontend-demo:build
  • Browser-checked the product detail page with agent-browser:
    • MDXEditor renders in dark mode.
    • Rich text saves to /admin/products/:id as HTML.
    • Reloading the product restores the rich content.
    • Default Edit Product drawer no longer exposes Description.
    • Saving from the default drawer preserves the rich description.
    • MDX toolbar/link popup stay behind/hidden while the product drawer is open.

Deployment

No Zane/test deployment was performed in this change.

Summary by CodeRabbit

  • New Features

    • Added product description editor widget in admin with markdown editing capabilities and live preview.
  • Improvements

    • Enhanced HTML content handling with expanded support for headings, table elements, and link attributes (href, target, rel).
    • Strengthened internal type safety for sanitiser configuration.

@semanticdiff-com
Copy link
Copy Markdown

semanticdiff-com Bot commented May 7, 2026

Review changes with  SemanticDiff

Changed Files
File Status
  apps/frontend-demo/src/components/safe-html-content.tsx  10% smaller
  apps/medusa-be/medusa-config.ts  0% smaller
  apps/medusa-be/package.json  0% smaller
  apps/medusa-be/src/admin/widgets/product-description-editor.css  0% smaller
  apps/medusa-be/src/admin/widgets/product-description-editor.tsx  0% smaller
  package.json  0% smaller
  patches/medusa-plugin-content@0.2.0.patch Unsupported file format
  pnpm-lock.yaml Unsupported file format

@vercel
Copy link
Copy Markdown

vercel Bot commented May 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
new-engine-ui-storybook Ready Ready Preview, Comment May 7, 2026 9:24pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack

Warning

Rate limit exceeded

@BleedingDev has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 39 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ba59c8a2-7605-428e-88b6-af3b57445569

📥 Commits

Reviewing files that changed from the base of the PR and between 5fe2dd0 and fc5d03c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • apps/frontend-demo/src/components/safe-html-content.tsx
  • apps/medusa-be/medusa-config.ts
  • apps/medusa-be/package.json
  • apps/medusa-be/src/admin/widgets/product-description-editor.css
  • apps/medusa-be/src/admin/widgets/product-description-editor.tsx

Walkthrough

This pull request introduces a product description editor widget using MDX Editor in the Medusa admin, adds HTML↔Markdown conversion support, and improves the SafeHtmlContent component with stronger typing and expanded sanitisation rules. The feature includes comprehensive styling, API integration via React Query, and a patch for the medusa-plugin-content dependency to align endpoint routing.

Changes

Product Description MDX Editor Feature

Layer / File(s) Summary
Dependencies & Plugin Configuration
apps/medusa-be/package.json, apps/medusa-be/medusa-config.ts, package.json
Added @mdxeditor/editor (3.54.0), marked (15.0.12), and medusa-plugin-content (^0.2.0) dependencies; registered medusa-plugin-content plugin in Medusa config; declared pnpm patch for content plugin.
Component Styling
apps/medusa-be/src/admin/widgets/product-description-editor.css
Complete stylesheet with themeable CSS variables for light and dark modes, toolbar/modal-aware visibility rules, CodeMirror editor styling, and typography/layout rules for content elements (headings, lists, blockquotes, code).
Editor Component
apps/medusa-be/src/admin/widgets/product-description-editor.tsx
React component managing MDX editor state, HTML↔Markdown conversions via marked, native textarea hiding using MutationObserver, Save button with React Query mutation, editor plugin configuration (toolbar, shortcuts, tables), and widget zone placement.
Plugin Patch
patches/medusa-plugin-content@0.2.0.patch
Patch for medusa-plugin-content admin UI: introduces shared QueryClient wrapper, removes content_collection_id from item creation payloads, and updates edit loader endpoint to /admin/content/:collectionId/items/:itemId.

SafeHtmlContent Component Type Safety

Layer / File(s) Summary
Type Safety & Patterns
apps/frontend-demo/src/components/safe-html-content.tsx
Introduces SanitizerConfig type derived from DOMPurify config parameter, replacing permissive any; defines precompiled regex patterns for HTML tag/entity detection.
Allowlist Expansion & Refactoring
apps/frontend-demo/src/components/safe-html-content.tsx
Refactors HTML detection to use compiled regex constants; expands ALLOWED_TAGS to include h1–h6 and table elements; expands ALLOWED_ATTR to include href, target, rel; adds lint suppression for dangerouslySetInnerHTML.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: adding a rich product description editor using MDXEditor to replace the plain textarea workflow.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/rich-product-description-editor
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch codex/rich-product-description-editor

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5fe2dd007f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/medusa-be/src/admin/widgets/product-description-editor.tsx
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0f6c0ad4f2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/medusa-be/src/admin/widgets/product-description-editor.tsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/frontend-demo/src/components/safe-html-content.tsx (1)

77-78: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

docs(config): misleading "merge" comment — shallow spread replaces arrays, not extends them

If a caller passes config: { ALLOWED_TAGS: ['p'] }, the spread will completely replace defaultConfig.ALLOWED_TAGS rather than appending to it, contrary to what the comment implies. This is likely acceptable intentional behaviour (full override), but the comment should be corrected to avoid confusion.

📝 Proposed fix
-      // Merge custom config with defaults
+      // Override defaults with any custom config (array fields like ALLOWED_TAGS are fully replaced, not merged)
       const finalConfig = { ...defaultConfig, ...config }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/frontend-demo/src/components/safe-html-content.tsx` around lines 77 -
78, The comment above the assignment to finalConfig is misleading: the spread "{
...defaultConfig, ...config }" performs a shallow override (replacing
arrays/objects) rather than merging/concatenating nested arrays like
ALLOWED_TAGS; update the comment to state that defaultConfig values are
overridden by config (shallow merge/replace) and, if desired, document how to
extend arrays (e.g., by explicitly concatenating defaultConfig.ALLOWED_TAGS and
config.ALLOWED_TAGS) and reference the variables finalConfig, defaultConfig, and
config so readers can find the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/frontend-demo/src/components/safe-html-content.tsx`:
- Line 71: The ALLOWED_ATTR currently permits "target" which allows <a
target="_blank"> without a protective rel; add a
DOMPurify.afterSanitizeAttributes hook at module scope (outside the
SafeHtmlContent component and after the ALLOWED_ATTR/constants) that inspects
anchor elements and, when el.target === "_blank", ensures el.rel contains
"noopener noreferrer" (appending if needed) so every DOMPurify.sanitize call
enforces the protection; no changes to the existing useMemo-based sanitize calls
are required because the hook runs globally for all sanitizations.

In `@apps/medusa-be/package.json`:
- Line 80: The dependency "medusa-plugin-content" in package.json is using a
caret range ("^0.2.0") which prevents the pnpm patch keyed to the exact version
"medusa-plugin-content@0.2.0" from applying; change the version specifier for
medusa-plugin-content in apps/medusa-be/package.json to the exact version
"0.2.0" so the patch key matches and the patch will be applied at install time.

In `@apps/medusa-be/src/admin/widgets/product-description-editor.css`:
- Around line 20-26: The CSS block defining custom properties ends with
--accentTextContrast; Stylelint's declaration-empty-line-before requires an
empty line before the next non-custom declaration, so add a single blank line
before the isolation: isolate; declaration (and any subsequent non-custom
declarations like background and color) in the same rule to satisfy the rule in
product-description-editor.css; locate the declarations by the custom properties
(--accentSolidHover, --accentText, --accentTextContrast) and insert the empty
line immediately before isolation.

In `@apps/medusa-be/src/admin/widgets/product-description-editor.tsx`:
- Around line 233-235: Replace the use of the logical OR in the body object
(currently setting body.description via description || null) with an explicit
falsy check: detect the empty-string case and set null only when description ===
"" (otherwise keep description); update the assignment for body.description (the
variable named description in this component/product-description-editor)
accordingly so it no longer uses || and complies with the ??-over-|| guideline.
- Around line 212-226: The MutationObserver callback should be debounced with
requestAnimationFrame to avoid repeated DOM queries: wrap the direct call to
hideNativeProductDescriptionField passed into new MutationObserver with a small
rAF-based coalescer (e.g., maintain a local let rafId: number | null = null; the
observer callback should if (rafId == null) rafId = requestAnimationFrame(() =>
{ rafId = null; hideNativeProductDescriptionField(); })); also cancel any
pending frame in the cleanup (if (rafId != null) cancelAnimationFrame(rafId));
update references to hideNativeProductDescriptionField and
restoreNativeProductDescriptionField accordingly so the same logic is used when
disconnecting the observer.
- Around line 157-174: The htmlToMarkdown renderer (renderNode) currently falls
back to returning children for unknown tags which strips tables and code blocks;
update renderNode (used by htmlToMarkdown and referenced in onSuccess) to
explicitly handle table, thead, tbody, tr, td/th by converting to Markdown table
rows (generate header row from thead or first tr, separate header with
|---|---|, join cells with |) and to handle code and pre by emitting fenced code
blocks (```language\n...```) for block <pre><code> and inline backticks for
<code> inside text; ensure li/ol/ul handling still trims correctly and that
child rendering is recursively applied when building rows and code block content
so roundtrip load/save doesn't lose structure.

---

Outside diff comments:
In `@apps/frontend-demo/src/components/safe-html-content.tsx`:
- Around line 77-78: The comment above the assignment to finalConfig is
misleading: the spread "{ ...defaultConfig, ...config }" performs a shallow
override (replacing arrays/objects) rather than merging/concatenating nested
arrays like ALLOWED_TAGS; update the comment to state that defaultConfig values
are overridden by config (shallow merge/replace) and, if desired, document how
to extend arrays (e.g., by explicitly concatenating defaultConfig.ALLOWED_TAGS
and config.ALLOWED_TAGS) and reference the variables finalConfig, defaultConfig,
and config so readers can find the code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5395e78a-f3fb-49a9-b392-7679a9c39f2e

📥 Commits

Reviewing files that changed from the base of the PR and between f9226cc and 5fe2dd0.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • apps/frontend-demo/src/components/safe-html-content.tsx
  • apps/medusa-be/medusa-config.ts
  • apps/medusa-be/package.json
  • apps/medusa-be/src/admin/widgets/product-description-editor.css
  • apps/medusa-be/src/admin/widgets/product-description-editor.tsx
  • package.json
  • patches/medusa-plugin-content@0.2.0.patch
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: main
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (8)
**/package.json

📄 CodeRabbit inference engine (CLAUDE.md)

Use pnpm CLI to add dependencies; never edit package.json directly

Files:

  • package.json
  • apps/medusa-be/package.json
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Import UI components using the pattern import { ComponentName } from '@libs/ui/atoms/component-name' or '@libs/ui/molecules/component-name'

Files:

  • apps/frontend-demo/src/components/safe-html-content.tsx
  • apps/medusa-be/medusa-config.ts
  • apps/medusa-be/src/admin/widgets/product-description-editor.tsx
apps/medusa-be/src/admin/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Place admin panel customizations under apps/medusa-be/src/admin

Files:

  • apps/medusa-be/src/admin/widgets/product-description-editor.css
  • apps/medusa-be/src/admin/widgets/product-description-editor.tsx
apps/medusa-be/src/{api,modules,workflows,admin,subscribers,jobs}/**

📄 CodeRabbit inference engine (AGENTS.md)

In Medusa backend applications, organize custom code using the directory structure: api/, modules/, workflows/, admin/, subscribers/, jobs/

Files:

  • apps/medusa-be/src/admin/widgets/product-description-editor.css
  • apps/medusa-be/src/admin/widgets/product-description-editor.tsx
apps/medusa-be/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/**/*.{ts,tsx,js,jsx}: Always use braces for if/else statements, even for single statements
Declare one variable per const/let statement; do not use multiple declarations on one line
Use nullish coalescing operator (??) instead of logical OR (||) for null/undefined defaults
Write comments explaining why code exists, not what it does; rely on self-documenting code with clear naming
Always validate data before type casting; do not use 'as Type' without prior validation

Files:

  • apps/medusa-be/medusa-config.ts
  • apps/medusa-be/src/admin/widgets/product-description-editor.tsx
apps/medusa-be/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/**/*.{ts,tsx}: Do not use non-null assertion operator (!) in TypeScript code
Annotate the type of generic field access (e.g., result[field]) as unknown before type guards
Use Modules.* and ContainerRegistrationKeys.* constants instead of hardcoding strings for container resolution

Files:

  • apps/medusa-be/medusa-config.ts
  • apps/medusa-be/src/admin/widgets/product-description-editor.tsx
apps/medusa-be/**/medusa-config.ts

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/**/medusa-config.ts: Conditional module loading must be evaluated at BUILD time via environment variables, not runtime
Cross-module provider dependencies must declare 'dependencies' array in module config for injection into provider container

Files:

  • apps/medusa-be/medusa-config.ts
apps/medusa-be/src/admin/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

Import admin environment variables using import.meta.env.VITE_* and .DEV, .PROD suffixes

Files:

  • apps/medusa-be/src/admin/widgets/product-description-editor.tsx
🧠 Learnings (5)
📚 Learning: 2026-02-05T14:43:17.404Z
Learnt from: KaiUweCZE
Repo: NMIT-WR/new-engine PR: 324
File: apps/medusa-be/package.json:0-0
Timestamp: 2026-02-05T14:43:17.404Z
Learning: Validate and enforce React 19 compatibility across monorepo workspaces. Since Medusa UI supports React 19 via root package.json overrides and Medusa Cloud prerequisites show React 19 overrides for npm workspaces, ensure workspace root and all relevant package.json files align with React 19 (18+ requirement is satisfied). When reviewing, verify that overrides exist in the root package.json and that dependent packages in apps or packages directories declare React 19 (or compatible) in their peerDependencies or dependencies as appropriate for workspace usage.

Applied to files:

  • package.json
  • apps/medusa-be/package.json
📚 Learning: 2026-05-07T19:05:58.339Z
Learnt from: redeyecz
Repo: TechsioCZ/new-engine PR: 390
File: apps/medusa-be/package.json:78-81
Timestamp: 2026-05-07T19:05:58.339Z
Learning: When reviewing changes to `package.json`, do not automatically flag dependency additions/removals as "manually edited" or as "bypassing the pnpm lockfile" just because the `package.json` diff shows only that file changed. First verify whether `pnpm-lock.yaml` is missing the corresponding entries. Since `pnpm add` updates both `package.json` and `pnpm-lock.yaml` together, legitimate changes can appear in the `package.json` diff while still being properly tracked in the lockfile.

Applied to files:

  • package.json
  • apps/medusa-be/package.json
📚 Learning: 2025-12-16T19:45:17.746Z
Learnt from: BleedingDev
Repo: NMIT-WR/new-engine PR: 207
File: libs/ui/src/molecules/select.tsx:50-50
Timestamp: 2025-12-16T19:45:17.746Z
Learning: When reviewing Tailwind classes in TSX/TS files, prefer using square brackets for arbitrary CSS values and complex expressions. Specifically: - Do not use the parentheses syntax (z-(--z-index)) for anything beyond simple CSS variable references; this syntax auto-wraps in var() and cannot handle calc or complex functions. - Use the square brackets syntax (e.g., h-[calc(var(--available-height)-var(--spacing-content))]) for calc expressions, var with calc, and any complex CSS expressions. This rule applies broadly to Tailwind v4 usage in TSX code across the project.

Applied to files:

  • apps/frontend-demo/src/components/safe-html-content.tsx
  • apps/medusa-be/src/admin/widgets/product-description-editor.tsx
📚 Learning: 2026-02-10T15:19:38.125Z
Learnt from: KaiUweCZE
Repo: NMIT-WR/new-engine PR: 328
File: apps/frontend-demo/src/components/molecules/header-search.tsx:75-99
Timestamp: 2026-02-10T15:19:38.125Z
Learning: In apps/frontend-demo/src/components/molecules/header-search.tsx, the dataset invariant is that every product has a handle property. The current fallback in handleSelect calls handleSearch(selected) when a product exists but lacks a handle, and this is acceptable only if that invariant always holds. Actionable guidance: 1) Document the invariant and rely on it in code reviews. 2) Add a defensive guard or a test to verify no product arrives without a handle. For future-proofing, consider guarding the fallback (e.g., if (product?.handle) { handleSearch(product) } else { log/throw or skip }) and ensure a log message when the invariant is violated.

Applied to files:

  • apps/frontend-demo/src/components/safe-html-content.tsx
📚 Learning: 2026-05-07T12:06:55.558Z
Learnt from: redeyecz
Repo: TechsioCZ/new-engine PR: 375
File: apps/medusa-be/src/admin/widgets/order-payment-reminder.tsx:3-11
Timestamp: 2026-05-07T12:06:55.558Z
Learning: In Medusa admin code under `apps/medusa-be/src/admin/` (admin widgets/routes), UI components must be imported from `medusajs/ui` (the Medusa admin UI kit). Do not apply the project-wide `libs/ui/atoms/*` and `libs/ui/molecules/*` import conventions to these files, and never flag `medusajs/ui` imports in this admin directory as violating the `libs/ui` convention.

Applied to files:

  • apps/medusa-be/src/admin/widgets/product-description-editor.tsx
🪛 ast-grep (0.42.1)
apps/frontend-demo/src/components/safe-html-content.tsx

[warning] 95-95: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Stylelint (17.10.0)
apps/medusa-be/src/admin/widgets/product-description-editor.css

[error] 23-23: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🔇 Additional comments (5)
apps/frontend-demo/src/components/safe-html-content.tsx (1)

6-6: Type extraction is correct for DOMPurify 3.3.0.

The catch-all overload sanitize(source: string | Node, config?: Config): string is indeed the last (and widest) signature in DOMPurify's type definitions, so Parameters<typeof DOMPurify.sanitize>[1] correctly resolves to Config | undefined. The NonNullable extraction yields the full Config interface as intended. The pattern is sound for the project's DOMPurify version and the implementation uses the type correctly.

apps/medusa-be/medusa-config.ts (1)

76-79: LGTM!

The plugin registration is straightforward and consistent with the other entries in the plugins array.

package.json (1)

89-91: LGTM!

The patchedDependencies entry correctly points to the patch file using the exact version key, which is the required format for pnpm patches. React 19 overrides are already in place.

patches/medusa-plugin-content@0.2.0.patch (1)

14-27: ⚡ Quick win

No issue - the content_collection_id removal is correct and justified.

The patch restructures the API from a flat to a nested resource design:

  • Old API: /admin/content-items/:itemId (collection ID required in request body)
  • New API: /admin/content/:collectionId/items/:itemId (collection ID in URL path)

The loader function was updated to extract collectionId from URL parameters and use it in the nested path, which is the standard REST pattern for hierarchical resources. The removal of content_collection_id from request payloads is correct because the backend now derives the collection association from the URL path itself, not from the request body. This design eliminates the risk of orphaned items and aligns with the plugin's upstream API evolution.

apps/medusa-be/src/admin/widgets/product-description-editor.tsx (1)

183-184: ⚡ Quick win

Confirmed: marked does not sanitise HTML – verified that frontend renders safely via DOMPurify

You're correct that marked.parse with no sanitisation options passes raw HTML through unchanged. However, the risk is mitigated on the storefront: the SafeHtmlContent component in apps/frontend-demo/src/components/safe-html-content.tsx sanitises product descriptions at render time using DOMPurify v3.3.0 with a strict allowlist (forbids <script>, <iframe>, <form>, event handlers like onerror/onclick). Both product-info.tsx and product-tabs.tsx use SafeHtmlContent when displaying product.description.

The architecture stores unsanitised HTML in the database and sanitises at render time per context (e.g., the n1 app escapes HTML entities for XML feeds at line 96 of apps/n1/src/app/api/feed/products/route.ts). This is a valid pattern when client-side sanitisation is consistently applied.

The original concern about the PR description stating "sanitized, product-safe HTML" is partially valid—sanitisation happens on the frontend, not during storage, which differs from the stated expectation.

Comment thread apps/frontend-demo/src/components/safe-html-content.tsx
Comment thread apps/medusa-be/package.json Outdated
Comment thread apps/medusa-be/src/admin/widgets/product-description-editor.css
Comment thread apps/medusa-be/src/admin/widgets/product-description-editor.tsx
Comment thread apps/medusa-be/src/admin/widgets/product-description-editor.tsx
Comment thread apps/medusa-be/src/admin/widgets/product-description-editor.tsx
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3d786f8ef2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/frontend-demo/src/components/safe-html-content.tsx
…description-editor

# Conflicts:
#	apps/medusa-be/package.json
#	pnpm-lock.yaml
@blacksmith-sh
Copy link
Copy Markdown

blacksmith-sh Bot commented May 7, 2026

Blacksmith Account Suspended

This Blacksmith account requires additional verification. Jobs targeting Blacksmith runners will not be picked up and will remain queued until they timeout.

Please contact Blacksmith Support for assistance.

Copy link
Copy Markdown
Collaborator Author

Review follow-up status:

  • All GitHub review threads are resolved: 9 total, 0 unresolved.
  • Replied directly to the 3 Codex threads that were resolved manually.
  • Current head: fc5d03c84b0888001040d72266d8febad1885024.
  • Checks are green: main, Analyze (javascript-typescript), CodeRabbit, Vercel, Vercel Preview Comments.

Remaining visible bot comments are top-level status/walkthrough comments, not unresolved review conversations.

@BleedingDev BleedingDev merged commit 12fa81e into master May 7, 2026
5 checks passed
@BleedingDev BleedingDev deleted the codex/rich-product-description-editor branch May 7, 2026 22:14
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.

1 participant