Skip to content

feat: added page-constructor extension#1096

Merged
separatrixxx merged 13 commits intomainfrom
pc-extension
Apr 25, 2026
Merged

feat: added page-constructor extension#1096
separatrixxx merged 13 commits intomainfrom
pc-extension

Conversation

@separatrixxx
Copy link
Copy Markdown
Contributor

@separatrixxx separatrixxx commented Apr 22, 2026

Creating a page-constructor extension in a separate package

Summary by Sourcery

Add a new page-constructor extension package for the markdown editor, including schema, node view, actions, toolbar integration, and preview support, and wire it into the monorepo tooling and release configuration.

New Features:

  • Introduce a @gravity-ui/markdown-editor-page-constructor-extension package providing a YFM page-constructor node with WYSIWYG editing and preview inside the markdown editor.
  • Add a toolbar action and button to insert page-constructor blocks into editor content.
  • Provide a React HOC to enable split-mode/page view rendering of page-constructor blocks using the Page Constructor runtime.

Enhancements:

  • Integrate the new page-constructor extension package into ESLint, TypeScript, pnpm workspace catalogs, and release-please configuration.

Documentation:

  • Document installation and usage of the page-constructor extension, toolbar item, and HOC in a new package README.

Tests:

  • Add unit tests to verify parsing and serialization of YFM page-constructor markdown blocks, including multiline YAML and usage inside blockquotes.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 22, 2026

Reviewer's Guide

Adds a new standalone @gravity-ui/markdown-editor-page-constructor-extension package that defines a YFM Page Constructor node/spec, React-based node view with live preview and editing, toolbar integration, split-mode preview HOC, and wires the package into the monorepo tooling (linting, build, tests, and release-please).

File-Level Changes

Change Details Files
Introduce YFM Page Constructor ProseMirror node/spec and markdown (de)serialization for ::: page-constructor blocks.
  • Define YfmPageConstructor node name, token name, attributes (content, entityId), and default entity ID constants.
  • Register markdown-it transform for page-constructor and add a ProseMirror atom block node with attrs, toDOM, and toMd serialization emitting ::: page-constructor fenced blocks.
  • Generate entity IDs on parse and validate/repair them at node-view construction time to ensure uniqueness across the document.
  • Add unit tests that cover parsing/serialization of simple, multiline YAML, and blockquote-embedded page-constructor blocks using ExtensionsManager.
packages/page-constructor-extension/src/YfmPageConstructorSpecs/index.tsx
packages/page-constructor-extension/src/YfmPageConstructorSpecs/const.ts
packages/page-constructor-extension/src/YfmPageConstructor.test.ts
Implement React-based WYSIWYG node view with live Page Constructor preview, editing controls, and ProseMirror integration.
  • Create WYfmPageConstructorNodeView class that renders into a non-editable container via Portal and wires onChange/onRemove back to ProseMirror transactions.
  • Add YfmPageConstructorView React component that manages shared editing state per-entity, local YAML content, and a popup menu for edit/remove actions, with a double-click-to-edit interaction.
  • Implement PageConstructorEditMode with side-by-side Page Constructor preview and YAML textarea editor, using markdown-editor auto-save utilities and a fixed TextArea wrapper to ensure focus behavior.
  • Handle stopEvent and STOP_EVENT_CLASSNAME to prevent ProseMirror from intercepting DOM events originating from node view controls.
packages/page-constructor-extension/src/YfmPageConstructorNodeView/NodeView.tsx
packages/page-constructor-extension/src/YfmPageConstructorNodeView/YfmPageConstructorView.tsx
packages/page-constructor-extension/src/YfmPageConstructorNodeView/YfmPageConstructor.scss
packages/page-constructor-extension/src/TextAreaFixed.tsx
packages/page-constructor-extension/src/YfmPageConstructorNodeView/index.ts
Provide in-editor Page Constructor preview using @gravity-ui/page-constructor and optional server-side content transformation.
  • Add YfmPageConstructorPreview component that parses YAML via loadPageContent, optionally runs contentTransformer with configurable options, and renders PageConstructor inside PageConstructorProvider with ErrorBoundary handling.
  • Expose TransformerOptions / YfmPageConstructorTransformerOptions type to allow caller to enable/disable and configure server-side transformation (e.g., language).
  • Wire theme selection via useThemeType and apply appropriate CSS classes for layout sizing within the markdown editor.
