From f2d338022618d3b53e68c4673cda02164581fe01 Mon Sep 17 00:00:00 2001 From: purnimagarg1 <139125209+purnimagarg1@users.noreply.github.com> Date: Tue, 7 Oct 2025 23:48:12 +0530 Subject: [PATCH] improvement(summary-tab): hide current property in replace dropdown of property header (#14842) --- .../property/properties/BaseProperty.tsx | 5 +- .../properties/__tests__/utils.test.ts | 186 ++++++++++++++++++ .../properties/property/properties/utils.ts | 39 ++++ 3 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 datahub-web-react/src/app/entityV2/summary/properties/property/properties/__tests__/utils.test.ts create mode 100644 datahub-web-react/src/app/entityV2/summary/properties/property/properties/utils.ts diff --git a/datahub-web-react/src/app/entityV2/summary/properties/property/properties/BaseProperty.tsx b/datahub-web-react/src/app/entityV2/summary/properties/property/properties/BaseProperty.tsx index 628d198540..72f9d51d1c 100644 --- a/datahub-web-react/src/app/entityV2/summary/properties/property/properties/BaseProperty.tsx +++ b/datahub-web-react/src/app/entityV2/summary/properties/property/properties/BaseProperty.tsx @@ -4,6 +4,7 @@ import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; import usePropertyMenuItems from '@app/entityV2/summary/properties/menuProperty/usePropertyMenuItems'; +import { filterCurrentItemInReplaceMenu } from '@app/entityV2/summary/properties/property/properties/utils'; import { PropertyComponentProps } from '@app/entityV2/summary/properties/types'; import { usePageTemplateContext } from '@app/homeV3/context/PageTemplateContext'; @@ -85,6 +86,8 @@ export default function BaseProperty({ const menuItems = usePropertyMenuItems(position, property.type); + const filteredItems = filterCurrentItemInReplaceMenu(menuItems, property); + const valuesToShow = useMemo(() => values.slice(0, maxValues ?? DEFAULT_MAX_ITEMS), [values, maxValues]); const valuesToShowInPopover = useMemo(() => values.slice(maxValues ?? DEFAULT_MAX_ITEMS), [values, maxValues]); @@ -124,7 +127,7 @@ export default function BaseProperty({ return ( - + {property.name} diff --git a/datahub-web-react/src/app/entityV2/summary/properties/property/properties/__tests__/utils.test.ts b/datahub-web-react/src/app/entityV2/summary/properties/property/properties/__tests__/utils.test.ts new file mode 100644 index 0000000000..c894cf0981 --- /dev/null +++ b/datahub-web-react/src/app/entityV2/summary/properties/property/properties/__tests__/utils.test.ts @@ -0,0 +1,186 @@ +import { describe, expect, it } from 'vitest'; + +import { ItemType } from '@components/components/Menu/types'; + +import { filterCurrentItemInReplaceMenu } from '@app/entityV2/summary/properties/property/properties/utils'; +import type { AssetProperty } from '@app/entityV2/summary/properties/types'; + +import { StructuredPropertyFieldsFragment } from '@graphql/fragments.generated'; +import { EntityType, StdDataType, SummaryElementType } from '@types'; + +function isItemType(item: ItemType): item is Extract { + return 'children' in item; +} + +describe('filterCurrentItemInReplaceMenu', () => { + it('should return items unchanged if no item with key "replace"', () => { + const menuItems: ItemType[] = [ + { key: 'notReplace', type: 'item', title: 'Item 1' }, + { key: 'anotherItem', type: 'item', title: 'Item 2' }, + ]; + const property: AssetProperty = { + name: 'test', + type: SummaryElementType.Owners, + }; + + const result = filterCurrentItemInReplaceMenu(menuItems, property); + expect(result).toEqual(menuItems); + }); + + it('should not modify replace item if no children present', () => { + const menuItems: ItemType[] = [{ key: 'replace', type: 'item', title: 'Replace' }]; + const property: AssetProperty = { + name: 'test', + type: SummaryElementType.Tags, + }; + + const result = filterCurrentItemInReplaceMenu(menuItems, property); + expect(result).toEqual(menuItems); + }); + + it('should filter excludeKey from children if property.type is not StructuredProperty', () => { + const menuItems: ItemType[] = [ + { + key: 'replace', + type: 'group', + title: 'Replace Group', + children: [ + { key: SummaryElementType.Tags, type: 'item', title: 'To Remove' }, + { key: 'keepMe', type: 'item', title: 'Keep Me' }, + ], + }, + ]; + const property: AssetProperty = { + name: 'test', + type: SummaryElementType.Tags, + }; + + const result = filterCurrentItemInReplaceMenu(menuItems, property); + if (isItemType(result[0])) { + expect(result[0].children).toEqual([{ key: 'keepMe', type: 'item', title: 'Keep Me' }]); + } + }); + + it('should filter nested children in structuredProperties if property.type is StructuredProperty', () => { + const menuItems: ItemType[] = [ + { + key: 'replace', + type: 'group', + title: 'Replace Group', + children: [ + { + key: 'structuredProperties', + type: 'group', + title: 'Structured', + children: [ + { key: 'urn-to-remove', type: 'item', title: 'Remove Me' }, + { key: 'keepMe', type: 'item', title: 'Keep Me' }, + ], + }, + { key: 'somethingElse', type: 'item', title: 'Other' }, + ], + }, + ]; + + const structuredProperty: StructuredPropertyFieldsFragment = { + urn: 'urn-to-remove', + type: EntityType.StructuredProperty, + exists: true, + definition: { + qualifiedName: 'testQualifiedName', + immutable: false, + entityTypes: [ + { + type: EntityType.Dataset, + urn: 'urn:li:dataset', + info: { + type: EntityType.Dataset, + }, + }, + ], + valueType: { + urn: 'urn:li:dataType:string', + type: EntityType.DataType, + info: { + type: StdDataType.String, + }, + }, + }, + }; + + const property: AssetProperty = { + name: 'test', + type: SummaryElementType.StructuredProperty, + structuredProperty, + }; + + const result = filterCurrentItemInReplaceMenu(menuItems, property); + if (isItemType(result[0]) && result[0].children && isItemType(result[0].children[0])) { + expect(result[0].children[0].children).toEqual([{ key: 'keepMe', type: 'item', title: 'Keep Me' }]); + expect(result[0].children[1]).toEqual({ key: 'somethingElse', type: 'item', title: 'Other' }); + } + }); + + it('should handle structuredProperties node without children gracefully', () => { + const menuItems: ItemType[] = [ + { + key: 'replace', + type: 'group', + title: 'Replace Group', + children: [{ key: 'structuredProperties', type: 'group', title: 'Structured' }], + }, + ]; + + const structuredProperty: StructuredPropertyFieldsFragment = { + urn: 'urn-to-remove', + type: EntityType.StructuredProperty, + exists: true, + definition: { + qualifiedName: 'testQualifiedName', + immutable: false, + entityTypes: [ + { + type: EntityType.Dataset, + urn: 'urn:li:dataset', + info: { + type: EntityType.Dataset, + }, + }, + ], + valueType: { + urn: 'urn:li:dataType:string', + type: EntityType.DataType, + info: { + type: StdDataType.String, + }, + }, + }, + }; + + const property: AssetProperty = { + name: 'test', + type: SummaryElementType.StructuredProperty, + structuredProperty, + }; + + const result = filterCurrentItemInReplaceMenu(menuItems, property); + if (isItemType(result[0])) { + expect(result[0].children?.[0]).toEqual({ + key: 'structuredProperties', + type: 'group', + title: 'Structured', + }); + } + }); + + it('should handle empty menuItems array', () => { + const menuItems: ItemType[] = []; + const property: AssetProperty = { + name: 'test', + type: SummaryElementType.Domain, + }; + + const result = filterCurrentItemInReplaceMenu(menuItems, property); + expect(result).toEqual([]); + }); +}); diff --git a/datahub-web-react/src/app/entityV2/summary/properties/property/properties/utils.ts b/datahub-web-react/src/app/entityV2/summary/properties/property/properties/utils.ts new file mode 100644 index 0000000000..8d8093d37e --- /dev/null +++ b/datahub-web-react/src/app/entityV2/summary/properties/property/properties/utils.ts @@ -0,0 +1,39 @@ +import { ItemType } from '@components/components/Menu/types'; + +import { AssetProperty } from '@app/entityV2/summary/properties/types'; + +import { SummaryElementType } from '@types'; + +export function filterCurrentItemInReplaceMenu(menuItems: ItemType[], property: AssetProperty) { + const excludeKey = + property.type === SummaryElementType.StructuredProperty ? property.structuredProperty?.urn : property.type; + + return menuItems.map((item) => { + if (item.key !== 'replace') { + return item; + } + + if (!('children' in item) || !item.children) { + return item; + } + + if (property.type === SummaryElementType.StructuredProperty) { + return { + ...item, + children: item.children.map((child) => + child.key === 'structuredProperties' && 'children' in child + ? { + ...child, + children: child.children?.filter((c) => c.key !== excludeKey), + } + : child, + ), + }; + } + + return { + ...item, + children: item.children.filter((child) => child.key !== excludeKey), + }; + }); +}