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 = ({ ({ @@ -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/components/Explore/ExploreQuickFilters.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.interface.ts index 472c6f5baec1..252c5d2f1866 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.interface.ts @@ -23,6 +23,7 @@ export interface ExploreQuickFiltersProps { onAdvanceSearch?: () => 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 7eee73c4d7de..d6625e5a5f79 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 @@ -53,7 +53,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, @@ -229,38 +228,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/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; } 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, }, }; }