From b3319705f7c88d98f520075b9d51f12477865a22 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Thu, 25 Jan 2024 14:03:00 +0530 Subject: [PATCH 1/2] fix: minor search issues --- .../CustomControls.component.tsx | 1 + .../Explore/ExploreQuickFilters.interface.ts | 1 + .../Explore/ExploreQuickFilters.tsx | 4 +- .../SearchDropdown.interface.ts | 1 + .../SearchDropdown/SearchDropdown.tsx | 11 ++-- .../ExplorePage/ExplorePageV1.component.tsx | 65 ++++++++++--------- .../resources/ui/src/utils/SearchClassBase.ts | 28 ++++++++ 7 files changed, 75 insertions(+), 36 deletions(-) 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 e0ee967a6bd2..120f6aa13c68 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 @@ -237,6 +237,7 @@ const CustomControls: FC = ({ void; showDeleted?: boolean; onChangeShowDeleted?: (showDeleted: boolean) => void; + independent?: boolean; // flag to indicate if the filters are independent of aggregations } export interface FilterFieldsMenuItem { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.tsx index 38a17eb9d3af..4a535d5e3dbe 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.tsx @@ -41,6 +41,7 @@ const ExploreQuickFilters: FC = ({ fields, index, aggregations, + independent = false, onFieldValueSelect, }) => { const location = useLocation(); @@ -151,7 +152,7 @@ const ExploreQuickFilters: FC = ({ return; } - if (aggregations?.[key] && key !== TIER_FQN_KEY) { + if (key !== TIER_FQN_KEY) { const res = await getAggregateFieldOptions( index, key, @@ -197,6 +198,7 @@ const ExploreQuickFilters: FC = ({ void; onGetInitialOptions?: (searchKey: string) => void; onSearch: (searchText: string, searchKey: string) => void; + independent?: boolean; // flag to indicate if the filters are independent of aggregations } export interface SearchDropdownOption { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/SearchDropdown/SearchDropdown.tsx b/openmetadata-ui/src/main/resources/ui/src/components/SearchDropdown/SearchDropdown.tsx index fa4e1b23f1e9..38e6a0e4f57c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/SearchDropdown/SearchDropdown.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/SearchDropdown/SearchDropdown.tsx @@ -63,6 +63,7 @@ const SearchDropdown: FC = ({ onGetInitialOptions, onSearch, index, + independent = false, }) => { const tabsInfo = searchClassBase.getTabsInfo(); const { t } = useTranslation(); @@ -75,9 +76,11 @@ const SearchDropdown: FC = ({ // derive menu props from options and selected keys const menuOptions: MenuProps['items'] = useMemo(() => { // Filtering out selected options - const selectedOptionsObj = options.filter((option) => - selectedOptions.find((selectedOpt) => option.key === selectedOpt.key) - ); + const selectedOptionsObj = independent + ? selectedOptions + : options.filter((option) => + selectedOptions.find((selectedOpt) => option.key === selectedOpt.key) + ); if (fixedOrderOptions) { return options.map((item) => ({ @@ -116,7 +119,7 @@ const SearchDropdown: FC = ({ return [...selectedOptionKeys, ...otherOptions]; } - }, [options, selectedOptions, fixedOrderOptions]); + }, [options, selectedOptions, fixedOrderOptions, independent]); // handle menu item click const handleMenuItemClick: MenuItemProps['onClick'] = (info) => { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePageV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePageV1.component.tsx index 58bb3b9fe52a..21b0300f6063 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePageV1.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePageV1.component.tsx @@ -52,7 +52,6 @@ import { findActiveSearchIndex } from '../../utils/Explore.utils'; import { getCombinedQueryFilterObject } from '../../utils/ExplorePage/ExplorePageUtils'; import searchClassBase from '../../utils/SearchClassBase'; import { escapeESReservedCharacters } from '../../utils/StringsUtils'; -import { getEntityIcon } from '../../utils/TableUtils'; import { showErrorToast } from '../../utils/ToastUtils'; import { QueryFieldInterface, @@ -220,38 +219,42 @@ const ExplorePageV1: FunctionComponent = () => { const tabItems = useMemo(() => { const items = Object.entries(tabsInfo).map( - ([tabSearchIndex, tabDetail]) => ({ - key: tabSearchIndex, - label: ( -
-
- - {getEntityIcon(tabSearchIndex)} + ([tabSearchIndex, tabDetail]) => { + const Icon = tabDetail.icon; + + return { + key: tabSearchIndex, + label: ( +
+
+ + + + + {tabDetail.label} + +
+ + {!isNil(searchHitCounts) + ? getCountBadge( + searchHitCounts[tabSearchIndex as ExploreSearchIndex], + '', + tabSearchIndex === searchIndex + ) + : getCountBadge()} - - {tabDetail.label} -
- - {!isNil(searchHitCounts) - ? getCountBadge( - searchHitCounts[tabSearchIndex as ExploreSearchIndex], - '', - tabSearchIndex === searchIndex - ) - : getCountBadge()} - -
- ), - count: searchHitCounts - ? searchHitCounts[tabSearchIndex as ExploreSearchIndex] - : 0, - }) + ), + count: searchHitCounts + ? searchHitCounts[tabSearchIndex as ExploreSearchIndex] + : 0, + }; + } ); return searchQueryParam diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SearchClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/SearchClassBase.ts index f4729a0e4b87..e3a4b4f91549 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/SearchClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/SearchClassBase.ts @@ -10,7 +10,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { SearchOutlined } from '@ant-design/icons'; import i18next from 'i18next'; +import { ReactComponent as ClassificationIcon } from '../assets/svg/classification.svg'; +import { ReactComponent as IconDataModel } from '../assets/svg/data-model.svg'; +import { ReactComponent as GlossaryIcon } from '../assets/svg/glossary.svg'; +import { ReactComponent as DashboardIcon } from '../assets/svg/ic-dashboard.svg'; +import { ReactComponent as DataProductIcon } from '../assets/svg/ic-data-product.svg'; +import { ReactComponent as DatabaseIcon } from '../assets/svg/ic-database.svg'; +import { ReactComponent as MlModelIcon } from '../assets/svg/ic-ml-model.svg'; +import { ReactComponent as PipelineIcon } from '../assets/svg/ic-pipeline.svg'; +import { ReactComponent as SchemaIcon } from '../assets/svg/ic-schema.svg'; +import { ReactComponent as ContainerIcon } from '../assets/svg/ic-storage.svg'; +import { ReactComponent as IconStoredProcedure } from '../assets/svg/ic-stored-procedure.svg'; +import { ReactComponent as TableIcon } from '../assets/svg/ic-table.svg'; +import { ReactComponent as TopicIcon } from '../assets/svg/ic-topic.svg'; import { ReactComponent as IconTable } from '../assets/svg/table-grey.svg'; import { Option, @@ -82,84 +96,98 @@ class SearchClassBase { sortingFields: tableSortingFields, sortField: INITIAL_SORT_FIELD, path: 'tables', + icon: TableIcon, }, [SearchIndex.STORED_PROCEDURE]: { label: i18n.t('label.stored-procedure-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'storedProcedure', + icon: IconStoredProcedure, }, [SearchIndex.DATABASE]: { label: i18n.t('label.database-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'databases', + icon: DatabaseIcon, }, [SearchIndex.DATABASE_SCHEMA]: { label: i18n.t('label.database-schema-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'databaseSchemas', + icon: SchemaIcon, }, [SearchIndex.DASHBOARD]: { label: i18n.t('label.dashboard-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'dashboards', + icon: DashboardIcon, }, [SearchIndex.DASHBOARD_DATA_MODEL]: { label: i18n.t('label.dashboard-data-model-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'dashboardDataModel', + icon: IconDataModel, }, [SearchIndex.PIPELINE]: { label: i18n.t('label.pipeline-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'pipelines', + icon: PipelineIcon, }, [SearchIndex.TOPIC]: { label: i18n.t('label.topic-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'topics', + icon: TopicIcon, }, [SearchIndex.MLMODEL]: { label: i18n.t('label.ml-model-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'mlmodels', + icon: MlModelIcon, }, [SearchIndex.CONTAINER]: { label: i18n.t('label.container-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'containers', + icon: ContainerIcon, }, [SearchIndex.SEARCH_INDEX]: { label: i18n.t('label.search-index-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'searchIndexes', + icon: SearchOutlined, }, [SearchIndex.GLOSSARY]: { label: i18n.t('label.glossary-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'glossaries', + icon: GlossaryIcon, }, [SearchIndex.TAG]: { label: i18n.t('label.tag-plural'), sortingFields: entitySortingFields, sortField: INITIAL_SORT_FIELD, path: 'tags', + icon: ClassificationIcon, }, [SearchIndex.DATA_PRODUCT]: { label: i18n.t('label.data-product-plural'), sortingFields: tableSortingFields, sortField: INITIAL_SORT_FIELD, path: 'dataProducts', + icon: DataProductIcon, }, }; } From e9d43827b44c1c2ebcd1e94bd5daeac633d622b9 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Thu, 25 Jan 2024 16:26:02 +0530 Subject: [PATCH 2/2] show dbt icon in lineage --- .../EntityLineage/CustomNodeV1.test.tsx | 40 +++++++++++++++++++ .../EntityLineage/LineageNodeLabelV1.tsx | 37 +++++++++-------- .../NodeSuggestions.component.tsx | 2 +- .../ui/src/styles/components/size.less | 6 +++ 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/CustomNodeV1.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/CustomNodeV1.test.tsx index 90322635dc38..3d65ff76158d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/CustomNodeV1.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/CustomNodeV1.test.tsx @@ -13,6 +13,7 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; import { ReactFlowProvider } from 'reactflow'; +import { ModelType } from '../../../generated/entity/data/table'; import CustomNodeV1Component from './CustomNodeV1.component'; const mockNodeDataProps = { @@ -39,6 +40,33 @@ const mockNodeDataProps = { zIndex: 0, }; +const mockNodeDataProps2 = { + id: 'node1', + type: 'table', + data: { + node: { + fullyQualifiedName: 'dim_customer', + type: 'table', + entityType: 'table', + id: 'khjahjfja', + columns: [ + { fullyQualifiedName: 'col1', name: 'col1' }, + { fullyQualifiedName: 'col2', name: 'col2' }, + { fullyQualifiedName: 'col3', name: 'col3' }, + ], + dataModel: { + modelType: ModelType.Dbt, + }, + }, + }, + selected: false, + isConnectable: false, + xPos: 0, + yPos: 0, + dragging: true, + zIndex: 0, +}; + const onMockColumnClick = jest.fn(); jest.mock('../../LineageProvider/LineageProvider', () => ({ @@ -74,4 +102,16 @@ describe('CustomNodeV1', () => { expect(screen.getByTestId('lineage-node-dim_customer')).toBeInTheDocument(); expect(screen.getByTestId('expand-cols-btn')).toBeInTheDocument(); }); + + it('renders node with dbt icon correctly', () => { + render( + + + + ); + + expect(screen.getByTestId('lineage-node-dim_customer')).toBeInTheDocument(); + expect(screen.getByTestId('expand-cols-btn')).toBeInTheDocument(); + expect(screen.getByTestId('dbt-icon')).toBeInTheDocument(); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/LineageNodeLabelV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/LineageNodeLabelV1.tsx index db80940f3116..ca24d97c126e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/LineageNodeLabelV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/LineageNodeLabelV1.tsx @@ -12,33 +12,27 @@ */ import { Col, Row, Space, Typography } from 'antd'; -import { get } from 'lodash'; -import React from 'react'; +import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { EntityLineageNodeType } from '../../../enums/entity.enum'; -import { EntityReference } from '../../../generated/entity/type'; +import { ReactComponent as IconDBTModel } from '../../../assets/svg/dbt-model.svg'; +import { EntityType } from '../../../enums/entity.enum'; +import { ModelType, Table } from '../../../generated/entity/data/table'; import { getBreadcrumbsFromFqn } from '../../../utils/EntityUtils'; import { getServiceIcon } from '../../../utils/TableUtils'; import { SourceType } from '../../SearchedData/SearchedData.interface'; import './lineage-node-label.less'; interface LineageNodeLabelProps { - node: EntityReference; + node: SourceType; } const EntityLabel = ({ node }: Pick) => { - const { t } = useTranslation(); - if (node.type === EntityLineageNodeType.LOAD_MORE) { + const showDbtIcon = useMemo(() => { return ( -
- {t('label.load-more')} - {`(${get( - node, - 'pagination_data.childrenLength' - )})`} -
+ (node as SourceType).entityType === EntityType.TABLE && + (node as Table)?.dataModel?.modelType === ModelType.Dbt ); - } + }, [node]); return ( @@ -48,29 +42,34 @@ const EntityLabel = ({ node }: Pick) => {
{node.name} {node.displayName || node.name} + {showDbtIcon && ( +
+ +
+ )} ); }; -const LineageNodeLabelV1 = ({ node }: { node: EntityReference }) => { +const LineageNodeLabelV1 = ({ node }: Pick) => { const { t } = useTranslation(); const breadcrumbs = getBreadcrumbsFromFqn(node.fullyQualifiedName ?? ''); return ( -
+
= ({ autoFocus open showSearch - className="w-72 lineage-node-searchbox" + className="w-76 lineage-node-searchbox" data-testid="node-search-box" options={(data || []).map((entity) => ({ value: entity.fullyQualifiedName, diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/components/size.less b/openmetadata-ui/src/main/resources/ui/src/styles/components/size.less index 8a7e0e886801..435530544c01 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/components/size.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/components/size.less @@ -59,6 +59,9 @@ .w-52 { width: 13rem; /* 208px */ } +.w-54 { + width: 13.5rem; +} .w-56 { width: 14rem; } @@ -71,6 +74,9 @@ .w-72 { width: 288px; } +.w-76 { + width: 19rem; +} .w-400 { width: 400px; }