diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-column-customize.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-column-customize.svg index 6846ccd696d..640efb4a438 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-column-customize.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-column-customize.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-column.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-column.svg index fc71fdf9c3e..cd35ddd1f4b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-column.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-column.svg @@ -1,5 +1,4 @@ - - - - + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-download.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-download.svg index cf0fadbc677..85001cff320 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-download.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-download.svg @@ -1,3 +1,3 @@ - + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-switch-vertical.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-switch-vertical.svg index c93aa17146a..cd9d7509858 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-switch-vertical.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-switch-vertical.svg @@ -1,3 +1,3 @@ - + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-trend-down.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-trend-down.svg index 31a26659bbb..2a874a3d03d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-trend-down.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-trend-down.svg @@ -1,3 +1,3 @@ - + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AppRouter.tsx index bed20d17876..17156c82ef9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AppRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AppRouter.tsx @@ -116,7 +116,7 @@ const AppRouter = () => { {/* Render APP position plugin routes (they handle their own layouts) */} {isAuthenticated && - plugins.flatMap((plugin) => { + plugins?.flatMap((plugin) => { const routes = plugin.getRoutes?.() || []; // Filter routes with APP position const appRoutes = routes.filter( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/CustomControls.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/CustomControls.component.tsx index 80e0c4f6799..7324c357090 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/CustomControls.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/CustomControls.component.tsx @@ -34,12 +34,9 @@ import { ExploreQuickFilterField } from '../../Explore/ExplorePage.interface'; import ExploreQuickFilters from '../../Explore/ExploreQuickFilters'; import { AssetsOfEntity } from '../../Glossary/GlossaryTerms/tabs/AssetsTabs.interface'; import { StyledMenu } from '../../LineageTable/LineageTable.styled'; -import { LineageControlProps } from './EntityLineage.interface'; import LineageSearchSelect from './LineageSearchSelect/LineageSearchSelect'; -const CustomControls: FC = ({ - onlyShowTabSwitch, -}: LineageControlProps) => { +const CustomControls: FC = () => { const { t } = useTranslation(); const { setSelectedQuickFilters, nodes, selectedQuickFilters } = useLineageProvider(); @@ -158,41 +155,35 @@ const CustomControls: FC = ({ }, [filters]); return ( -
- {!onlyShowTabSwitch && ( -
- - - - - {filterMenu} - +
+
+ + + + + {filterMenu} + - - -
- )} + + +
) }> - {activeViewTab === LINEAGE_TAB_VIEW.DIAGRAM_VIEW ? ( + {
)}
- ) : ( - - )} + } ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Lineage/LineageTable/LineageTable.component.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Lineage/LineageTable/LineageTable.component.test.tsx deleted file mode 100644 index cd9ea0d8c81..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Lineage/LineageTable/LineageTable.component.test.tsx +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright 2023 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 { render, screen, waitFor } from '@testing-library/react'; -import { act } from 'react'; -import { readString } from 'react-papaparse'; -import { ExportTypes } from '../../../constants/Export.constants'; -import { LINEAGE_TABLE_COLUMN_LOCALIZATION_KEYS } from '../../../constants/Lineage.constants'; -import { useLineageProvider } from '../../../context/LineageProvider/LineageProvider'; -import { useFqn } from '../../../hooks/useFqn'; -import { getLineageTableConfig } from '../../../utils/EntityLineageUtils'; -import { useEntityExportModalProvider } from '../../Entity/EntityExportModalProvider/EntityExportModalProvider.component'; -import LineageTable from './LineageTable.component'; - -// Mock the dependencies -jest.mock('../../../context/LineageProvider/LineageProvider'); -jest.mock('../../../hooks/useFqn'); -jest.mock('../../../utils/EntityLineageUtils'); -jest.mock( - '../../Entity/EntityExportModalProvider/EntityExportModalProvider.component' -); -jest.mock('react-papaparse', () => ({ - readString: jest.fn(), -})); - -const mockUseLineageProvider = useLineageProvider as jest.MockedFunction< - typeof useLineageProvider ->; -const mockUseFqn = useFqn as jest.MockedFunction; -const mockUseEntityExportModalProvider = - useEntityExportModalProvider as jest.MockedFunction< - typeof useEntityExportModalProvider - >; -const mockGetLineageColumnsAndDataSourceFromCSV = - getLineageTableConfig as jest.MockedFunction; -const mockReadString = readString as jest.MockedFunction; - -describe('LineageTable Component', () => { - const mockFqn = 'test.fqn'; - const mockExportLineageData = jest.fn(); - const mockTriggerExportForBulkEdit = jest.fn(); - const mockClearCSVExportData = jest.fn(); - - const mockTableConfig = { - columns: [ - { - title: 'Name', - dataIndex: 'name', - key: 'name', - width: 200, - ellipsis: { showTitle: false }, - render: jest.fn(), - }, - { - title: 'Type', - dataIndex: 'type', - key: 'type', - width: 200, - ellipsis: { showTitle: false }, - render: jest.fn(), - }, - ], - dataSource: [ - { name: 'Test Entity 1', type: 'Table', key: '0' }, - { name: 'Test Entity 2', type: 'Dashboard', key: '1' }, - ], - }; - - beforeEach(() => { - jest.clearAllMocks(); - - // Setup default mocks - mockUseFqn.mockReturnValue({ fqn: mockFqn } as any); - mockUseLineageProvider.mockReturnValue({ - exportLineageData: mockExportLineageData, - } as any); - mockUseEntityExportModalProvider.mockReturnValue({ - triggerExportForBulkEdit: mockTriggerExportForBulkEdit, - csvExportData: undefined, - clearCSVExportData: mockClearCSVExportData, - } as any); - mockGetLineageColumnsAndDataSourceFromCSV.mockReturnValue(mockTableConfig); - }); - - it('should render the component', async () => { - // Mock CSV data to trigger table rendering - const mockCSVData = 'Name,Type\nTest Entity 1,Table'; - const mockParsedData = { - data: [ - ['Name', 'Type'], - ['Test Entity 1', 'Table'], - ], - }; - - mockReadString.mockImplementation((_: string, options: any) => { - if (options.complete) { - options.complete(mockParsedData); - } - }); - - mockUseEntityExportModalProvider.mockReturnValue({ - triggerExportForBulkEdit: mockTriggerExportForBulkEdit, - csvExportData: mockCSVData, - clearCSVExportData: mockClearCSVExportData, - } as any); - - await act(async () => { - render(); - }); - - await waitFor(() => { - expect(screen.getByTestId('lineage-table')).toBeInTheDocument(); - }); - }); - - it('should trigger export for bulk edit on mount', () => { - render(); - - expect(mockTriggerExportForBulkEdit).toHaveBeenCalledWith({ - name: mockFqn, - onExport: mockExportLineageData, - exportTypes: [ExportTypes.CSV], - hideExportModal: true, - }); - }); - - it('should clear CSV export data on unmount', () => { - const { unmount } = render(); - - unmount(); - - expect(mockClearCSVExportData).toHaveBeenCalled(); - }); - - it('should render table with empty data initially', async () => { - // Mock empty CSV data to trigger table rendering with empty state - const mockCSVData = 'Name,Type'; - const mockParsedData = { - data: [['Name', 'Type']], - }; - - mockReadString.mockImplementation((_: string, options: any) => { - if (options.complete) { - options.complete(mockParsedData); - } - }); - - mockUseEntityExportModalProvider.mockReturnValue({ - triggerExportForBulkEdit: mockTriggerExportForBulkEdit, - csvExportData: mockCSVData, - clearCSVExportData: mockClearCSVExportData, - } as any); - - render(); - - await waitFor(() => { - expect(screen.getByTestId('lineage-table')).toBeInTheDocument(); - }); - }); - - it('should handle CSV data processing when csvExportData is available', async () => { - const mockCSVData = - 'Name,Type\nTest Entity 1,Table\nTest Entity 2,Dashboard'; - const mockParsedData = { - data: [ - ['Name', 'Type'], - ['Test Entity 1', 'Table'], - ['Test Entity 2', 'Dashboard'], - ], - }; - - mockReadString.mockImplementation((_: string, options: any) => { - if (options.complete) { - options.complete(mockParsedData); - } - }); - - mockUseEntityExportModalProvider.mockReturnValue({ - triggerExportForBulkEdit: mockTriggerExportForBulkEdit, - csvExportData: mockCSVData, - clearCSVExportData: mockClearCSVExportData, - } as any); - - render(); - - await waitFor(() => { - expect(mockReadString).toHaveBeenCalledWith(mockCSVData, { - worker: true, - skipEmptyLines: true, - complete: expect.any(Function), - }); - }); - - expect(mockGetLineageColumnsAndDataSourceFromCSV).toHaveBeenCalledWith( - mockParsedData.data - ); - }); - - it('should not process CSV data when csvExportData is not available', () => { - render(); - - expect(mockReadString).not.toHaveBeenCalled(); - }); - - it('should update table config when CSV data is processed', async () => { - const mockCSVData = 'Name,Type\nTest Entity 1,Table'; - const mockParsedData = { - data: [ - ['Name', 'Type'], - ['Test Entity 1', 'Table'], - ], - }; - - mockReadString.mockImplementation((_: string, options: any) => { - if (options.complete) { - options.complete(mockParsedData); - } - }); - - mockUseEntityExportModalProvider.mockReturnValue({ - triggerExportForBulkEdit: mockTriggerExportForBulkEdit, - csvExportData: mockCSVData, - clearCSVExportData: mockClearCSVExportData, - } as any); - - render(); - - await waitFor(() => { - expect(mockGetLineageColumnsAndDataSourceFromCSV).toHaveBeenCalledWith( - mockParsedData.data - ); - }); - }); - - it('should handle multiple CSV data updates', async () => { - const mockCSVData1 = 'Name,Type\nTest Entity 1,Table'; - const mockCSVData2 = 'Name,Type\nTest Entity 2,Dashboard'; - const mockParsedData1 = { - data: [ - ['Name', 'Type'], - ['Test Entity 1', 'Table'], - ], - }; - const mockParsedData2 = { - data: [ - ['Name', 'Type'], - ['Test Entity 2', 'Dashboard'], - ], - }; - - mockReadString.mockImplementation((data: string, options: any) => { - if (data === mockCSVData1 && options.complete) { - options.complete(mockParsedData1); - } else if (data === mockCSVData2 && options.complete) { - options.complete(mockParsedData2); - } - }); - - const { rerender } = render(); - - mockUseEntityExportModalProvider.mockReturnValue({ - triggerExportForBulkEdit: mockTriggerExportForBulkEdit, - csvExportData: mockCSVData1, - clearCSVExportData: mockClearCSVExportData, - } as any); - - rerender(); - - await waitFor(() => { - expect(mockGetLineageColumnsAndDataSourceFromCSV).toHaveBeenCalledWith( - mockParsedData1.data - ); - }); - - mockUseEntityExportModalProvider.mockReturnValue({ - triggerExportForBulkEdit: mockTriggerExportForBulkEdit, - csvExportData: mockCSVData2, - clearCSVExportData: mockClearCSVExportData, - } as any); - - rerender(); - - await waitFor(() => { - expect(mockGetLineageColumnsAndDataSourceFromCSV).toHaveBeenCalledWith( - mockParsedData2.data - ); - }); - }); -}); - -describe('LINEAGE_TABLE_COLUMN_LOCALIZATION_KEYS', () => { - it('should contain all required column keys', () => { - const expectedKeys = [ - 'fromEntityFQN', - 'fromServiceName', - 'fromServiceType', - 'fromOwners', - 'fromDomain', - 'toEntityFQN', - 'toServiceName', - 'toServiceType', - 'toOwners', - 'toDomain', - 'fromChildEntityFQN', - 'toChildEntityFQN', - 'pipelineName', - 'pipelineDisplayName', - 'pipelineType', - 'pipelineDescription', - 'pipelineOwners', - 'pipelineDomain', - 'pipelineServiceName', - 'pipelineServiceType', - ]; - - expectedKeys.forEach((key) => { - expect(LINEAGE_TABLE_COLUMN_LOCALIZATION_KEYS).toHaveProperty(key); - expect(LINEAGE_TABLE_COLUMN_LOCALIZATION_KEYS[key]).toMatch(/^label\./); - }); - }); - - it('should have proper localization key format', () => { - Object.values(LINEAGE_TABLE_COLUMN_LOCALIZATION_KEYS).forEach((value) => { - expect(value).toMatch(/^label\.[a-z-]+$/); - }); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Lineage/LineageTable/LineageTable.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Lineage/LineageTable/LineageTable.component.tsx deleted file mode 100644 index 007dc6f9fcf..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Lineage/LineageTable/LineageTable.component.tsx +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2025 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 { ColumnsType } from 'antd/lib/table'; -import { isEmpty } from 'lodash'; -import { useCallback, useEffect, useState } from 'react'; -import { readString } from 'react-papaparse'; -import { ExportTypes } from '../../../constants/Export.constants'; -import { TABLE_SCROLL_VALUE } from '../../../constants/Table.constants'; -import { useLineageProvider } from '../../../context/LineageProvider/LineageProvider'; -import { useFqn } from '../../../hooks/useFqn'; -import { getLineageTableConfig } from '../../../utils/EntityLineageUtils'; -import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../common/Loader/Loader'; -import Table from '../../common/Table/Table'; -import { useEntityExportModalProvider } from '../../Entity/EntityExportModalProvider/EntityExportModalProvider.component'; -import './lineage-table.less'; - -const LineageTable = () => { - const { fqn } = useFqn(); - const { exportLineageData } = useLineageProvider(); - const { triggerExportForBulkEdit, csvExportData, clearCSVExportData } = - useEntityExportModalProvider(); - const [tableConfig, setTableConfig] = useState<{ - isLoading: boolean; - columns: ColumnsType; - dataSource: Record[]; - }>({ - isLoading: true, - columns: [], - dataSource: [], - }); - - const onCSVReadComplete = useCallback((results: { data: string[][] }) => { - const { columns, dataSource } = getLineageTableConfig( - results.data as string[][] - ); - - setTableConfig({ - isLoading: false, - columns, - dataSource, - }); - }, []); - - useEffect(() => { - setTableConfig({ - isLoading: true, - columns: [], - dataSource: [], - }); - - triggerExportForBulkEdit({ - name: fqn, - onExport: exportLineageData, - exportTypes: [ExportTypes.CSV], - hideExportModal: true, - }); - }, []); - - useEffect(() => { - if (csvExportData) { - readString(csvExportData, { - worker: true, - skipEmptyLines: true, - complete: onCSVReadComplete, - }); - } - }, [csvExportData]); - - useEffect(() => { - // clear the csvExportData data from the state - return () => { - clearCSVExportData(); - }; - }, []); - - if (tableConfig.isLoading) { - return ; - } - - if (isEmpty(tableConfig.columns)) { - return ; - } - - return ( - - ); -}; - -export default LineageTable; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Lineage/LineageTable/lineage-table.less b/openmetadata-ui/src/main/resources/ui/src/components/Lineage/LineageTable/lineage-table.less deleted file mode 100644 index 5e693c772a8..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Lineage/LineageTable/lineage-table.less +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2025 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. - */ -.header-icon { - width: 20px; - height: 20px; -} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.tsx index ccd30a04cac..7350b2a2560 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/LineageTable/LineageTable.tsx @@ -11,7 +11,6 @@ * limitations under the License. */ import { SettingOutlined } from '@ant-design/icons'; -import { ListItemIcon, ListItemText } from '@mui/material'; import Button from '@mui/material/Button'; import Chip from '@mui/material/Chip'; import MenuItem from '@mui/material/MenuItem'; @@ -384,8 +383,8 @@ const LineageTable = () => { handlePageChange(currentPage); setImpactOnEl(null); }}> - {option.icon} - {option.label} + {option.icon} + {option.label} ))} @@ -555,6 +554,7 @@ const LineageTable = () => {