mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2026-01-06 20:47:06 +00:00
Fix: Issue related to SQL query viewer (#21627)
(cherry picked from commit 5fc2c90e5b8c927c8d1aef57f064b5e7ab1754a1)
This commit is contained in:
parent
2cd35486ec
commit
25ef5a6f4c
@ -543,6 +543,7 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
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}
|
||||
/>
|
||||
|
||||
@ -20,6 +20,7 @@ export type Mode = {
|
||||
|
||||
export interface SchemaEditorProps {
|
||||
value?: string;
|
||||
refreshEditor?: boolean;
|
||||
className?: string;
|
||||
mode?: Mode;
|
||||
readOnly?: boolean;
|
||||
|
||||
@ -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(<SchemaEditor {...mockProps} refreshEditor />);
|
||||
|
||||
// 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(<SchemaEditor {...mockProps} refreshEditor={false} />);
|
||||
|
||||
// Fast-forward timers to trigger the refresh
|
||||
jest.advanceTimersByTime(50);
|
||||
|
||||
expect(mockEditor.refresh).not.toHaveBeenCalled();
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
@ -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<string>(
|
||||
getSchemaEditorValue(value)
|
||||
);
|
||||
// Store the CodeMirror editor instance
|
||||
const editorInstance = useRef<Editor | null>(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 (
|
||||
<div
|
||||
className={classNames('relative', className)}
|
||||
@ -114,6 +129,9 @@ const SchemaEditor = ({
|
||||
|
||||
<CodeMirror
|
||||
className={editorClass}
|
||||
editorDidMount={(editor) => {
|
||||
editorInstance.current = editor;
|
||||
}}
|
||||
options={defaultOptions}
|
||||
value={internalValue}
|
||||
onBeforeChange={handleEditorInputBeforeChange}
|
||||
|
||||
@ -309,6 +309,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
|
||||
),
|
||||
queryViewerTab: (
|
||||
<QueryViewer
|
||||
isActive={activeTab === EntityTabs.CONFIG}
|
||||
sqlQuery={JSON.stringify(topicDetails.topicConfig)}
|
||||
title={t('label.config')}
|
||||
/>
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
|
||||
@ -53,7 +53,4 @@
|
||||
.CodeMirror-linenumber {
|
||||
color: @text-color;
|
||||
}
|
||||
.CodeMirror-sizer {
|
||||
margin-left: 40px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,12 +99,13 @@ export const getDashboardDataModelDetailPageTabs = ({
|
||||
children: (
|
||||
<Card>
|
||||
<SchemaEditor
|
||||
editorClass="custom-code-mirror-theme full-screen-editor-height"
|
||||
editorClass="custom-query-editor custom-code-mirror-theme full-screen-editor-height"
|
||||
mode={{ name: CSMode.SQL }}
|
||||
options={{
|
||||
styleActiveLine: false,
|
||||
readOnly: true,
|
||||
}}
|
||||
refreshEditor={activeTab === EntityTabs.SQL}
|
||||
value={dataModelData?.sql}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
@ -151,6 +151,7 @@ export const getSearchIndexDetailsTabs = ({
|
||||
key: EntityTabs.SEARCH_INDEX_SETTINGS,
|
||||
children: (
|
||||
<QueryViewer
|
||||
isActive={activeTab === EntityTabs.SEARCH_INDEX_SETTINGS}
|
||||
sqlQuery={JSON.stringify(searchIndexDetails?.searchIndexSettings)}
|
||||
title={t('label.search-index-setting-plural')}
|
||||
/>
|
||||
|
||||
@ -966,6 +966,7 @@ export const getTableDetailPageBaseTabs = ({
|
||||
key: EntityTabs.DBT,
|
||||
children: (
|
||||
<QueryViewer
|
||||
isActive={activeTab === EntityTabs.DBT}
|
||||
sqlQuery={
|
||||
get(tableDetails, 'dataModel.sql', '') ??
|
||||
get(tableDetails, 'dataModel.rawSql', '')
|
||||
@ -1000,7 +1001,15 @@ export const getTableDetailPageBaseTabs = ({
|
||||
),
|
||||
isHidden: isUndefined(tableDetails?.schemaDefinition),
|
||||
key: EntityTabs.VIEW_DEFINITION,
|
||||
children: <QueryViewer sqlQuery={tableDetails?.schemaDefinition ?? ''} />,
|
||||
children: (
|
||||
<QueryViewer
|
||||
isActive={[
|
||||
EntityTabs.VIEW_DEFINITION,
|
||||
EntityTabs.SCHEMA_DEFINITION,
|
||||
].includes(activeTab)}
|
||||
sqlQuery={tableDetails?.schemaDefinition ?? ''}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user