feat(ui) Allow a configurable default tab for domain entity profile page (#8316)

This commit is contained in:
Chris Collins 2023-07-11 10:30:47 -04:00 committed by GitHub
parent b97e9b5b53
commit 18c1f12436
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 103 additions and 2 deletions

View File

@ -7,6 +7,8 @@ import com.linkedin.datahub.graphql.featureflags.FeatureFlags;
import com.linkedin.datahub.graphql.generated.AnalyticsConfig; import com.linkedin.datahub.graphql.generated.AnalyticsConfig;
import com.linkedin.datahub.graphql.generated.AppConfig; import com.linkedin.datahub.graphql.generated.AppConfig;
import com.linkedin.datahub.graphql.generated.AuthConfig; import com.linkedin.datahub.graphql.generated.AuthConfig;
import com.linkedin.datahub.graphql.generated.EntityProfileConfig;
import com.linkedin.datahub.graphql.generated.EntityProfilesConfig;
import com.linkedin.datahub.graphql.generated.EntityType; import com.linkedin.datahub.graphql.generated.EntityType;
import com.linkedin.datahub.graphql.generated.FeatureFlagsConfig; import com.linkedin.datahub.graphql.generated.FeatureFlagsConfig;
import com.linkedin.datahub.graphql.generated.IdentityManagementConfig; import com.linkedin.datahub.graphql.generated.IdentityManagementConfig;
@ -133,6 +135,15 @@ public class AppConfigResolver implements DataFetcher<CompletableFuture<AppConfi
queriesTabConfig.setQueriesTabResultSize(_visualConfiguration.getQueriesTab().getQueriesTabResultSize()); queriesTabConfig.setQueriesTabResultSize(_visualConfiguration.getQueriesTab().getQueriesTabResultSize());
visualConfig.setQueriesTab(queriesTabConfig); visualConfig.setQueriesTab(queriesTabConfig);
} }
if (_visualConfiguration != null && _visualConfiguration.getEntityProfile() != null) {
EntityProfilesConfig entityProfilesConfig = new EntityProfilesConfig();
if (_visualConfiguration.getEntityProfile().getDomainDefaultTab() != null) {
EntityProfileConfig profileConfig = new EntityProfileConfig();
profileConfig.setDefaultTab(_visualConfiguration.getEntityProfile().getDomainDefaultTab());
entityProfilesConfig.setDomain(profileConfig);
}
visualConfig.setEntityProfiles(entityProfilesConfig);
}
appConfig.setVisualConfig(visualConfig); appConfig.setVisualConfig(visualConfig);
final TelemetryConfig telemetryConfig = new TelemetryConfig(); final TelemetryConfig telemetryConfig = new TelemetryConfig();

View File

@ -211,6 +211,11 @@ type VisualConfig {
Configuration for the queries tab Configuration for the queries tab
""" """
queriesTab: QueriesTabConfig queriesTab: QueriesTabConfig
"""
Configuration for the queries tab
"""
entityProfiles: EntityProfilesConfig
} }
""" """
@ -223,6 +228,28 @@ type QueriesTabConfig {
queriesTabResultSize: Int queriesTabResultSize: Int
} }
"""
Configuration for different entity profiles
"""
type EntityProfilesConfig {
"""
The configurations for a Domain entity profile
"""
domain: EntityProfileConfig
}
"""
Configuration for an entity profile
"""
type EntityProfileConfig {
"""
The enum value from EntityProfileTab for which tab should be showed by default on
entity profile pages. If null, rely on default sorting from React code.
"""
defaultTab: String
}
""" """
Configurations related to tracking users in the app Configurations related to tracking users in the app
""" """

View File

