From 25ef5a6f4ce8c865d63e911ca71e2cfb5f2ebec2 Mon Sep 17 00:00:00 2001 From: Shailesh Parmar Date: Fri, 6 Jun 2025 21:33:06 +0530 Subject: [PATCH] Fix: Issue related to SQL query viewer (#21627) (cherry picked from commit 5fc2c90e5b8c927c8d1aef57f064b5e7ab1754a1) --- .../ProfilerSettingsModal.tsx | 1 + .../SchemaEditor/SchemaEditor.interface.ts | 1 + .../SchemaEditor/SchemaEditor.test.tsx | 40 +++++++++++++++++++ .../Database/SchemaEditor/SchemaEditor.tsx | 20 +++++++++- .../TopicDetails/TopicDetails.component.tsx | 1 + .../QueryViewer/QueryViewer.component.tsx | 3 ++ .../ui/src/styles/components/code-mirror.less | 3 -- .../ui/src/utils/DashboardDataModelUtils.tsx | 3 +- .../ui/src/utils/SearchIndexUtils.tsx | 1 + .../resources/ui/src/utils/TableUtils.tsx | 11 ++++- 10 files changed, 78 insertions(+), 6 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ProfilerSettingsModal/ProfilerSettingsModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ProfilerSettingsModal/ProfilerSettingsModal.tsx index 6a1296288e8..6580cd507c5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ProfilerSettingsModal/ProfilerSettingsModal.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ProfilerSettingsModal/ProfilerSettingsModal.tsx @@ -543,6 +543,7 @@ const ProfilerSettingsModal: React.FC = ({ className="custom-query-editor query-editor-h-200 custom-code-mirror-theme" data-testid="profiler-setting-sql-editor" mode={{ name: CSMode.SQL }} + refreshEditor={visible} value={state?.sqlQuery ?? ''} onChange={handleCodeMirrorChange} /> diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.interface.ts index 03a30dfca92..9ce4df5c0a0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.interface.ts @@ -20,6 +20,7 @@ export type Mode = { export interface SchemaEditorProps { value?: string; + refreshEditor?: boolean; className?: string; mode?: Mode; readOnly?: boolean; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.test.tsx index 7315cd9a80c..1c1aa215c5e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.test.tsx @@ -95,4 +95,44 @@ describe('SchemaEditor component test', () => { expect(mockOnChange).toHaveBeenCalled(); }); + + it('Should call refreshEditor', async () => { + jest.useFakeTimers(); + const mockEditor = { + refresh: jest.fn(), + }; + + jest.spyOn(React, 'useRef').mockReturnValue({ + current: mockEditor, + }); + + render(); + + // Fast-forward timers to trigger the refresh + jest.advanceTimersByTime(50); + + expect(mockEditor.refresh).toHaveBeenCalled(); + + jest.useRealTimers(); + }); + + it('Should not call refresh if refreshEditor is false', async () => { + jest.useFakeTimers(); + const mockEditor = { + refresh: jest.fn(), + }; + + jest.spyOn(React, 'useRef').mockReturnValue({ + current: mockEditor, + }); + + render(); + + // Fast-forward timers to trigger the refresh + jest.advanceTimersByTime(50); + + expect(mockEditor.refresh).not.toHaveBeenCalled(); + + jest.useRealTimers(); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.tsx index a4285570047..0eebcb61bf4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.tsx @@ -26,7 +26,7 @@ import 'codemirror/mode/javascript/javascript'; import 'codemirror/mode/python/python'; import 'codemirror/mode/sql/sql'; import { isUndefined } from 'lodash'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Controlled as CodeMirror } from 'react-codemirror2'; import { useTranslation } from 'react-i18next'; import { ReactComponent as CopyIcon } from '../../../assets/svg/icon-copy.svg'; @@ -49,6 +49,7 @@ const SchemaEditor = ({ showCopyButton = true, onChange, onFocus, + refreshEditor, }: SchemaEditorProps) => { const { t } = useTranslation(); const defaultOptions = { @@ -69,6 +70,8 @@ const SchemaEditor = ({ const [internalValue, setInternalValue] = useState( getSchemaEditorValue(value) ); + // Store the CodeMirror editor instance + const editorInstance = useRef(null); const { onCopyToClipBoard, hasCopied } = useClipboard(internalValue); const handleEditorInputBeforeChange = ( @@ -92,6 +95,18 @@ const SchemaEditor = ({ setInternalValue(getSchemaEditorValue(value)); }, [value]); + useEffect(() => { + if (refreshEditor) { + // CodeMirror can't measure its container if hidden (e.g., in an inactive tab with display: none). + // When the tab becomes visible, the browser may not have finished layout/reflow when this runs. + // Delaying refresh by 50ms ensures the editor is visible and DOM is ready for CodeMirror to re-render. + // This is a common workaround for editors inside tabbed interfaces. + setTimeout(() => { + editorInstance.current?.refresh(); + }, 50); + } + }, [refreshEditor]); + return (
{ + editorInstance.current = editor; + }} options={defaultOptions} value={internalValue} onBeforeChange={handleEditorInputBeforeChange} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicDetails/TopicDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicDetails/TopicDetails.component.tsx index 38dbcf6d3c9..d17ef6fb4ea 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicDetails/TopicDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicDetails/TopicDetails.component.tsx @@ -309,6 +309,7 @@ const TopicDetails: React.FC = ({ ), queryViewerTab: ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/QueryViewer/QueryViewer.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/QueryViewer/QueryViewer.component.tsx index 597be7baecc..37a2d5a6643 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/QueryViewer/QueryViewer.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/QueryViewer/QueryViewer.component.tsx @@ -24,9 +24,11 @@ import './query-viewer.style.less'; const QueryViewer = ({ title, sqlQuery, + isActive, }: { title?: React.ReactNode; sqlQuery: string; + isActive?: boolean; }) => { const { t } = useTranslation(); @@ -69,6 +71,7 @@ const QueryViewer = ({ )} mode={{ name: CSMode.SQL }} options={{ readOnly: true }} + refreshEditor={isActive} showCopyButton={false} value={sqlQuery} /> diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/components/code-mirror.less b/openmetadata-ui/src/main/resources/ui/src/styles/components/code-mirror.less index 24cfc45d5df..28a49d4eeb2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/components/code-mirror.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/components/code-mirror.less @@ -53,7 +53,4 @@ .CodeMirror-linenumber { color: @text-color; } - .CodeMirror-sizer { - margin-left: 40px !important; - } } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDataModelUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDataModelUtils.tsx index 08e0cd25293..ea83ed20b5f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDataModelUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDataModelUtils.tsx @@ -99,12 +99,13 @@ export const getDashboardDataModelDetailPageTabs = ({ children: ( diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx index b0e912ce89e..19e032f7e21 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx @@ -151,6 +151,7 @@ export const getSearchIndexDetailsTabs = ({ key: EntityTabs.SEARCH_INDEX_SETTINGS, children: ( diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx index 4482c15a558..01ccb2a7320 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx @@ -966,6 +966,7 @@ export const getTableDetailPageBaseTabs = ({ key: EntityTabs.DBT, children: ( , + children: ( + + ), }, { label: (