Skip to content

fixes 27953: migrate CodeMirror v5 to v6 (@codemirror/* packages)#28438

Open
satender-kumar-collate wants to merge 41 commits into
mainfrom
upgrade/cmV6
Open

fixes 27953: migrate CodeMirror v5 to v6 (@codemirror/* packages)#28438
satender-kumar-collate wants to merge 41 commits into
mainfrom
upgrade/cmV6

Conversation

@satender-kumar-collate

@satender-kumar-collate satender-kumar-collate commented May 26, 2026

Copy link
Copy Markdown
Contributor

Replaces react-codemirror2 / codemirror@5 with the modular @codemirror v6 packages. Introduces a useCodeMirror hook that mounts a single EditorView, syncs external value changes via an Annotation (suppressing spurious onChange calls), and reconfigures language/options via a Compartment. All CSS selectors, Playwright locators, and Jest mocks are updated to use CM6 class names (.cm-editor, .cm-scroller, .cm-gutters, etc.).

Fixes #27953, #3721

Type of change:

  • Bug fix
  • Improvement
  • New feature
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation

High-level design:

N/A — small change.

Tests:

Use cases covered

Unit tests

Backend integration tests

Ingestion integration tests

Playwright (UI) tests

Manual testing performed

UI screen recording / screenshots:

image image

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • My PR is linked to a GitHub issue via Fixes #<issue-number> above.
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: I updated the migration scripts or explained why it is not needed.
  • For UI changes: I attached a screen recording and/or screenshots above.
  • I have added tests (unit / integration / Playwright as applicable) and listed them above.

Summary by Gitar

  • Dependency updates:
    • Added react-markdown version 10.1.0 to package.json dependencies.
  • Refactored CodeMirror props:
    • Simplified readOnly boolean logic in CodeEditor and SchemaEditor components.
  • Updated Playwright selectors:
    • Replaced deprecated pre[role='presentation'] locators with .cm-content class to support CodeMirror v6 DOM structure.
    • Switched from keyboard.type to keyboard.insertText for more reliable CM6 editor input handling in Playwright tests.
  • Maintenance:
    • Cleaned up redundant logic in SchemaEditor.test.tsx.
    • Added visibility assertions before interacting with CodeMirror editors in test suites.

This will update automatically on new commits.

Replaces react-codemirror2 / codemirror@5 with the modular @codemirror v6
packages. Introduces a useCodeMirror hook that mounts a single EditorView,
syncs external value changes via an Annotation (suppressing spurious onChange
calls), and reconfigures language/options via a Compartment. All CSS selectors,
Playwright locators, and Jest mocks are updated to use CM6 class names
(.cm-editor, .cm-scroller, .cm-gutters, etc.).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@satender-kumar-collate satender-kumar-collate requested a review from a team as a code owner May 26, 2026 11:55
@satender-kumar-collate satender-kumar-collate added UI UI specific issues safe to test Add this label to run secure Github workflows on PRs labels May 26, 2026
Comment thread openmetadata-ui/src/main/resources/ui/src/hooks/useCodeMirror.ts Outdated
Comment thread openmetadata-ui/src/main/resources/ui/src/hooks/useCodeMirror.ts Outdated
@satender-kumar-collate satender-kumar-collate changed the title feat(ui): migrate CodeMirror v5 to v6 (@codemirror/* packages) fixes 27953: migrate CodeMirror v5 to v6 (@codemirror/* packages) May 26, 2026
@siddhant1 siddhant1 requested a review from Copilot May 26, 2026 12:01
@gitar-bot

gitar-bot Bot commented May 26, 2026

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 0 resolved / 3 findings

Migrates CodeMirror v5 to v6 using a custom hook for EditorView management, but introduces instability in opts.mode dependency tracking, missing height constraints in the ManifestJsonWidget, and incomplete component cleanup.

⚠️ Performance: opts.mode object reference instability triggers reconfigure on every render

📄 openmetadata-ui/src/main/resources/ui/src/hooks/useCodeMirror.ts:220-221 📄 openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.tsx:32-35

The useEffect at line 210-230 in useCodeMirror.ts includes opts.mode as a dependency. Since mode has an inline default value { name: CSMode.JAVASCRIPT, json: true } in both SchemaEditor.tsx and CodeEditor.tsx, a new object reference is created on every render, causing dynamicCompartment.current.reconfigure(...) to fire on every re-render. This dispatches a transaction to the editor unnecessarily and could cause performance issues (especially with frequent parent re-renders).

The hook should either compare mode by value (e.g., use opts.mode?.name and opts.mode?.json as separate deps) or the components should stabilize the default with useMemo.

Replace opts.mode with its primitive constituents in the dependency array to avoid reference comparison issues
], [
  opts.mode?.name,
  opts.mode?.json,
  opts.readOnly,
  opts.showLineNumbers,
  opts.lineWrapping,
  opts.showFoldGutter,
  opts.styleActiveLine,
  opts.matchBrackets,
  opts.autoCloseBrackets,
  opts.tabSize,
]);
⚠️ Bug: ManifestJsonWidget editor height broken: missing height:100% on wrapper

📄 openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/ManifestJsonWidget/manifest-json-widget.less:26-28

In manifest-json-widget.less, the old CSS applied height: 100% !important to three selectors: .manifest-json-widget-editor, .manifest-json-widget-editor > div, and .CodeMirror. The new CSS only applies it to .manifest-json-widget-editor > div (which targets the .cm-editor). However, the .manifest-json-widget-editor element itself (the editorRef div in SchemaEditor) no longer gets height: 100%, breaking the height chain from .manifest-json-widget-resize-wrapper.manifest-json-widget-editor.cm-editor. The editor will collapse to its content height instead of filling the resizable wrapper.

Restore height: 100% on the .manifest-json-widget-editor wrapper to maintain the height chain
.manifest-json-widget-editor,
.manifest-json-widget-editor > div {
  height: 100% !important;
}
💡 Edge Case: useCodeMirror never cleans up EditorView on editorRef detach

📄 openmetadata-ui/src/main/resources/ui/src/hooks/useCodeMirror.ts:145-159

The useEffect that creates the EditorView (line 145-193) has an empty dependency array and only runs once. If editorRef.current is null at mount time (e.g., the component conditionally renders the container div), the editor is never created. Conversely, if the ref div is replaced later, the old view remains attached to a detached DOM node. This is a minor edge case since the current callers always render the div unconditionally, but it's worth noting as a fragility if the hook is reused elsewhere.

🤖 Prompt for agents
Code Review: Migrates CodeMirror v5 to v6 using a custom hook for EditorView management, but introduces instability in `opts.mode` dependency tracking, missing height constraints in the ManifestJsonWidget, and incomplete component cleanup.

1. ⚠️ Performance: opts.mode object reference instability triggers reconfigure on every render
   Files: openmetadata-ui/src/main/resources/ui/src/hooks/useCodeMirror.ts:220-221, openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.tsx:32-35

   The `useEffect` at line 210-230 in `useCodeMirror.ts` includes `opts.mode` as a dependency. Since `mode` has an inline default value `{ name: CSMode.JAVASCRIPT, json: true }` in both `SchemaEditor.tsx` and `CodeEditor.tsx`, a new object reference is created on every render, causing `dynamicCompartment.current.reconfigure(...)` to fire on every re-render. This dispatches a transaction to the editor unnecessarily and could cause performance issues (especially with frequent parent re-renders).
   
   The hook should either compare `mode` by value (e.g., use `opts.mode?.name` and `opts.mode?.json` as separate deps) or the components should stabilize the default with `useMemo`.

   Fix (Replace opts.mode with its primitive constituents in the dependency array to avoid reference comparison issues):
   ], [
     opts.mode?.name,
     opts.mode?.json,
     opts.readOnly,
     opts.showLineNumbers,
     opts.lineWrapping,
     opts.showFoldGutter,
     opts.styleActiveLine,
     opts.matchBrackets,
     opts.autoCloseBrackets,
     opts.tabSize,
   ]);

2. ⚠️ Bug: ManifestJsonWidget editor height broken: missing height:100% on wrapper
   Files: openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/ManifestJsonWidget/manifest-json-widget.less:26-28

   In `manifest-json-widget.less`, the old CSS applied `height: 100% !important` to three selectors: `.manifest-json-widget-editor`, `.manifest-json-widget-editor > div`, and `.CodeMirror`. The new CSS only applies it to `.manifest-json-widget-editor > div` (which targets the `.cm-editor`). However, the `.manifest-json-widget-editor` element itself (the `editorRef` div in SchemaEditor) no longer gets `height: 100%`, breaking the height chain from `.manifest-json-widget-resize-wrapper` → `.manifest-json-widget-editor` → `.cm-editor`. The editor will collapse to its content height instead of filling the resizable wrapper.

   Fix (Restore height: 100% on the .manifest-json-widget-editor wrapper to maintain the height chain):
   .manifest-json-widget-editor,
   .manifest-json-widget-editor > div {
     height: 100% !important;
   }

3. 💡 Edge Case: useCodeMirror never cleans up EditorView on editorRef detach
   Files: openmetadata-ui/src/main/resources/ui/src/hooks/useCodeMirror.ts:145-159

   The `useEffect` that creates the `EditorView` (line 145-193) has an empty dependency array and only runs once. If `editorRef.current` is null at mount time (e.g., the component conditionally renders the container div), the editor is never created. Conversely, if the ref div is replaced later, the old view remains attached to a detached DOM node. This is a minor edge case since the current callers always render the div unconditionally, but it's worth noting as a fragility if the hook is reused elsewhere.

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Satender and others added 3 commits May 26, 2026 17:33
…widget

- Expand opts.mode into primitive constituents (opts.mode?.name, opts.mode?.json)
  in the dynamic compartment useEffect dep array to avoid unnecessary
  reconfigures on every render when an inline default object is passed
- Change editorRef type from useRef<HTMLDivElement>(null!) to
  useRef<HTMLDivElement | null>(null) to remove the non-null assertion and
  correctly type the null case
- Restore .manifest-json-widget-editor to the height:100% selector group so
  the height chain from the resize wrapper through to .cm-editor is maintained

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a comment on the mount useEffect documenting that the hook assumes
the host <div ref={editorRef} /> is rendered unconditionally by the
caller, and noting the edge cases (null at mount / ref replacement) that
are acceptable for current SchemaEditor / CodeEditor usage. Fixes #1 and
#2 (primitive mode deps + manifest-json-widget height chain) were already
applied in the previous commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Issue #1 (root cause): Replace inline mode default objects in SchemaEditor
and CodeEditor with a module-level DEFAULT_MODE constant so the same
reference is reused across renders, eliminating unnecessary reconfigures
regardless of how the hook tracks deps.

Issue #3 (edge case): Replace the empty-dep useEffect + editorRef useRef
pattern with a callback ref (useCallback with no deps). React calls the
callback whenever the DOM node mounts, unmounts, or is replaced, so the
EditorView is always destroyed on detach — including when the host element
is conditionally rendered or swapped without the parent unmounting.
Update test mocks to return editorRef: jest.fn() to match the new
(node: HTMLDivElement | null) => void type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 62%
62.45% (67132/107488) 43.98% (37223/84617) 45.82% (11477/25043)

@github-actions

github-actions Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

🟡 Playwright Results — all passed (13 flaky)

✅ 4303 passed · ❌ 0 failed · 🟡 13 flaky · ⏭️ 88 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 300 0 1 4
✅ Shard 2 812 0 0 9
🟡 Shard 3 812 0 2 8
🟡 Shard 4 843 0 7 12
🟡 Shard 5 733 0 1 47
🟡 Shard 6 803 0 2 8
🟡 13 flaky test(s) (passed on retry)
  • Pages/Roles.spec.ts › Roles page should work properly (shard 1, 1 retry)
  • Features/KnowledgeCenterTextEditor.spec.ts › Rich Text Editor - Text Formatting (shard 3, 1 retry)
  • Features/RTL.spec.ts › Verify Following widget functionality (shard 3, 2 retries)
  • Flow/ObservabilityAlerts.spec.ts › Pipeline Alert (shard 4, 2 retries)
  • Flow/ObservabilityAlerts.spec.ts › Ingestion Pipeline alert (shard 4, 1 retry)
  • Flow/PersonaDeletionUserProfile.spec.ts › User profile loads correctly before and after persona deletion (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Table (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Time (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Time Interval (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Entity Reference (shard 4, 1 retry)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 5, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)
  • Pages/UserDetails.spec.ts › Admin user can get all the roles hierarchy and edit roles (shard 6, 1 retry)

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

Satender and others added 2 commits May 27, 2026 08:52
…CM6 .cm-content

CM6 does not render `pre[role='presentation']` elements — those were CM5-specific
line nodes. Tests were timing out because the locator never resolved. Replace all
six occurrences with `.cm-content`, the CM6 contenteditable div that accepts input.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Drop redundant `?? false` from readOnly expression (no-constant-binary-expression):
  `readOnly ?? options?.readOnly === true` already yields a boolean
- Remove unused `capturedOnFocus` variable from SchemaEditor.test.tsx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread openmetadata-ui/src/main/resources/ui/src/hooks/useCodeMirror.ts
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

satender-kumar-collate and others added 9 commits June 5, 2026 11:16
…m property test

- Replace CM6 default fold gutter markers (⌄/›) with consistent SVG chevrons
  via markerDOM, fixing vertical misalignment between expanded/collapsed states
- Add CSS centering for .cm-foldGutter .cm-gutterElement to back the SVG markers
- Fix editColumnCustomProperty for date/time pickers: use picker.fill() +
  picker.press('Enter') instead of page.keyboard.type(), which was failing
  because global keyboard events after click() could miss the focused input;
  also drop the erroneous OK-button click that raced against the panel close animation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

❌ UI Checkstyle Failed

❌ ESLint + Prettier + Organise Imports (src)

One or more source files have linting or formatting issues.

Affected files
  • openmetadata-ui/src/main/resources/ui/src/hooks/useCodeMirror.ts

Fix locally (fast — only checks files changed in this branch):

make ui-checkstyle-changed

@gitar-bot

gitar-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 3 resolved / 4 findings

Migrates CodeMirror to version 6 with a new hook-based architecture and updated test locators, but contains a duplicate react-markdown entry in package.json.

⚠️ Bug: Duplicate "react-markdown" key in package.json dependencies

📄 openmetadata-ui/src/main/resources/ui/package.json:162 📄 openmetadata-ui/src/main/resources/ui/package.json:164

This commit adds "react-markdown": "^10.1.0" at line 162, but an identical "react-markdown": "^10.1.0" already exists at line 164. Duplicate keys in a JSON object are invalid/redundant: only the last occurrence is honored, and many tools/linters (and yarn itself) will warn or error on duplicate dependency entries. This also creates confusion and risks merge churn. Remove the newly-added duplicate at line 162 (keep a single entry), then re-run yarn install to regenerate the lockfile cleanly.

Remove the duplicate react-markdown entry, leaving a single occurrence.
159     "react-i18next": "^15.5.3",
160     "react-intersection-observer": "^9.16.0",
161     "react-latex-next": "^3.0.0",
162     "react-oidc": "^1.0.3",
163     "react-markdown": "^10.1.0",
164     "react-papaparse": "^4.1.0",
✅ 3 resolved
Performance: opts.mode object reference instability triggers reconfigure on every render

📄 openmetadata-ui/src/main/resources/ui/src/hooks/useCodeMirror.ts:220-221 📄 openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.tsx:32-35
The useEffect at line 210-230 in useCodeMirror.ts includes opts.mode as a dependency. Since mode has an inline default value { name: CSMode.JAVASCRIPT, json: true } in both SchemaEditor.tsx and CodeEditor.tsx, a new object reference is created on every render, causing dynamicCompartment.current.reconfigure(...) to fire on every re-render. This dispatches a transaction to the editor unnecessarily and could cause performance issues (especially with frequent parent re-renders).

The hook should either compare mode by value (e.g., use opts.mode?.name and opts.mode?.json as separate deps) or the components should stabilize the default with useMemo.

Bug: ManifestJsonWidget editor height broken: missing height:100% on wrapper

📄 openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/ManifestJsonWidget/manifest-json-widget.less:26-28
In manifest-json-widget.less, the old CSS applied height: 100% !important to three selectors: .manifest-json-widget-editor, .manifest-json-widget-editor > div, and .CodeMirror. The new CSS only applies it to .manifest-json-widget-editor > div (which targets the .cm-editor). However, the .manifest-json-widget-editor element itself (the editorRef div in SchemaEditor) no longer gets height: 100%, breaking the height chain from .manifest-json-widget-resize-wrapper.manifest-json-widget-editor.cm-editor. The editor will collapse to its content height instead of filling the resizable wrapper.

Edge Case: useCodeMirror never cleans up EditorView on editorRef detach

📄 openmetadata-ui/src/main/resources/ui/src/hooks/useCodeMirror.ts:145-159
The useEffect that creates the EditorView (line 145-193) has an empty dependency array and only runs once. If editorRef.current is null at mount time (e.g., the component conditionally renders the container div), the editor is never created. Conversely, if the ref div is replaced later, the old view remains attached to a detached DOM node. This is a minor edge case since the current callers always render the div unconditionally, but it's worth noting as a fragility if the hook is reused elsewhere.

🤖 Prompt for agents
Code Review: Migrates CodeMirror to version 6 with a new hook-based architecture and updated test locators, but contains a duplicate react-markdown entry in package.json.

1. ⚠️ Bug: Duplicate "react-markdown" key in package.json dependencies
   Files: openmetadata-ui/src/main/resources/ui/package.json:162, openmetadata-ui/src/main/resources/ui/package.json:164

   This commit adds `"react-markdown": "^10.1.0"` at line 162, but an identical `"react-markdown": "^10.1.0"` already exists at line 164. Duplicate keys in a JSON object are invalid/redundant: only the last occurrence is honored, and many tools/linters (and `yarn` itself) will warn or error on duplicate dependency entries. This also creates confusion and risks merge churn. Remove the newly-added duplicate at line 162 (keep a single entry), then re-run `yarn install` to regenerate the lockfile cleanly.

   Fix (Remove the duplicate react-markdown entry, leaving a single occurrence.):
   159     "react-i18next": "^15.5.3",
   160     "react-intersection-observer": "^9.16.0",
   161     "react-latex-next": "^3.0.0",
   162     "react-oidc": "^1.0.3",
   163     "react-markdown": "^10.1.0",
   164     "react-papaparse": "^4.1.0",

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Codemirror version migration

3 participants