@ -13,6 +13,7 @@ import { DomainEntitiesTab } from './DomainEntitiesTab';
import { EntityMenuItems } from '../shared/EntityDropdown/EntityDropdown'; import { EntityMenuItems } from '../shared/EntityDropdown/EntityDropdown';
import { EntityActionItem } from '../shared/entity/EntityActions'; import { EntityActionItem } from '../shared/entity/EntityActions';
import DataProductsTab from './DataProductsTab/DataProductsTab'; import DataProductsTab from './DataProductsTab/DataProductsTab';
import { EntityProfileTab } from '../shared/constants';
// import { EntityActionItem } from '../shared/entity/EntityActions'; // import { EntityActionItem } from '../shared/entity/EntityActions';
/** /**
@ -72,14 +73,17 @@ export class DomainEntity implements Entity<Domain> {
isNameEditable isNameEditable
tabs={[ tabs={[
{ {
id: EntityProfileTab.DOMAIN_ENTITIES_TAB,
name: 'Entities', name: 'Entities',
component: DomainEntitiesTab, component: DomainEntitiesTab,
}, },
{ {
id: EntityProfileTab.DOCUMENTATION_TAB,
name: 'Documentation', name: 'Documentation',
component: DocumentationTab, component: DocumentationTab,
}, },
{ {
id: EntityProfileTab.DATA_PRODUCTS_TAB,
name: 'Data Products', name: 'Data Products',
component: DataProductsTab, component: DataProductsTab,
}, },

View File

@ -93,3 +93,10 @@ export const GLOSSARY_ENTITY_TYPES = [EntityType.GlossaryTerm, EntityType.Glossa
export const DEFAULT_SYSTEM_ACTOR_URNS = ['urn:li:corpuser:__datahub_system', 'urn:li:corpuser:unknown']; export const DEFAULT_SYSTEM_ACTOR_URNS = ['urn:li:corpuser:__datahub_system', 'urn:li:corpuser:unknown'];
export const VIEW_ENTITY_PAGE = 'VIEW_ENTITY_PAGE'; export const VIEW_ENTITY_PAGE = 'VIEW_ENTITY_PAGE';
// only values for Domain Entity for custom configurable default tab
export enum EntityProfileTab {
DOMAIN_ENTITIES_TAB = 'DOMAIN_ENTITIES_TAB',
DOCUMENTATION_TAB = 'DOCUMENTATION_TAB',
DATA_PRODUCTS_TAB = 'DATA_PRODUCTS_TAB',
}

View File

@ -8,6 +8,7 @@ import { Message } from '../../../../shared/Message';
import { import {
getEntityPath, getEntityPath,
getOnboardingStepIdsForEntityType, getOnboardingStepIdsForEntityType,
sortEntityProfileTabs,
useRoutedTab, useRoutedTab,
useUpdateGlossaryEntityDataOnChange, useUpdateGlossaryEntityDataOnChange,
} from './utils'; } from './utils';
@ -43,6 +44,7 @@ import {
LINEAGE_GRAPH_INTRO_ID, LINEAGE_GRAPH_INTRO_ID,
LINEAGE_GRAPH_TIME_FILTER_ID, LINEAGE_GRAPH_TIME_FILTER_ID,
} from '../../../../onboarding/config/LineageGraphOnboardingConfig'; } from '../../../../onboarding/config/LineageGraphOnboardingConfig';
import { useAppConfig } from '../../../../useAppConfig';
type Props<T, U> = { type Props<T, U> = {
urn: string; urn: string;
@ -168,8 +170,10 @@ export const EntityProfile = <T, U>({
const isHideSiblingMode = useIsSeparateSiblingsMode(); const isHideSiblingMode = useIsSeparateSiblingsMode();
const entityRegistry = useEntityRegistry(); const entityRegistry = useEntityRegistry();
const history = useHistory(); const history = useHistory();
const appConfig = useAppConfig();
const isCompact = React.useContext(CompactContext); const isCompact = React.useContext(CompactContext);
const tabsWithDefaults = tabs.map((tab) => ({ ...tab, display: { ...defaultTabDisplayConfig, ...tab.display } })); const tabsWithDefaults = tabs.map((tab) => ({ ...tab, display: { ...defaultTabDisplayConfig, ...tab.display } }));
const sortedTabs = sortEntityProfileTabs(appConfig.config, entityType, tabsWithDefaults);
const sideBarSectionsWithDefaults = sidebarSections.map((sidebarSection) => ({ const sideBarSectionsWithDefaults = sidebarSections.map((sidebarSection) => ({
...sidebarSection, ...sidebarSection,
display: { ...defaultSidebarSection, ...sidebarSection.display }, display: { ...defaultSidebarSection, ...sidebarSection.display },
@ -235,7 +239,7 @@ export const EntityProfile = <T, U>({
}, },
})) || []; })) || [];
const visibleTabs = [...tabsWithDefaults, ...autoRenderTabs].filter((tab) => const visibleTabs = [...sortedTabs, ...autoRenderTabs].filter((tab) =>
tab.display?.visible(entityData, dataPossiblyCombinedWithSiblings), tab.display?.visible(entityData, dataPossiblyCombinedWithSiblings),
); );

View File

@ -2,7 +2,7 @@ import { useEffect } from 'react';
import { useLocation } from 'react-router'; import { useLocation } from 'react-router';
import queryString from 'query-string'; import queryString from 'query-string';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { EntityType } from '../../../../../types.generated'; import { AppConfig, EntityType } from '../../../../../types.generated';
import useIsLineageMode from '../../../../lineage/utils/useIsLineageMode'; import useIsLineageMode from '../../../../lineage/utils/useIsLineageMode';
import { useEntityRegistry } from '../../../../useEntityRegistry'; import { useEntityRegistry } from '../../../../useEntityRegistry';
import EntityRegistry from '../../../EntityRegistry'; import EntityRegistry from '../../../EntityRegistry';
@ -202,3 +202,22 @@ export function getOnboardingStepIdsForEntityType(entityType: EntityType): strin
return []; return [];
} }
} }
function sortTabsWithDefaultTabId(tabs: EntityTab[], defaultTabId: string) {
return tabs.sort((tabA, tabB) => {
if (tabA.id === defaultTabId) return -1;
if (tabB.id === defaultTabId) return 1;
return 0;
});
}
export function sortEntityProfileTabs(appConfig: AppConfig, entityType: EntityType, tabs: EntityTab[]) {
const sortedTabs = [...tabs];
if (entityType === EntityType.Domain && appConfig.visualConfig.entityProfiles?.domain?.defaultTab) {
const defaultTabId = appConfig.visualConfig.entityProfiles?.domain.defaultTab;
sortTabsWithDefaultTabId(sortedTabs, defaultTabId);
}
return sortedTabs;
}

View File

@ -48,6 +48,7 @@ export type EntityTab = {
enabled: (GenericEntityProperties, T) => boolean; // Whether the tab is enabled on the UI. Defaults to true. enabled: (GenericEntityProperties, T) => boolean; // Whether the tab is enabled on the UI. Defaults to true.
}; };
properties?: any; properties?: any;
id?: string;
}; };
export type EntitySidebarSection = { export type EntitySidebarSection = {

View File

@ -24,6 +24,9 @@ export const DEFAULT_APP_CONFIG = {
queriesTab: { queriesTab: {
queriesTabResultSize: 5, queriesTabResultSize: 5,
}, },
entityProfile: {
domainDefaultTab: null,
},
}, },
authConfig: { authConfig: {
tokenAuthEnabled: false, tokenAuthEnabled: false,

View File

@ -40,6 +40,11 @@ query appConfig {
queriesTab { queriesTab {
queriesTabResultSize queriesTabResultSize
} }
entityProfiles {
domain {
defaultTab
}
}
} }
telemetryConfig { telemetryConfig {
enableThirdPartyLogging enableThirdPartyLogging

View File

@ -0,0 +1,12 @@
package com.linkedin.metadata.config;
import lombok.Data;
@Data
public class EntityProfileConfig {
/**
* The default tab to show first on a Domain entity profile. Defaults to React code sorting if not present.
*/
public String domainDefaultTab;
}

View File

@ -17,4 +17,9 @@ public class VisualConfiguration {
* Queries tab related configurations * Queries tab related configurations
*/ */
public QueriesTabConfig queriesTab; public QueriesTabConfig queriesTab;
/**
* Queries tab related configurations
*/
public EntityProfileConfig entityProfile;
} }

View File

@ -108,6 +108,9 @@ visualConfig:
assets: assets:
logoUrl: ${REACT_APP_LOGO_URL:/assets/platforms/datahublogo.png} logoUrl: ${REACT_APP_LOGO_URL:/assets/platforms/datahublogo.png}
faviconUrl: ${REACT_APP_FAVICON_URL:/assets/favicon.ico} faviconUrl: ${REACT_APP_FAVICON_URL:/assets/favicon.ico}
entityProfile:
# we only support default tab for domains right now. In order to implement for other entities, update React code
domainDefaultTab: ${DOMAIN_DEFAULT_TAB:} # set to DOCUMENTATION_TAB to show documentation tab first
# Storage Layer # Storage Layer