packages/page-constructor-extension/src/YfmPageConstructorNodeView/YfmPageConstructorPreview.tsx
packages/page-constructor-extension/src/YfmPageConstructorNodeView/YfmPageConstructorView.tsx
packages/page-constructor-extension/src/index.ts
Expose toolbar action and insertion command to create page-constructor blocks from the markdown editor UI.
  • Implement createYfmPageConstructor ProseMirror command that inserts a yfm-page-constructor node with default YAML content and sets shared editing state to open the editor immediately.
  • Export addYfmPageConstructor ActionSpec and register it under YfmPageConstructorAction in the extension, wiring it into the WysiwygEditor.Actions namespace.
  • Add wYfmPageConstructorItemData toolbar item definition with icon, localized title, and exec/isActive/isEnable hooks delegating to the registered action.
packages/page-constructor-extension/src/actions.ts
packages/page-constructor-extension/src/index.ts
packages/page-constructor-extension/src/toolbar.ts
packages/page-constructor-extension/src/i18n/index.ts
packages/page-constructor-extension/src/const.ts
Add split-mode preview support via a HOC that bootstraps the Page Constructor runtime on static HTML.
  • Implement withYfmPageConstructor HOC that wraps a component expecting an html prop, calls usePageConstructor with optional theme/preMountHook, and re-renders Page Constructor on html changes.
  • Add useYfmPageConstructorRuntime hook to lazy-load the Diplodoc page-constructor runtime chunk and wire basic SCSS to ensure layout height behaves correctly in preview.
  • Define auxiliary types for TransformMeta and config, and export HOC typings for consumer code.
packages/page-constructor-extension/src/hocs/withYfmPageConstructor/index.tsx
packages/page-constructor-extension/src/hocs/withYfmPageConstructor/useYfmPageConstructorRuntime.ts
packages/page-constructor-extension/src/hocs/withYfmPageConstructor/withYfmPageConstructor.scss
packages/page-constructor-extension/src/hocs/withYfmPageConstructor/types.ts
Set up the new page-constructor-extension package configuration, build, linting, tests, and monorepo wiring.
  • Create package.json with build, lint, typecheck, test scripts; dual ESM/CJS exports (root, toolbar, HOC) and appropriate dependencies/peerDependencies/sideEffects.
  • Add tsconfig, gulpfile, jest.config, style mock, and .prettierignore to align with existing workspace tooling and testing patterns.
  • Register ESLint project configuration for the new package, update pnpm workspace catalogs (including peer-diplodoc and peer-gravity deps), and wire the package into release-please manifest for versioning.
  • Include README documenting installation, editor extension usage, toolbar integration, and split-mode preview HOC.
packages/page-constructor-extension/package.json
packages/page-constructor-extension/tsconfig.json
packages/page-constructor-extension/gulpfile.mjs
packages/page-constructor-extension/jest.config.cjs
packages/page-constructor-extension/__mocks__/styleMock.cjs
packages/page-constructor-extension/.prettierignore
packages/page-constructor-extension/README.md
eslint.config.mjs
pnpm-workspace.yaml
.release-please/manifest.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gravity-ui
Copy link
Copy Markdown

gravity-ui Bot commented Apr 22, 2026

Storybook Deployed

@gravity-ui
Copy link
Copy Markdown

gravity-ui Bot commented Apr 22, 2026

🎭 Playwright Report

@separatrixxx separatrixxx marked this pull request as ready for review April 22, 2026 10:34
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The toolbar item calls e.actions.createYfmPageConstructor.isActive(), but the corresponding ActionSpec (addYfmPageConstructor) doesn’t define an isActive implementation, so at runtime this may be undefined; either implement an isActive handler or stop wiring it through the toolbar item.
  • useYfmPageConstructorRuntime performs a dynamic import on every render of the HOC instead of in an effect, which can cause unnecessary work; consider wrapping the import in useEffect (or useEffectOnce) inside the hook so the runtime is loaded only once per mount.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The toolbar item calls `e.actions.createYfmPageConstructor.isActive()`, but the corresponding `ActionSpec` (`addYfmPageConstructor`) doesn’t define an `isActive` implementation, so at runtime this may be `undefined`; either implement an `isActive` handler or stop wiring it through the toolbar item.
