Gravity UI Markdown Editor

repository·main·Indexed 19 days ago

https://github.com/gravity-ui/markdown-editor

A React-based Markdown editor featuring WYSIWYG (visual) and Markup (source) modes. Built on ProseMirror and CodeMirror, it supports deep extensibility through text-bound extensions and provides submodules for direct access to core ProseMirror and CodeMirror modules. The editor includes support for LaTeX via @gravity-ui/markdown-editor-latex-extension, Page Constructor blocks via @gravity-ui/markdown-editor-page-constructor-extension, and a useFilesGallery hook for managing media galleries.

Tokens
109.4K
Snippets
393
Records
570
Agent score
64%

What's inside @gravity-ui/markdown-editor

  1. Access CodeMirror core modules via the CM submodule

    main

    The @gravity-ui/markdown-editor package provides a cm submodule that re-exports several core CodeMirror modules. This allows you to use CodeMirror functionality (like state management, view handling, and language support) without needing to install the individual @codemirror/* packages separately in your project.

    Supported re-exports include:

    • @codemirror/autocomplete
    • @codemirror/commands
    • @codemirror/language
    • @codemirror/state
    • @codemirror/view
    import {EditorView} from '@gravity-ui/markdown-editor/cm/view';
  2. How Editor and Toolbar Presets work together

    main

    The Markdown Editor uses two distinct types of presets to manage functionality and UI:

    1. Editor Preset (MarkdownEditorPreset): Defines the core functionality and available extensions (e.g., bold, italic, etc.). This is configured via the preset property in useMarkdownEditor.

      • Available values: 'zero', 'commonmark', 'default', 'yfm', 'full'.
    2. Toolbar Preset (ToolbarsPreset): Defines which buttons appear in the toolbars and their specific order. This is configured via the toolbarsPreset property in the MarkdownEditorView component.

    Relationship Logic:

    • If you do not provide a toolbarsPreset, the editor automatically selects a toolbar preset that matches the name of your MarkdownEditorPreset.
    • If you do provide a toolbarsPreset, it overrides the automatic selection, allowing you to use a custom UI layout regardless of the editor's functional preset.
    // Case 1: Automatic matching (Editor 'default' -> Toolbar 'default')
    const editor = useMarkdownEditor({ preset: 'default' });
    <MarkdownEditorView editor={editor} />
    
    // Case 2: Manual override (Editor 'default' -> Custom Toolbar)
    const editor = useMarkdownEditor({ preset: 'default' });
    <MarkdownEditorView editor={editor} toolbarsPreset={myCustomToolbar} />
  3. Understand WYSIWYG vs Markup modes for extensions

    main

    The Markdown Editor operates in two modes which serve different purposes during extension development:

    1. Markup Mode: The editor performs standard conversion of markdown (or advanced markdown like YFM) into HTML. Use this mode to verify that your markdown plugin correctly translates syntax into the expected HTML code.
    2. WYSIWYG Mode: This is where editor extensions actually live. Extensions enhance the visual, interactive functionality of the editor.

    When developing, ensure your markdown plugin works in Markup mode first, then implement the corresponding extension for the WYSIWYG functionality.

  4. Best practices for writing editor visual tests

    main

    When writing tests for the editor's view layer and plugins in WYSIWYG mode, follow these guidelines:

    Input Emulation

    Use editor.fill, editor.press, or editor.pressSequentially to emulate user input within the contenteditable region.

    Locators

    • React-based components: Prefer locating elements via data-qa attributes (especially for plugins defining node views via React).
    • Non-React elements: For elements rendered via toDOM in a ProseMirror node spec, use class names or specific attributes.

    Click Testing

    Choose the appropriate method based on your needs:

    • locator.click()
    • element.dispatchEvent('click', params) (Use { bubbles: true } for events involving bubbling)
    • page.mouse.click(x, y)

    Clipboard

    Note that Playwright clipboard support varies across browsers; account for this when testing copy-paste functionality.

  5. Understand context-specific toolbar components

    main

    Context-specific toolbar components are UI elements that depend on specific editor extensions to function correctly. Because their operation is tied to the presence of these extensions, they cannot be added directly to the standard MenuBar component. Instead, they are intended to be used in contexts where the required extensions are active.

    Examples of such components include:

    • Heading selection buttons
    • Text coloring buttons
    • List formatting buttons
  6. When to use @gravity-ui/markdown-editor

    main

    Use this editor when:

    • You need a dual-mode editor that switches between WYSIWYG (ProseMirror) and raw markup (CodeMirror).
    • You are working with Markdown or YFM (YAML Front Matter) syntax.
    • You require an extensible editor (e.g., adding LaTeX, Mermaid, HTML, or GPT extensions).

    Do NOT use this editor when:

    • Read-only rendering: If you only need to render Markdown to HTML without editing, use @diplodoc/transform instead.
    • Plain text input: For simple multiline text, use TextArea from @gravity-ui/uikit.
    • Non-Markdown rich text: This editor is specifically optimized for Markdown/YFM.
  7. Update visual test screenshots

    main

    When reference screenshots need to be updated, use the following commands (ideally within the Docker environment):

    • Update all snapshots: pnpm run playwright:docker:update
    • Update snapshots for a specific test (using --grep or -g): pnpm run playwright:docker:update --grep 'test name'
    • Update only snapshots that failed in the last run: pnpm run playwright:docker:update --last-failed
    pnpm run playwright:docker:update --last-failed
  8. Follow Commit and PR standards

    main

    The project uses Conventional Commits to manage versioning and changelogs.

    Commit Types

    • feat: New features (triggers a minor release).
    • fix: Bug fixes (triggers a patch release).
    • refactor: Code structure changes without affecting functionality.
    • perf: Performance improvements.
    • build: Changes to the build system or dependencies.
    • chore: Miscellaneous tasks that don’t modify source code or tests.
    • ci: Updates to CI configuration.
    • docs: Documentation updates.
    • test: Adding or updating tests.

    Changelog Rules

    • Only feat, fix, refactor, and perf are included in the automatic changelog.
    • Other types (e.g., docs, chore) are excluded from the changelog.

    Breaking Changes

    Do not use feat! to signal breaking changes. Instead, create or comment on an issue tagged with breaking change in the Planned Breaking Changes issue list.

  9. Run visual tests in Docker

    main

    Always run tests in Docker to match CI results. You can run tests from either the monorepo root or the demo/ subpackage.

    From the monorepo root

    • Run all tests: pnpm run test:e2e
    • Display report: pnpm run test:e2e:report
    • Filter by test name: pnpm run test:e2e --grep '<name>'

    From the demo/ subpackage

    • Run all tests: pnpm run playwright:docker
    • Clear cache: pnpm run playwright:docker:clear
    • Display report: pnpm run playwright:docker:report
    • Filter by test name: pnpm run playwright:docker --grep '<name>'
    # From root
    pnpm run test:e2e
    
    # From demo/
    pnpm run playwright:docker