diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelDetails.component.tsx index 00a5a323c45..ccd6247ae3a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelDetails.component.tsx @@ -326,7 +326,7 @@ const DataModelDetails = ({ mode={{ name: CSMode.SQL }} options={{ styleActiveLine: false, - readOnly: 'nocursor', + readOnly: true, }} value={dataModelData?.sql} /> diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/ParameterForm.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/ParameterForm.tsx index b89d0f50dbd..78910424b62 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/ParameterForm.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/ParameterForm.tsx @@ -99,9 +99,7 @@ const ParameterForm: React.FC = ({ definition, table }) => { ); } else { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/CustomMetricForm/CustomMetricForm.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/CustomMetricForm/CustomMetricForm.component.tsx index a8aeb40d53f..377849c1515 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/CustomMetricForm/CustomMetricForm.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/CustomMetricForm/CustomMetricForm.component.tsx @@ -161,9 +161,7 @@ const CustomMetricForm = ({ 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 a082a4ab3cc..9c11fcb5cd1 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 @@ -526,9 +526,6 @@ 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 }} - options={{ - readOnly: false, - }} 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 new file mode 100644 index 00000000000..c159895e5f7 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaEditor/SchemaEditor.interface.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2024 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CSMode } from '../../../enums/codemirror.enum'; + +type Mode = { + name: CSMode; + json?: boolean; +}; + +export interface SchemaEditorProps { + value?: string; + className?: string; + mode?: Mode; + readOnly?: boolean; + options?: { + [key: string]: string | boolean | Array; + }; + editorClass?: string; + showCopyButton?: boolean; + onChange?: (value: string) => void; +} 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 fc666186b7e..7315cd9a80c 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 @@ -11,28 +11,88 @@ * limitations under the License. */ -import { render, screen } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; import SchemaEditor from './SchemaEditor'; +const mockOnChange = jest.fn(); +const mockOnCopyToClipBoard = jest.fn(); + jest.mock('../../../constants/constants', () => ({ JSON_TAB_SIZE: 25, })); +jest.mock('../../../utils/SchemaEditor.utils', () => ({ + getSchemaEditorValue: jest.fn().mockReturnValue('test SQL query'), +})); + +jest.mock('../../../hooks/useClipBoard', () => ({ + ...jest.requireActual('../../../hooks/useClipBoard'), + useClipboard: jest + .fn() + .mockImplementation(() => ({ onCopyToClipBoard: mockOnCopyToClipBoard })), +})); + +jest.mock('react-codemirror2', () => ({ + ...jest.requireActual('react-codemirror2'), + Controlled: jest.fn().mockImplementation(({ value, onChange }) => ( +
+ {value} + +
+ )), +})); + +const mockProps = { + value: 'test SQL query', + showCopyButton: true, + onChange: mockOnChange, +}; + describe('SchemaEditor component test', () => { it('Component should render properly', async () => { - render(); + render(); expect( await screen.findByTestId('code-mirror-container') ).toBeInTheDocument(); + + expect(await screen.findByTestId('query-copy-button')).toBeInTheDocument(); }); it('Value provided via props should be visible', async () => { - render(); + render(); expect( (await screen.findByTestId('code-mirror-container')).textContent ).toBe('test SQL query'); }); + + it('Copy button should not be visible', async () => { + render(); + + expect(screen.queryByTestId('query-copy-button')).not.toBeInTheDocument(); + }); + + it('Should call onCopyToClipBoard', async () => { + render(); + + fireEvent.click(screen.getByTestId('query-copy-button')); + + expect(mockOnCopyToClipBoard).toHaveBeenCalled(); + }); + + it('Should call onChange handler', async () => { + render(); + + fireEvent.change(screen.getByTestId('code-mirror-editor-input'), { + target: { value: 'new SQL query' }, + }); + + expect(mockOnChange).toHaveBeenCalled(); + }); }); 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 b1d0552bc9d..8e78b17264d 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 @@ -11,6 +11,8 @@ * limitations under the License. */ +import { Button, Tooltip } from 'antd'; +import classNames from 'classnames'; import { Editor, EditorChange } from 'codemirror'; import 'codemirror/addon/edit/closebrackets.js'; import 'codemirror/addon/edit/matchbrackets.js'; @@ -24,14 +26,14 @@ import 'codemirror/mode/sql/sql'; import { isUndefined } from 'lodash'; import React, { useEffect, 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'; import { JSON_TAB_SIZE } from '../../../constants/constants'; import { CSMode } from '../../../enums/codemirror.enum'; +import { useClipboard } from '../../../hooks/useClipBoard'; import { getSchemaEditorValue } from '../../../utils/SchemaEditor.utils'; - -type Mode = { - name: CSMode; - json?: boolean; -}; +import './schema-editor.less'; +import { SchemaEditorProps } from './SchemaEditor.interface'; const SchemaEditor = ({ value = '', @@ -42,18 +44,10 @@ const SchemaEditor = ({ }, options, editorClass, + showCopyButton = true, onChange, -}: { - value?: string; - className?: string; - mode?: Mode; - readOnly?: boolean; - options?: { - [key: string]: string | boolean | Array; - }; - editorClass?: string; - onChange?: (value: string) => void; -}) => { +}: SchemaEditorProps) => { + const { t } = useTranslation(); const defaultOptions = { tabSize: JSON_TAB_SIZE, indentUnit: JSON_TAB_SIZE, @@ -72,6 +66,8 @@ const SchemaEditor = ({ const [internalValue, setInternalValue] = useState( getSchemaEditorValue(value) ); + const { onCopyToClipBoard } = useClipboard(internalValue); + const handleEditorInputBeforeChange = ( _editor: Editor, _data: EditorChange, @@ -94,7 +90,22 @@ const SchemaEditor = ({ }, [value]); return ( -
+
+ {showCopyButton && ( +
+ +
+ )} + = ({ styleActiveLine: isEditMode, readOnly: isEditMode ? false : 'nocursor', }} + showCopyButton={false} value={query.query ?? ''} onChange={handleQueryChange} /> diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx index d0111e0f7cd..f120d3a7583 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx @@ -84,7 +84,7 @@ const StoredProcedureSummary = ({ mode={{ name: CSMode.SQL }} options={{ styleActiveLine: false, - readOnly: 'nocursor', + readOnly: true, }} value={ ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/ModalWithQueryEditor/ModalWithQueryEditor.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ModalWithQueryEditor/ModalWithQueryEditor.tsx index b835382702b..c29a4c0ea44 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/ModalWithQueryEditor/ModalWithQueryEditor.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ModalWithQueryEditor/ModalWithQueryEditor.tsx @@ -100,9 +100,7 @@ export const ModalWithQueryEditor = ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyValue.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyValue.tsx index bbd8fe9001b..d6d98781ce9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyValue.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyValue.tsx @@ -530,9 +530,7 @@ export const PropertyValue: FC = ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddQueryPage/AddQueryPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddQueryPage/AddQueryPage.component.tsx index f6d89e3b04c..742d40e2ceb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/AddQueryPage/AddQueryPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddQueryPage/AddQueryPage.component.tsx @@ -227,9 +227,7 @@ const AddQueryPage = () => { { mode={{ name: CSMode.SQL }} options={{ styleActiveLine: false, - readOnly: 'nocursor', + readOnly: true, }} value={code} />