feat(storefront): render product description as markdown
Some checks failed
Deploy — Staging / Detect changed apps (push) Successful in 16s
Deploy — Staging / Lint, Typecheck & Test (push) Successful in 1m29s
Deploy — Staging / Build & push — ${{ matrix.app }} (push) Failing after 49s
Deploy — Staging / Deploy to staging VPS (push) Has been skipped

- Add react-markdown dependency to storefront
- Use ReactMarkdown in ProductDetailDescriptionSection for formatted descriptions

Made-with: Cursor
This commit is contained in:
2026-03-08 16:27:48 +03:00
parent 0bd0d90f45
commit f6156c78d1
3 changed files with 1167 additions and 14 deletions

View File

@@ -19,6 +19,7 @@
"@stripe/react-stripe-js": "^5.6.0", "@stripe/react-stripe-js": "^5.6.0",
"@stripe/stripe-js": "^8.8.0", "@stripe/stripe-js": "^8.8.0",
"framer-motion": "^11.0.0", "framer-motion": "^11.0.0",
"lucide-react": "^0.400.0" "lucide-react": "^0.400.0",
"react-markdown": "^10.1.0"
} }
} }

View File

@@ -1,11 +1,16 @@
import ReactMarkdown from "react-markdown";
type ProductDetailDescriptionSectionProps = { type ProductDetailDescriptionSectionProps = {
/** Product description (HTML or plain text); server-rendered in initial HTML per SEO. */ /** Product description (Markdown); server-rendered in initial HTML per SEO. */
description?: string | null; description?: string | null;
}; };
const descriptionClasses =
"max-w-3xl text-sm leading-relaxed text-[var(--foreground)]/80 [&_ol]:mb-3 [&_ol]:list-inside [&_ol]:list-decimal [&_p:last-child]:mb-0 [&_p]:mb-3 [&_ul]:mb-3 [&_ul]:list-inside [&_ul]:list-disc";
/** /**
* Description content for the PDP tabs section. * Description content for the PDP tabs section.
* Renders inside a parent <section> provided by ProductDetailTabsSection. * Renders Markdown as HTML inside a parent <section> provided by ProductDetailTabsSection.
* If empty, shows a short fallback message. * If empty, shows a short fallback message.
*/ */
export function ProductDetailDescriptionSection({ export function ProductDetailDescriptionSection({
@@ -15,10 +20,9 @@ export function ProductDetailDescriptionSection({
typeof description === "string" && description.trim().length > 0; typeof description === "string" && description.trim().length > 0;
return hasContent ? ( return hasContent ? (
<div <div className={descriptionClasses}>
className="max-w-3xl text-sm leading-relaxed text-[var(--foreground)]/80 [&_ol]:mb-3 [&_ol]:list-inside [&_ol]:list-decimal [&_p:last-child]:mb-0 [&_p]:mb-3 [&_ul]:mb-3 [&_ul]:list-inside [&_ul]:list-disc" <ReactMarkdown>{description!.trim()}</ReactMarkdown>
dangerouslySetInnerHTML={{ __html: description!.trim() }} </div>
/>
) : ( ) : (
<p className="text-sm text-[var(--muted)]">No description available.</p> <p className="text-sm text-[var(--muted)]">No description available.</p>
); );

1162
package-lock.json generated

File diff suppressed because it is too large Load Diff