From b55d5be3bc997f4f360d57fdf7afa477407582ea Mon Sep 17 00:00:00 2001
From: Karan Hotchandani <33024356+karanh37@users.noreply.github.com>
Date: Sat, 6 Jul 2024 17:52:42 +0530
Subject: [PATCH] Show Data Assets in hierarchy on Explore (#16938)
* initial commit
* push changes
* update explore page
* fix explore page
* update labels
* add tests
* fix review comments
* fix review comments
* fix review comments
* fix sizing
---
.../playwright/e2e/Pages/ExploreTree.spec.ts | 87 +++++++++
.../resources/ui/src/assets/svg/ic-search.svg | 2 +-
.../AssetSelectionModal.tsx | 4 +-
.../CustomControls.component.tsx | 2 +-
.../Explore/ExplorePage.interface.ts | 2 +-
.../ExploreTree/ExploreTree.interface.ts | 45 +++++
.../Explore/ExploreTree/ExploreTree.test.tsx | 32 +++
.../Explore/ExploreTree/ExploreTree.tsx | 184 ++++++++++++++++++
.../components/Explore/useExplore.store.ts | 24 +++
.../ExploreV1/ExploreV1.component.tsx | 94 +++++++--
.../src/components/ExploreV1/exploreV1.less | 30 ++-
.../tabs/AssetsTabs.component.tsx | 2 +-
.../src/constants/AdvancedSearch.constants.ts | 31 +++
.../ExplorePage/ExplorePage.interface.ts | 5 +
.../ExplorePage/ExplorePageV1.component.tsx | 111 ++++++-----
.../src/main/resources/ui/src/styles/app.less | 3 +
...ore.utils.test.ts => ExploreUtils.test.ts} | 101 +++++++++-
.../{Explore.utils.ts => ExploreUtils.ts} | 59 ++++++
.../resources/ui/src/utils/SearchClassBase.ts | 79 ++++++++
.../ui/src/utils/ServiceUtilClassBase.ts | 12 +-
20 files changed, 828 insertions(+), 81 deletions(-)
create mode 100644 openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts
create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.interface.ts
create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.test.tsx
create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.tsx
create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Explore/useExplore.store.ts
rename openmetadata-ui/src/main/resources/ui/src/utils/{Explore.utils.test.ts => ExploreUtils.test.ts} (60%)
rename openmetadata-ui/src/main/resources/ui/src/utils/{Explore.utils.ts => ExploreUtils.ts} (78%)
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts
new file mode 100644
index 00000000000..d3bd900a46b
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreTree.spec.ts
@@ -0,0 +1,87 @@
+/*
+ * 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 test, { expect } from '@playwright/test';
+import { SidebarItem } from '../../constant/sidebar';
+import { redirectToHomePage } from '../../utils/common';
+import { sidebarClick } from '../../utils/sidebar';
+
+// use the admin user to login
+test.use({ storageState: 'playwright/.auth/admin.json' });
+
+test.beforeEach(async ({ page }) => {
+ await redirectToHomePage(page);
+ await sidebarClick(page, SidebarItem.EXPLORE);
+});
+
+test('Explore Tree', async ({ page }) => {
+ await page.getByTestId('explore-tree-tab').getByText('Tree').click();
+
+ await test.step('Check the explore tree', async () => {
+ await expect(page.getByRole('tree')).toContainText('Databases');
+ await expect(page.getByRole('tree')).toContainText('Dashboards');
+ await expect(page.getByRole('tree')).toContainText('Pipelines');
+ await expect(page.getByRole('tree')).toContainText('Topics');
+ await expect(page.getByRole('tree')).toContainText('ML Models');
+ await expect(page.getByRole('tree')).toContainText('Containers');
+ await expect(page.getByRole('tree')).toContainText('Search Indexes');
+ await expect(page.getByRole('tree')).toContainText('Governance');
+
+ await page
+ .locator('div')
+ .filter({ hasText: /^Governance$/ })
+ .locator('svg')
+ .first()
+ .click();
+
+ await expect(page.getByRole('tree')).toContainText('Glossaries');
+ await expect(page.getByRole('tree')).toContainText('Tags');
+ });
+
+ await test.step('Check the quick filters', async () => {
+ await expect(
+ page.getByTestId('search-dropdown-Domain').locator('span')
+ ).toContainText('Domain');
+ await expect(page.getByTestId('search-dropdown-Owner')).toContainText(
+ 'Owner'
+ );
+ await expect(
+ page.getByTestId('search-dropdown-Tag').locator('span')
+ ).toContainText('Tag');
+
+ await page.getByRole('button', { name: 'Tier' }).click();
+
+ await expect(
+ page.getByTestId('search-dropdown-Tier').locator('span')
+ ).toContainText('Tier');
+ await expect(
+ page.getByTestId('search-dropdown-Service').locator('span')
+ ).toContainText('Service');
+ await expect(
+ page.getByTestId('search-dropdown-Service Type').locator('span')
+ ).toContainText('Service Type');
+ });
+
+ await test.step('Click on tree item and check quick filter', async () => {
+ await page.getByTestId('explore-tree-title-Glossaries').click();
+
+ await expect(page.getByTestId('search-dropdown-Data Assets')).toContainText(
+ 'Data Assets: glossaryTerm'
+ );
+
+ await page.getByTestId('explore-tree-title-Tags').click();
+
+ await expect(page.getByTestId('search-dropdown-Data Assets')).toContainText(
+ 'Data Assets: tag'
+ );
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-search.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-search.svg
index 6e7771027c4..206c4dcc02c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-search.svg
+++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-search.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/AssetsSelectionModal/AssetSelectionModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/AssetsSelectionModal/AssetSelectionModal.tsx
index 75e2b5b611d..8fd9b0ecf39 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/AssetsSelectionModal/AssetSelectionModal.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/AssetsSelectionModal/AssetSelectionModal.tsx
@@ -65,11 +65,11 @@ import {
import { searchQuery } from '../../../rest/searchAPI';
import { getAssetsPageQuickFilters } from '../../../utils/AdvancedSearchUtils';
import { getEntityReferenceFromEntity } from '../../../utils/EntityUtils';
+import { getCombinedQueryFilterObject } from '../../../utils/ExplorePage/ExplorePageUtils';
import {
getAggregations,
getQuickFilterQuery,
-} from '../../../utils/Explore.utils';
-import { getCombinedQueryFilterObject } from '../../../utils/ExplorePage/ExplorePageUtils';
+} from '../../../utils/ExploreUtils';
import { showErrorToast } from '../../../utils/ToastUtils';
import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import Loader from '../../common/Loader/Loader';
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 3abb0b532cf..60c6a410ab1 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
@@ -37,7 +37,7 @@ import { SearchIndex } from '../../../enums/search.enum';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
import { getAssetsPageQuickFilters } from '../../../utils/AdvancedSearchUtils';
import { getLoadingStatusValue } from '../../../utils/EntityLineageUtils';
-import { getQuickFilterQuery } from '../../../utils/Explore.utils';
+import { getQuickFilterQuery } from '../../../utils/ExploreUtils';
import { ExploreQuickFilterField } from '../../Explore/ExplorePage.interface';
import ExploreQuickFilters from '../../Explore/ExploreQuickFilters';
import { AssetsOfEntity } from '../../Glossary/GlossaryTerms/tabs/AssetsTabs.interface';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExplorePage.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExplorePage.interface.ts
index b625b7578b6..e13ee8aa1fc 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExplorePage.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExplorePage.interface.ts
@@ -75,7 +75,7 @@ export interface ExploreProps {
queryFilter: QueryFilterInterface | undefined
) => void;
- searchIndex: ExploreSearchIndex;
+ searchIndex: SearchIndex.DATA_ASSET | ExploreSearchIndex;
onChangeSearchIndex: (searchIndex: ExploreSearchIndex) => void;
sortValue: string;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.interface.ts
new file mode 100644
index 00000000000..80db602f537
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.interface.ts
@@ -0,0 +1,45 @@
+/*
+ * 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 { ReactNode } from 'react';
+import { EntityFields } from '../../../enums/AdvancedSearch.enum';
+import { EntityType } from '../../../enums/entity.enum';
+import { ExploreQuickFilterField } from '../ExplorePage.interface';
+
+export type ExploreTreeNode = {
+ title: ReactNode;
+ key: string;
+ children?: ExploreTreeNode[];
+ isLeaf?: boolean;
+ icon?: JSX.Element | SvgComponent;
+ data?: TreeNodeData;
+};
+
+export type ExploreTreeProps = {
+ onFieldValueSelect: (field: ExploreQuickFilterField[]) => void;
+};
+
+export type TreeNodeData = {
+ isRoot?: boolean;
+ currentBucketKey?: string;
+ currentBucketValue?: string;
+ filterField?: ExploreQuickFilterField[];
+ parentSearchIndex?: string;
+ rootIndex?: string;
+ entityType?: EntityType;
+};
+
+export type DatabaseFields =
+ | EntityFields.SERVICE_TYPE
+ | EntityFields.SERVICE
+ | EntityFields.DATABASE
+ | EntityFields.DATABASE_SCHEMA;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.test.tsx
new file mode 100644
index 00000000000..ed0bc4b0371
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.test.tsx
@@ -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 { render } from '@testing-library/react';
+import React from 'react';
+import ExploreTree from './ExploreTree';
+
+describe('ExploreTree', () => {
+ it('renders the correct tree nodes', () => {
+ const { getByText } = render(
+