From 05853b832b333f1bc5ecb8dcaeb942ab2ca56594 Mon Sep 17 00:00:00 2001 From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com> Date: Tue, 12 Aug 2025 20:46:13 +0530 Subject: [PATCH] fix(ui): expand icon after updating column details (#22790) * fix(ui): expand icon after updating column details * add tests and fix container schema * fix tests (cherry picked from commit 07c3acfdbc21ba6577709301f8dd64e85fa2207c) --- .../playwright/e2e/Features/Container.spec.ts | 36 +++++++++++++++ .../e2e/Features/Dashboards.spec.ts | 46 +++++++++++++++++-- .../ui/playwright/e2e/Features/Table.spec.ts | 46 +++++++++++++++++-- .../ContainerDataModel.test.tsx | 1 + .../ContainerDataModel/ContainerDataModel.tsx | 15 ++++-- .../ContainerVersion.component.tsx | 5 +- .../ModelTab/ModelTab.component.tsx | 7 ++- .../SchemaTable/SchemaTable.component.tsx | 5 +- 8 files changed, 146 insertions(+), 15 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Container.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Container.spec.ts index 3ba743cc98f..41ac253907f 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Container.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Container.spec.ts @@ -15,6 +15,10 @@ import { CONTAINER_CHILDREN } from '../../constant/contianer'; import { ContainerClass } from '../../support/entity/ContainerClass'; import { performAdminLogin } from '../../utils/admin'; import { redirectToHomePage } from '../../utils/common'; +import { + assignTagToChildren, + removeTagsFromChildren, +} from '../../utils/entity'; import { test } from '../fixtures/pages'; const container = new ContainerClass(); @@ -116,4 +120,36 @@ test.describe('Container entity specific tests ', () => { 'Page 1 of 2' ); }); + + test('expand / collapse should not appear after updating nested fields for container', async ({ + page, + }) => { + await page.goto('/container/s3_storage_sample.departments.finance'); + + await page.waitForLoadState('networkidle'); + await page.waitForSelector('[data-testid="loader"]', { + state: 'detached', + }); + + await assignTagToChildren({ + page, + tag: 'PersonalData.Personal', + rowId: 'budget_executor', + entityEndpoint: 'containers', + }); + + // Should not show expand icon for non-nested columns + expect( + page + .locator('[data-row-key="budget_executor"]') + .getByTestId('expand-icon') + ).not.toBeVisible(); + + await removeTagsFromChildren({ + page, + tags: ['PersonalData.Personal'], + rowId: 'budget_executor', + entityEndpoint: 'containers', + }); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Dashboards.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Dashboards.spec.ts index 9276b7029d7..b1133b14404 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Dashboards.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Dashboards.spec.ts @@ -14,14 +14,18 @@ import { expect } from '@playwright/test'; import { DashboardServiceClass } from '../../support/entity/service/DashboardServiceClass'; import { performAdminLogin } from '../../utils/admin'; import { redirectToHomePage } from '../../utils/common'; -import { generateEntityChildren } from '../../utils/entity'; +import { + assignTagToChildren, + generateEntityChildren, + removeTagsFromChildren, +} from '../../utils/entity'; import { test } from '../fixtures/pages'; const dashboardEntity = new DashboardServiceClass(); -test.slow(true); - test.describe('Dashboards', () => { + test.slow(true); + test.beforeAll('Setup pre-requests', async ({ browser }) => { const { apiContext, afterAction } = await performAdminLogin(browser); @@ -79,3 +83,39 @@ test.describe('Dashboards', () => { ); }); }); + +test.describe('Data Model', () => { + test('expand / collapse should not appear after updating nested fields for dashboardDataModels', async ({ + page, + }) => { + await page.goto( + '/dashboardDataModel/sample_superset.model.big_analytics_data_model_with_nested_columns' + ); + + await page.waitForLoadState('networkidle'); + await page.waitForSelector('[data-testid="loader"]', { + state: 'detached', + }); + + await assignTagToChildren({ + page, + tag: 'PersonalData.Personal', + rowId: 'revenue_metrics_0031', + entityEndpoint: 'dashboard/datamodels', + }); + + // Should not show expand icon for non-nested columns + expect( + page + .locator('[data-row-key="revenue_metrics_0031"]') + .getByTestId('expand-icon') + ).not.toBeVisible(); + + await removeTagsFromChildren({ + page, + tags: ['PersonalData.Personal'], + rowId: 'revenue_metrics_0031', + entityEndpoint: 'dashboard/datamodels', + }); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Table.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Table.spec.ts index 201cee0381c..5c10ab50c75 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Table.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Table.spec.ts @@ -15,14 +15,16 @@ import { SidebarItem } from '../../constant/sidebar'; import { TableClass } from '../../support/entity/TableClass'; import { performAdminLogin } from '../../utils/admin'; import { redirectToHomePage } from '../../utils/common'; -import { getFirstRowColumnLink } from '../../utils/entity'; +import { + assignTagToChildren, + getFirstRowColumnLink, + removeTagsFromChildren, +} from '../../utils/entity'; import { sidebarClick } from '../../utils/sidebar'; import { test } from '../fixtures/pages'; const table1 = new TableClass(); -test.slow(true); - test.describe('Table pagination sorting search scenarios ', () => { test.beforeAll('Setup pre-requests', async ({ browser }) => { test.slow(true); @@ -438,4 +440,42 @@ test.describe('Table & Data Model columns table pagination', () => { page.locator('[data-row-key="shop_id"]').getByTestId('expand-icon') ).not.toBeVisible(); }); + + test('expand / collapse should not appear after updating nested fields table', async ({ + page, + }) => { + await page.goto( + '/table/sample_data.ecommerce_db.shopify.performance_test_table' + ); + + await page.waitForLoadState('networkidle'); + await page.waitForSelector('[data-testid="loader"]', { + state: 'detached', + }); + + await assignTagToChildren({ + page, + tag: 'PersonalData.Personal', + rowId: + 'sample_data.ecommerce_db.shopify.performance_test_table.test_col_0044', + entityEndpoint: 'tables', + }); + + // Should not show expand icon for non-nested columns + expect( + page + .locator( + '[data-row-key="sample_data.ecommerce_db.shopify.performance_test_table.test_col_0044"]' + ) + .getByTestId('expand-icon') + ).not.toBeVisible(); + + await removeTagsFromChildren({ + page, + tags: ['PersonalData.Personal'], + rowId: + 'sample_data.ecommerce_db.shopify.performance_test_table.test_col_0044', + entityEndpoint: 'tables', + }); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.test.tsx index 7a567f374f6..65bbffe3dee 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.test.tsx @@ -114,6 +114,7 @@ jest.mock('../../../utils/TableUtils', () => ({ 'glossary', ]), handleUpdateTableColumnSelections: jest.fn(), + pruneEmptyChildren: jest.fn().mockImplementation((value) => value), })); jest.mock('../../../utils/TableTags/TableTags.utils', () => ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.tsx index 93122dc28b2..f9f05f0add5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerDataModel/ContainerDataModel.tsx @@ -42,7 +42,10 @@ import { getAllTags, searchTagInData, } from '../../../utils/TableTags/TableTags.utils'; -import { getTableExpandableConfig } from '../../../utils/TableUtils'; +import { + getTableExpandableConfig, + pruneEmptyChildren, +} from '../../../utils/TableUtils'; import { EntityAttachmentProvider } from '../../common/EntityDescription/EntityAttachmentProvider/EntityAttachmentProvider'; import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; import Table from '../../common/Table/Table'; @@ -66,6 +69,8 @@ const ContainerDataModel: FC = ({ const [editContainerColumnDescription, setEditContainerColumnDescription] = useState(); + const schema = pruneEmptyChildren(dataModel?.columns ?? []); + const handleFieldTagsChange = useCallback( async (selectedTags: EntityTags[], editColumnTag: Column) => { if (selectedTags && editColumnTag) { @@ -99,13 +104,13 @@ const ContainerDataModel: FC = ({ }; const tagFilter = useMemo(() => { - const tags = getAllTags(dataModel?.columns ?? []); + const tags = getAllTags(schema); return groupBy(uniqBy(tags, 'value'), (tag) => tag.source) as Record< TagSource, TagFilterOptions[] >; - }, [dataModel?.columns]); + }, [schema]); const columns: ColumnsType = useMemo( () => [ @@ -226,7 +231,7 @@ const ContainerDataModel: FC = ({ ] ); - if (isEmpty(dataModel?.columns)) { + if (isEmpty(schema)) { return ; } @@ -236,7 +241,7 @@ const ContainerDataModel: FC = ({ className="align-table-filter-left" columns={columns} data-testid="container-data-model-table" - dataSource={dataModel?.columns} + dataSource={schema} defaultVisibleColumns={DEFAULT_CONTAINER_DATA_MODEL_VISIBLE_COLUMNS} expandable={{ ...getTableExpandableConfig(), diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.component.tsx index dac5259a4d8..5a56d9b4695 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.component.tsx @@ -35,6 +35,7 @@ import { getEntityVersionTags, } from '../../../utils/EntityVersionUtils'; import { getVersionPath } from '../../../utils/RouterUtils'; +import { pruneEmptyChildren } from '../../../utils/TableUtils'; import { useRequiredParams } from '../../../utils/useRequiredParams'; import { CustomPropertyTable } from '../../common/CustomPropertyTable/CustomPropertyTable'; import DescriptionV1 from '../../common/EntityDescription/DescriptionV1'; @@ -88,7 +89,9 @@ const ContainerVersion: React.FC = ({ ); const columns = useMemo(() => { - const colList = cloneDeep(currentVersionData.dataModel?.columns); + const colList = cloneDeep( + pruneEmptyChildren(currentVersionData.dataModel?.columns ?? []) + ); return getColumnsDataWithVersionChanges( changeDescription, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/ModelTab/ModelTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/ModelTab/ModelTab.component.tsx index 177dfb878be..178b9c467de 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/ModelTab/ModelTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/ModelTab/ModelTab.component.tsx @@ -12,7 +12,7 @@ */ import { Typography } from 'antd'; import { ColumnsType } from 'antd/lib/table'; -import { groupBy, omit, uniqBy } from 'lodash'; +import { groupBy, isEmpty, omit, uniqBy } from 'lodash'; import { EntityTags, TagFilterOptions } from 'Models'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -177,12 +177,15 @@ const ModelTab = () => { field?: keyof Column ) => { const response = await updateDataModelColumn(columnFqn, column); + const cleanResponse = isEmpty(response.children) + ? omit(response, 'children') + : response; setPaginatedColumns((prev) => prev.map((col) => col.fullyQualifiedName === columnFqn ? // Have to omit the field which is being updated to avoid persisted old value - { ...omit(col, field ?? ''), ...response } + { ...omit(col, field ?? ''), ...cleanResponse } : col ) ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx index c8fed741370..c2ce53d9924 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx @@ -314,12 +314,15 @@ const SchemaTable = () => { field?: keyof Column ) => { const response = await updateTableColumn(columnFqn, column); + const cleanResponse = isEmpty(response.children) + ? omit(response, 'children') + : response; setTableColumns((prev) => prev.map((col) => col.fullyQualifiedName === columnFqn ? // Have to omit the field which is being updated to avoid persisted old value - { ...omit(col, field ?? ''), ...response } + { ...omit(col, field ?? ''), ...cleanResponse } : col ) );