mirror of
https://github.com/datahub-project/datahub.git
synced 2025-08-15 12:46:53 +00:00
feat(ui) Allow a configurable default tab for domain entity profile page (#8316)
This commit is contained in:
parent
b97e9b5b53
commit
18c1f12436
@ -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();
|
||||||
|
@ -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
|
||||||
"""
|
"""
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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',
|
||||||
|
}
|
||||||
|
@ -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),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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 = {
|
||||||
|
@ -24,6 +24,9 @@ export const DEFAULT_APP_CONFIG = {
|
|||||||
queriesTab: {
|
queriesTab: {
|
||||||
queriesTabResultSize: 5,
|
queriesTabResultSize: 5,
|
||||||
},
|
},
|
||||||
|
entityProfile: {
|
||||||
|
domainDefaultTab: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
authConfig: {
|
authConfig: {
|
||||||
tokenAuthEnabled: false,
|
tokenAuthEnabled: false,
|
||||||
|
@ -40,6 +40,11 @@ query appConfig {
|
|||||||
queriesTab {
|
queriesTab {
|
||||||
queriesTabResultSize
|
queriesTabResultSize
|
||||||
}
|
}
|
||||||
|
entityProfiles {
|
||||||
|
domain {
|
||||||
|
defaultTab
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
telemetryConfig {
|
telemetryConfig {
|
||||||
enableThirdPartyLogging
|
enableThirdPartyLogging
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user