feat(ui): Add context paths for Data Products (#14802)

This commit is contained in:
Saketh Varma 2025-09-19 17:09:52 -04:00 committed by GitHub
parent 04b76b5664
commit 01b3ef27d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 95 additions and 40 deletions

View File

@ -3,6 +3,7 @@ import React from 'react';
import { GenericEntityProperties } from '@app/entity/shared/types';
import { EntityMenuActions, IconStyleType, PreviewType } from '@app/entityV2/Entity';
import { EntityMenuItems } from '@app/entityV2/shared/EntityDropdown/EntityMenuActions';
import { getParentEntities } from '@app/entityV2/shared/containers/profile/header/getParentEntities';
import DefaultPreviewCard from '@app/previewV2/DefaultPreviewCard';
import { useEntityRegistry } from '@app/useEntityRegistry';
@ -58,7 +59,7 @@ export const Preview = ({
tags={globalTags || undefined}
owners={owners}
domain={domain}
parentEntities={domain ? [domain] : []}
parentEntities={data ? getParentEntities(data, EntityType.DataProduct) : []}
glossaryTerms={glossaryTerms || undefined}
entityCount={entityCount}
externalUrl={externalUrl}

View File

@ -134,7 +134,7 @@ export const DefaultEntityHeader = ({
const displayedEntityType = getDisplayedEntityType(entityData, entityRegistry, entityType);
const { platform, platforms } = getEntityPlatforms(entityType, entityData);
const contextPath = getParentEntities(entityData);
const contextPath = getParentEntities(entityData, entityType);
return (
<>
<Row>

View File

@ -2,7 +2,7 @@ import { GenericEntityProperties } from '@app/entity/shared/types';
import { getParentEntities } from '@app/entityV2/shared/containers/profile/header/getParentEntities';
import { dataPlatform } from '@src/Mocks';
import { EntityType } from '@types';
import { DataProduct, EntityType } from '@types';
const PARENT_CONTAINERS: GenericEntityProperties['parentContainers'] = {
containers: [
@ -45,6 +45,20 @@ const PARENT: GenericEntityProperties = {
platform: dataPlatform,
};
const dataProduct: DataProduct = {
urn: 'urn:li:dataProduct:test',
type: EntityType.DataProduct,
domain: {
associatedUrn: '',
domain: {
urn: 'urn:li:domain:bebdad41-c523-469f-9b62-de94f938f603',
id: 'bebdad41-c523-469f-9b62-de94f938f603',
type: EntityType.Domain,
parentDomains: PARENT_DOMAINS,
},
},
};
describe('getContextPath', () => {
it('returns empty array by default', () => {
const entityData = {};
@ -100,4 +114,14 @@ describe('getContextPath', () => {
const contextPath = getParentEntities(entityData);
expect(contextPath).toEqual([PARENT]);
});
it('returns correct context path for data products', () => {
const entityData = dataProduct;
const contextPath = getParentEntities(entityData, EntityType.DataProduct);
expect(contextPath).toEqual([
dataProduct.domain?.domain,
...(dataProduct.domain?.domain?.parentDomains?.domains || []),
]);
});
});

View File

@ -1,18 +1,40 @@
import { GenericEntityProperties } from '@app/entity/shared/types';
import { Entity } from '@types';
import { DataProduct, Entity, EntityType } from '@types';
type GetContextPathInput = Pick<
GenericEntityProperties,
'parent' | 'parentContainers' | 'parentDomains' | 'parentNodes'
'parent' | 'parentContainers' | 'parentDomains' | 'parentNodes' | 'domain'
>;
export function getParentEntities(entityData: GetContextPathInput | null): Entity[] {
const containerPath =
entityData?.parentContainers?.containers ||
entityData?.parentDomains?.domains ||
entityData?.parentNodes?.nodes ||
[];
const parentPath: Entity[] = entityData?.parent ? [entityData.parent as Entity] : [];
return containerPath.length ? containerPath : parentPath;
export function getParentEntities(entityData: GetContextPathInput | null, entityType?: EntityType): Entity[] {
if (!entityData) return [];
switch (entityType) {
case EntityType.DataProduct: {
const domain = (entityData as DataProduct).domain?.domain;
return domain ? [domain, ...(domain.parentDomains?.domains || [])] : [];
}
case EntityType.GlossaryTerm:
case EntityType.GlossaryNode:
return entityData.parentNodes?.nodes || [];
case EntityType.Domain:
return entityData.parentDomains?.domains || [];
default: {
// generic fallback
const containerPath =
entityData.parentContainers?.containers ||
entityData.parentDomains?.domains ||
entityData.parentNodes?.nodes ||
[];
if (containerPath.length) return containerPath;
if (entityData.parent) return [entityData.parent as Entity];
return [];
}
}
}

View File

@ -6,12 +6,13 @@ import React, { useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import analytics, { EntityActionType, EventType } from '@app/analytics';
import { IconStyleType } from '@app/entityV2/Entity';
import { getParentEntities } from '@app/entityV2/shared/containers/profile/header/getParentEntities';
import { handleBatchError } from '@app/entityV2/shared/utils';
import { useModulesContext } from '@app/homeV3/module/context/ModulesContext';
import ContextPath from '@app/previewV2/ContextPath';
import { useEnterKeyListener } from '@app/shared/useEnterKeyListener';
import { useEntityRegistry } from '@app/useEntityRegistry';
import { Button } from '@src/alchemy-components';
import { Button, Text } from '@src/alchemy-components';
import { ANTD_GRAY } from '@src/app/entityV2/shared/constants';
import { ModalButtonContainer } from '@src/app/shared/button/styledComponents';
import { useGetRecommendations } from '@src/app/shared/recommendation';
@ -21,14 +22,6 @@ import { useBatchSetDataProductMutation } from '@graphql/dataProduct.generated';
import { useGetAutoCompleteMultipleResultsLazyQuery } from '@graphql/search.generated';
import { DataHubPageModuleType, DataProduct, Entity, EntityType } from '@types';
const OptionWrapper = styled.div`
padding: 2px 0;
svg {
margin-right: 8px;
}
`;
const LoadingWrapper = styled.div`
display: flex;
justify-content: center;
@ -179,15 +172,23 @@ export default function SetDataProductModal({
value: 'loading',
};
const options = displayedDataProducts.map((result) => ({
label: (
<OptionWrapper>
{entityRegistry.getIcon(EntityType.DataProduct, 12, IconStyleType.ACCENT, 'black')}
{entityRegistry.getDisplayName(EntityType.DataProduct, result)}
</OptionWrapper>
),
value: result.urn,
}));
const options = displayedDataProducts.map((result) => {
return {
label: (
<>
<Text size="md">{entityRegistry.getDisplayName(EntityType.DataProduct, result)}</Text>
<ContextPath
entityType={EntityType.DataProduct}
displayedEntityType="Data product"
parentEntities={getParentEntities(result as DataProduct, EntityType.DataProduct)}
entityTitleWidth={200}
numVisible={3}
/>
</>
),
value: result.urn,
};
});
return (
<Modal

View File

@ -7,6 +7,7 @@ import EntityTitleLoadingSection from '@app/entityV2/shared/containers/profile/h
import EntityName from '@app/entityV2/shared/containers/profile/header/EntityName';
import PlatformHeaderIcons from '@app/entityV2/shared/containers/profile/header/PlatformContent/PlatformHeaderIcons';
import StructuredPropertyBadge from '@app/entityV2/shared/containers/profile/header/StructuredPropertyBadge';
import { getParentEntities } from '@app/entityV2/shared/containers/profile/header/getParentEntities';
import { getDisplayedEntityType } from '@app/entityV2/shared/containers/profile/header/utils';
import VersioningBadge from '@app/entityV2/shared/versioning/VersioningBadge';
import ContextPath from '@app/previewV2/ContextPath';
@ -15,7 +16,7 @@ import NotesIcon from '@app/previewV2/NotesIcon';
import HorizontalScroller from '@app/sharedV2/carousel/HorizontalScroller';
import { useEntityRegistry } from '@app/useEntityRegistry';
import { DataPlatform, Entity, EntityType, Post } from '@types';
import { DataPlatform, EntityType, Post } from '@types';
const TitleContainer = styled(HorizontalScroller)`
display: flex;
@ -49,13 +50,7 @@ const SidebarEntityHeader = () => {
const platforms =
entityType === EntityType.SchemaField ? entityData?.parent?.siblingPlatforms : entityData?.siblingPlatforms;
const containerPath =
entityData?.parentContainers?.containers ||
entityData?.parentDomains?.domains ||
entityData?.parentNodes?.nodes ||
[];
const parentPath: Entity[] = entityData?.parent ? [entityData.parent as Entity] : [];
const parentEntities = containerPath.length ? containerPath : parentPath;
const parentEntities = getParentEntities(entityData, entityType);
if (loading) {
return <EntityTitleLoadingSection />;

View File

@ -228,6 +228,18 @@ fragment autoCompleteFields on Entity {
properties {
name
}
domain {
domain {
urn
type
properties {
name
}
parentDomains {
...parentDomainsFields
}
}
}
}
... on Application {
properties {