Skip to content

feat(components): added Markdown component (#DS-5225) - #447

Open
artembelik wants to merge 2 commits into
mainfrom
feat/DS-5225
Open

feat(components): added Markdown component (#DS-5225)#447
artembelik wants to merge 2 commits into
mainfrom
feat/DS-5225

Conversation

@artembelik

@artembelik artembelik commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added a Markdown component for rendering formatted Markdown content.
    • Supports headings, lists, links, images, tables, code blocks, line breaks, custom parser options, styling, and forwarded refs.
    • Sanitizes rendered content to help prevent unsafe HTML and script execution.
    • Added comprehensive documentation and interactive examples.
  • Bug Fixes

    • Added coverage for content updates, customization, parser options, and sanitization behavior.

@artembelik artembelik self-assigned this Aug 6, 2026
Copilot AI lite review requested due to automatic review settings August 6, 2026 09:40
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@artembelik, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 34 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e5e85085-b6e6-4c4c-a4e1-c073d95b76c2

📥 Commits

Reviewing files that changed from the base of the PR and between 1cd3c75 and 5b2ecb9.

📒 Files selected for processing (4)
  • packages/components/src/components/Markdown/Markdown.test.tsx
  • packages/components/src/components/Markdown/Markdown.tsx
  • packages/components/src/components/Markdown/types.ts
  • tools/public_api_guard/components/Markdown.api.md
📝 Walkthrough

Walkthrough

Adds a client-side Markdown component that parses and sanitizes Markdown, applies component styling, supports parser options and forwarded refs, and exposes documentation, Storybook examples, tests, and public API metadata.

Changes

Markdown component

Layer / File(s) Summary
Dependencies and public contract
package.json, packages/components/package.json, packages/components/src/components/Markdown/types.ts, packages/components/src/components/Markdown/index.ts, packages/components/src/components/index.ts, tools/api-extractor/config.json, tools/public_api_guard/components/Markdown.api.md
Adds optional dompurify and marked peer dependencies, defines MarkdownProps, and exposes the component through package exports and API reports.
Parsing and sanitized rendering
packages/components/src/components/Markdown/Markdown.tsx
Parses Markdown with marked, sanitizes browser output with DOMPurify, applies CSS module classes, forwards refs and div props, and renders empty content during SSR.
Presentation and examples
packages/components/src/components/Markdown/Markdown.module.css, packages/components/src/components/Markdown/Markdown.stories.tsx, packages/components/src/components/Markdown/Markdown.mdx
Adds styles, Storybook examples, and documentation for Markdown elements, line breaks, parser options, installation, usage, and props.
Behavior validation
packages/components/src/components/Markdown/Markdown.test.tsx
Tests refs, custom classes, rendered elements, parser options, child updates, and removal of scripts and event-handler attributes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Markdown
  participant marked
  participant DOMPurify
  participant React
  Markdown->>marked: Parse Markdown with markedOptions
  marked-->>Markdown: Return generated HTML
  Markdown->>DOMPurify: Sanitize HTML in the browser
  DOMPurify-->>Markdown: Return sanitized HTML
  Markdown->>React: Render with dangerouslySetInnerHTML
Loading

Suggested labels: enhancement

Suggested reviewers: kamilemeleev, copilot

🚥 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 clearly and concisely identifies the addition of the Markdown component, which is the main change in the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/DS-5225

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.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit 5b2ecb9):

https://react-koobiq-next--prs-447-2v2xguuh.web.app

(expires Tue, 11 Aug 2026 10:07:30 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: fc29847d4a9e5cb1adf458c76a9b681c76e2eeff

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 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 `@packages/components/package.json`:
- Around line 45-56: Update the dependency declarations in package.json so
dompurify and marked are required rather than optional peer dependencies, since
Markdown directly imports them. Remove their optional peer metadata while
preserving the existing Markdown export behavior.

In `@packages/components/src/components/Markdown/types.ts`:
- Around line 5-7: Update MarkdownProps in
packages/components/src/components/Markdown/types.ts to omit
dangerouslySetInnerHTML alongside children. In
packages/components/src/components/Markdown/Markdown.tsx, destructure and
discard dangerouslySetInnerHTML before spreading the remaining props onto the
div. Regenerate tools/public_api_guard/components/Markdown.api.md so the public
API reflects the updated type.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0e62241d-b177-4229-9755-ebb327fb4469

📥 Commits

Reviewing files that changed from the base of the PR and between 3bf6163 and 1cd3c75.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • package.json
  • packages/components/package.json
  • packages/components/src/components/Markdown/Markdown.mdx
  • packages/components/src/components/Markdown/Markdown.module.css
  • packages/components/src/components/Markdown/Markdown.stories.tsx
  • packages/components/src/components/Markdown/Markdown.test.tsx
  • packages/components/src/components/Markdown/Markdown.tsx
  • packages/components/src/components/Markdown/index.ts
  • packages/components/src/components/Markdown/types.ts
  • packages/components/src/components/index.ts
  • tools/api-extractor/config.json
  • tools/public_api_guard/components/Markdown.api.md

Comment thread packages/components/package.json
Comment on lines +5 to +7
export interface MarkdownProps extends Omit<
ComponentPropsWithoutRef<'div'>,
'children'

@coderabbitai coderabbitai Bot Aug 6, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 3 'dangerouslySetInnerHTML|ComponentPropsWithoutRef' \
  packages/components/src/components/Markdown/types.ts \
  packages/components/src/components/Markdown/Markdown.tsx \
  tools/public_api_guard/components/Markdown.api.md

Repository: koobiq/react-components

Length of output: 2772


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Markdown source outline:\n'
ast-grep outline packages/components/src/components/Markdown/Markdown.tsx --view expanded || true

printf '\nMarkdown source relevant section:\n'
sed -n '1,130p' packages/components/src/components/Markdown/Markdown.tsx

printf '\nReact prop merge behavior probe:\n'
node - <<'JS'
function shallowCloneProps(props) {
  const result = {};
  for (const key in props) {
    if (Object.prototype.hasOwnProperty.call(props, key)) {
      result[key] = props[key];
    }
  }
  return result;
}
const input = {
  ref: { current: null },
  className: 'markdown',
  children: 'Markdown text',
  dangerouslySetInnerHTML: { __html: '<p>bad html</p>' }
};
const { children, markedOptions, className, ...other } = input;
const outerDivProps = shallowCloneProps(other);
outerDivProps.children = children;
outerDivProps.className = className;
console.log(JSON.stringify(outerDivProps));
JS

Repository: koobiq/react-components

Length of output: 3325


🌐 Web query:

React DOMProps omit dangerouslySetInnerHTML component type ComponentPropsWithoutRef

💡 Result:

In React, ComponentPropsWithoutRef is a utility type that extracts all props accepted by a native DOM element (or a component), excluding the ref attribute [1][2][3]. Because dangerouslySetInnerHTML is a standard React prop supported by all intrinsic HTML elements, it is included in the type definition returned by ComponentPropsWithoutRef<'element'> [4][5]. For example, when using ComponentPropsWithoutRef<'div'>, the resulting type includes dangerouslySetInnerHTML as an optional property [5]. If you are creating a custom component that accepts standard DOM props but you wish to explicitly exclude or restrict dangerouslySetInnerHTML, you must use Omit to remove it [6]. Common patterns involving these concepts: 1. Extracting props: You can use ComponentPropsWithoutRef to create props interfaces that mirror standard HTML elements [1][7]. type MyComponentProps = React.ComponentPropsWithoutRef<'div'>; 2. Omitting dangerouslySetInnerHTML: If you want to prevent users of your component from passing dangerouslySetInnerHTML, you can explicitly omit it [6]. type SafeComponentProps = Omit<React.ComponentPropsWithoutRef<'div'>, 'dangerouslySetInnerHTML'>; 3. Usage of dangerouslySetInnerHTML: This prop expects an object with an html key, containing the raw HTML string [4][8]. It should be used with extreme caution due to the risk of Cross-Site Scripting (XSS) vulnerabilities, especially when handling user-provided data [4][9]. React also enforces that you cannot pass both children and dangerouslySetInnerHTML simultaneously [4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

node - <<'JS'
const domProps = { ref: { current: null }, className: 'markdown' };
const outerProps = { ...domProps, children: 'Markdown text' };
const input = { ...outerProps, dangerouslySetInnerHTML: { __html: '<p>bad html</p>' } };

const checked = { children: input.children, markedOptions: input.markedOptions, className: input.className };
const { dangerouslySetInnerHTML: _, ...other } = input;
const filtered = { ...checked, ...other };

const invalid = { dangerouslysetinnerhtml: input.dangerouslySetInnerHTML, children: input.children };
const valid = { children: input.children };

console.log('Invalid outer div props include children and dangerouslySetInnerHTML:', filtered.children != null && filtered.dangerouslySetInnerHTML != null);
console.log('Explicit invalid JSX equivalent throws when constructed in React:', typeof React === 'object' && typeof React.createElement === 'function'
  ? (() => { try { React.createElement('div', invalid); console.log('React.createElement did not throw'); } catch (error) { console.log('React.createElement threw:', error.message); } })()
  : 'React not loaded');
console.log('Filtered outer div props include children and dangerouslySetInnerHTML:', filtered.dangerouslySetInnerHTML != null);
console.log('Filtered outer div props have only valid content:', JSON.stringify(valid));
JS

Repository: koobiq/react-components

Length of output: 476


Exclude and filter dangerouslySetInnerHTML.

ComponentPropsWithoutRef<'div'> includes dangerouslySetInnerHTML, and Markdown passes spread props to a div that already renders children. Pass both props and React throws, so omit it from MarkdownProps and extract it before forwarding the remaining DOM props. Regenerate the API report to match the public type.

📍 Affects 3 files
  • packages/components/src/components/Markdown/types.ts#L5-L7 (this comment)
  • packages/components/src/components/Markdown/Markdown.tsx#L81-L94
  • tools/public_api_guard/components/Markdown.api.md#L16-L19
🤖 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 `@packages/components/src/components/Markdown/types.ts` around lines 5 - 7,
Update MarkdownProps in packages/components/src/components/Markdown/types.ts to
omit dangerouslySetInnerHTML alongside children. In
packages/components/src/components/Markdown/Markdown.tsx, destructure and
discard dangerouslySetInnerHTML before spreading the remaining props onto the
div. Regenerate tools/public_api_guard/components/Markdown.api.md so the public
API reflects the updated type.

✅ Addressed in commit 5b2ecb9

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@KamilEmeleev я это исправил, как считаешь это справедливое замечание?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

да, справедливое замечание. проблема некритичная - поэтому minor, но дополнительная защита здесь не помешает.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

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

Adds a new Markdown component to @koobiq/react-components, exposing it via the package public API and Storybook docs/stories, using marked for parsing and DOMPurify for sanitization.

Changes:

  • Introduces Markdown component implementation, styles, types, tests, stories, and MDX documentation.
  • Exposes the component through the components barrel exports and updates API Extractor config + API report.
  • Adds marked and dompurify to the workspace lockfile and declares them as peer dependencies of @koobiq/react-components.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/public_api_guard/components/Markdown.api.md Adds the generated API Extractor report for the new component.
tools/api-extractor/config.json Registers Markdown for API Extractor/public API guarding.
pnpm-lock.yaml Locks new dependencies (marked, dompurify) and related graph updates.
packages/components/src/components/Markdown/types.ts Defines the public MarkdownProps type surface.
packages/components/src/components/Markdown/Markdown.tsx Implements Markdown-to-HTML rendering + sanitation and class injection.
packages/components/src/components/Markdown/Markdown.test.tsx Adds unit tests for rendering, options, and sanitization behavior.
packages/components/src/components/Markdown/Markdown.stories.tsx Adds Storybook stories covering common Markdown structures.
packages/components/src/components/Markdown/Markdown.module.css Adds CSS module styles for rendered Markdown elements.
packages/components/src/components/Markdown/Markdown.mdx Adds Storybook documentation page for the new component.
packages/components/src/components/Markdown/index.ts Exports the component and its types from the component folder.
packages/components/src/components/index.ts Re-exports Markdown from the components barrel.
packages/components/package.json Declares marked/dompurify as peer deps (currently marked optional).
package.json Adds marked/dompurify to the workspace root dependencies for development/tooling.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/components/src/components/Markdown/Markdown.tsx
Comment thread packages/components/package.json
"@koobiq/design-tokens": "^3.17.2",
"@koobiq/react-icons": "^12.0.0",
"dompurify": "^3.4.13",
"marked": "^18.0.9",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Предлагаю использовать react-markdown: он преобразует Markdown в React-элементы, поэтому результат становится частью обычного React-дерева и без дополнительной обработки участвует в SSR в Next.js. Стандартные Markdown-элементы также можно переопределять компонентами дизайн-системы через components.

marked возвращает HTML-строку, которую нужно вставлять через dangerouslySetInnerHTML, а для недоверенного контента — дополнительно санитизировать. Это рабочий подход, но он создаёт отдельный пайплайн обработки HTML и хуже вписывается в компонентную архитектуру React.

Choosing the Right Markdown Renderer for React

Пример использования react-markdown в компонентной библиотеке

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.

3 participants