- `useYfmPageConstructorRuntime` performs a dynamic import on every render of the HOC instead of in an effect, which can cause unnecessary work; consider wrapping the import in `useEffect` (or `useEffectOnce`) inside the hook so the runtime is loaded only once per mount.

## Individual Comments

### Comment 1
<location path="packages/page-constructor-extension/src/YfmPageConstructorNodeView/YfmPageConstructorView.tsx" line_range="117-118" />
<code_context>
+    const [menuOpen, , closeMenu, toggleMenuOpen] = useBooleanState(false);
+
+    // Local state mirrors ProseMirror attribute; updates preview optimistically
+    const [content, setContent] = useState(
+        () => node.attrs[YfmPageConstructorConsts.NodeAttrs.content] || '',
+    );
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Local `content` state can diverge from ProseMirror node attrs and never resync on external updates.

`content` is only initialised from `node.attrs[...]` and then updated via `handleChange`, so if `node.attrs` changes externally (e.g. collaborative edits or other transactions), the component rerenders with a new `node` but keeps the old `content`, leaving the preview stale. Consider syncing `content` from `node.attrs` whenever you’re not editing, e.g. via a `useEffect` that watches `node.attrs[content]` and `editing` and updates local state only when `editing === false`.
</issue_to_address>

### Comment 2
<location path="packages/page-constructor-extension/src/hocs/withYfmPageConstructor/useYfmPageConstructorRuntime.ts" line_range="2-4" />
<code_context>
+/** @internal */
+export function useYfmPageConstructorRuntime() {
+    import(
+        /* webpackChunkName: "page-constructor-runtime" */ '@diplodoc/page-constructor-extension/runtime'
+    );
+}
</code_context>
<issue_to_address>
**suggestion (performance):** The dynamic import inside the hook body runs on every render, which is unnecessary work.

Calling `import()` unconditionally in the hook body means it runs on every render, creating a new (ignored) promise each time. Even if the browser dedupes the actual chunk load, this is avoidable overhead and can trip unhandled-promise lint rules.

Consider moving the `import()` into an effect so it runs once per mount, e.g. `useEffectOnce(() => { void import(...); });`, which preserves lazy loading without repeated calls.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread packages/page-constructor-extension/package.json Outdated
Comment thread packages/page-constructor-extension/package.json Outdated
Comment thread packages/page-constructor-extension/package.json Outdated
Comment thread packages/page-constructor-extension/package.json Outdated
Comment thread packages/page-constructor-extension/package.json Outdated
Comment thread packages/page-constructor-extension/package.json
Comment thread packages/page-constructor-extension/README.md Outdated
@makhnatkin
Copy link
Copy Markdown
Collaborator

Please also add a demo story for this PR

@separatrixxx separatrixxx requested a review from makhnatkin April 23, 2026 16:20
Comment thread demo/package.json Outdated
Comment thread packages/page-constructor-extension/package.json Outdated
Comment thread packages/page-constructor-extension/package.json Outdated
Comment thread packages/page-constructor-extension/package.json Outdated
Comment thread packages/page-constructor-extension/package.json
Comment thread packages/page-constructor-extension/src/TextAreaFixed.tsx Outdated
Comment thread packages/page-constructor-extension/src/index.ts
Comment thread packages/page-constructor-extension/src/extension/index.ts
Comment thread packages/page-constructor-extension/src/extension/toolbar.ts Outdated
Comment thread packages/page-constructor-extension/src/specs.ts Outdated
Comment thread packages/page-constructor-extension/src/specs.ts Outdated
Comment thread packages/page-constructor-extension/src/extension/index.ts Outdated
@separatrixxx separatrixxx merged commit 8020bc8 into main Apr 25, 2026
7 checks passed
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