Skip to content

Commit

Permalink
fix(ui) Fix UI flickering when switching between glossary entities (d…
Browse files Browse the repository at this point in the history
  • Loading branch information
chriscollins3456 authored Mar 6, 2023
1 parent 036983c commit 4f78de6
Show file tree
Hide file tree
Showing 23 changed files with 292 additions and 87 deletions.
6 changes: 3 additions & 3 deletions datahub-web-react/src/app/SearchRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { SearchPage } from './search/SearchPage';
import { AnalyticsPage } from './analyticsDashboard/components/AnalyticsPage';
import { ManageDomainsPage } from './domain/ManageDomainsPage';
import { ManageIngestionPage } from './ingest/ManageIngestionPage';
import BusinessGlossaryPage from './glossary/BusinessGlossaryPage';
import GlossaryRoutes from './glossary/GlossaryRoutes';
import { SettingsPage } from './settings/SettingsPage';

/**
Expand All @@ -21,7 +21,7 @@ export const SearchRoutes = (): JSX.Element => {
return (
<SearchablePage>
<Switch>
{entityRegistry.getEntities().map((entity) => (
{entityRegistry.getNonGlossaryEntities().map((entity) => (
<Route
key={entity.getPathName()}
path={`/${entity.getPathName()}/:urn`}
Expand All @@ -41,7 +41,7 @@ export const SearchRoutes = (): JSX.Element => {
<Route path={PageRoutes.DOMAINS} render={() => <ManageDomainsPage />} />
<Route path={PageRoutes.INGESTION} render={() => <ManageIngestionPage />} />
<Route path={PageRoutes.SETTINGS} render={() => <SettingsPage />} />
<Route path={PageRoutes.GLOSSARY} render={() => <BusinessGlossaryPage />} />
<GlossaryRoutes />
<Route component={NoPageFound} />
</Switch>
</SearchablePage>
Expand Down
9 changes: 9 additions & 0 deletions datahub-web-react/src/app/entity/EntityRegistry.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Entity as EntityInterface, EntityType, SearchResult } from '../../types.generated';
import { FetchedEntity } from '../lineage/types';
import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from './Entity';
import { GLOSSARY_ENTITY_TYPES } from './shared/constants';
import { GenericEntityProperties } from './shared/types';
import { dictToQueryStringParams, urlEncodeUrn } from './shared/utils';

Expand Down Expand Up @@ -38,6 +39,14 @@ export default class EntityRegistry {
return this.entities;
}

getNonGlossaryEntities(): Array<Entity<any>> {
return this.entities.filter((entity) => !GLOSSARY_ENTITY_TYPES.includes(entity.type));
}

getGlossaryEntities(): Array<Entity<any>> {
return this.entities.filter((entity) => GLOSSARY_ENTITY_TYPES.includes(entity.type));
}

getSearchEntityTypes(): Array<EntityType> {
return this.entities.filter((entity) => entity.isSearchEnabled()).map((entity) => entity.type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { FolderFilled, FolderOutlined } from '@ant-design/icons';
import React from 'react';
import { useGetGlossaryNodeQuery } from '../../../graphql/glossaryNode.generated';
import { EntityType, GlossaryNode, SearchResult } from '../../../types.generated';
import GlossaryEntitiesPath from '../../glossary/GlossaryEntitiesPath';
import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '../Entity';
import { EntityProfile } from '../shared/containers/profile/EntityProfile';
import { SidebarOwnerSection } from '../shared/containers/profile/sidebar/Ownership/SidebarOwnerSection';
Expand Down Expand Up @@ -56,8 +55,8 @@ class GlossaryNodeEntity implements Entity<GlossaryNode> {
entityType={EntityType.GlossaryNode}
useEntityQuery={useGetGlossaryNodeQuery}
getOverrideProperties={this.getOverridePropertiesFromEntity}
displayGlossaryBrowser
isNameEditable
hideBrowseBar
tabs={[
{
name: 'Contents',
Expand All @@ -82,7 +81,6 @@ class GlossaryNodeEntity implements Entity<GlossaryNode> {
component: SidebarOwnerSection,
},
]}
customNavBar={<GlossaryEntitiesPath />}
headerDropdownItems={
new Set([
EntityMenuItems.ADD_TERM_GROUP,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { SidebarOwnerSection } from '../shared/containers/profile/sidebar/Owners
import { PropertiesTab } from '../shared/tabs/Properties/PropertiesTab';
import { DocumentationTab } from '../shared/tabs/Documentation/DocumentationTab';
import { SidebarAboutSection } from '../shared/containers/profile/sidebar/AboutSection/SidebarAboutSection';
import GlossaryEntitiesPath from '../../glossary/GlossaryEntitiesPath';
import { EntityMenuItems } from '../shared/EntityDropdown/EntityDropdown';
import { EntityActionItem } from '../shared/entity/EntityActions';
import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domain/SidebarDomainSection';
Expand Down Expand Up @@ -68,8 +67,8 @@ export class GlossaryTermEntity implements Entity<GlossaryTerm> {
headerDropdownItems={
new Set([EntityMenuItems.UPDATE_DEPRECATION, EntityMenuItems.MOVE, EntityMenuItems.DELETE])
}
displayGlossaryBrowser
isNameEditable
hideBrowseBar
tabs={[
{
name: 'Documentation',
Expand Down Expand Up @@ -116,7 +115,6 @@ export class GlossaryTermEntity implements Entity<GlossaryTerm> {
},
]}
getOverrideProperties={this.getOverridePropertiesFromEntity}
customNavBar={<GlossaryEntitiesPath />}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { useEntityData, useRefetch } from '../EntityContext';
import analytics, { EventType } from '../../../analytics';
import DescriptionModal from '../components/legacy/DescriptionModal';
import { validateCustomUrnId } from '../../../shared/textUtil';
import { useGlossaryEntityData } from '../GlossaryEntityContext';
import { getGlossaryRootToUpdate, updateGlossarySidebar } from '../../../glossary/utils';

const StyledItem = styled(Form.Item)`
margin-bottom: 0;
Expand All @@ -36,6 +38,7 @@ interface Props {
function CreateGlossaryEntityModal(props: Props) {
const { entityType, onClose, refetchData } = props;
const entityData = useEntityData();
const { isInGlossaryContext, urnsToUpdate, setUrnsToUpdate } = useGlossaryEntityData();
const [form] = Form.useForm();
const entityRegistry = useEntityRegistry();
const [stagedId, setStagedId] = useState<string | undefined>(undefined);
Expand Down Expand Up @@ -77,6 +80,11 @@ function CreateGlossaryEntityModal(props: Props) {
duration: 2,
});
refetch();
if (isInGlossaryContext) {
// either refresh this current glossary node or the root nodes or root terms
const nodeToUpdate = entityData?.urn || getGlossaryRootToUpdate(entityType);
updateGlossarySidebar([nodeToUpdate], urnsToUpdate, setUrnsToUpdate);
}
if (refetchData) {
refetchData();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ interface Props {
refetchForEntity?: () => void;
refetchForTerms?: () => void;
refetchForNodes?: () => void;
refreshBrowser?: () => void;
onDeleteEntity?: () => void;
}

Expand All @@ -84,7 +83,6 @@ function EntityDropdown(props: Props) {
refetchForEntity,
refetchForTerms,
refetchForNodes,
refreshBrowser,
onDeleteEntity: onDelete,
size,
options,
Expand Down Expand Up @@ -252,9 +250,7 @@ function EntityDropdown(props: Props) {
refetch={refetchForEntity}
/>
)}
{isMoveModalVisible && (
<MoveGlossaryEntityModal onClose={() => setIsMoveModalVisible(false)} refetchData={refreshBrowser} />
)}
{isMoveModalVisible && <MoveGlossaryEntityModal onClose={() => setIsMoveModalVisible(false)} />}
{hasBeenDeleted && !onDelete && deleteRedirectPath && <Redirect to={deleteRedirectPath} />}
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { useEntityData, useRefetch } from '../EntityContext';
import { useEntityRegistry } from '../../../useEntityRegistry';
import { useUpdateParentNodeMutation } from '../../../../graphql/glossary.generated';
import NodeParentSelect from './NodeParentSelect';
import { useGlossaryEntityData } from '../GlossaryEntityContext';
import { getGlossaryRootToUpdate, getParentNodeToUpdate, updateGlossarySidebar } from '../../../glossary/utils';

const StyledItem = styled(Form.Item)`
margin-bottom: 0;
Expand All @@ -16,12 +18,12 @@ const OptionalWrapper = styled.span`

interface Props {
onClose: () => void;
refetchData?: () => void;
}

function MoveGlossaryEntityModal(props: Props) {
const { onClose, refetchData } = props;
const { urn: entityDataUrn, entityType } = useEntityData();
const { onClose } = props;
const { urn: entityDataUrn, entityData, entityType } = useEntityData();
const { isInGlossaryContext, urnsToUpdate, setUrnsToUpdate } = useGlossaryEntityData();
const [form] = Form.useForm();
const entityRegistry = useEntityRegistry();
const [selectedParentUrn, setSelectedParentUrn] = useState('');
Expand All @@ -46,8 +48,10 @@ function MoveGlossaryEntityModal(props: Props) {
duration: 2,
});
refetch();
if (refetchData) {
refetchData();
if (isInGlossaryContext) {
const oldParentToUpdate = getParentNodeToUpdate(entityData, entityType);
const newParentToUpdate = selectedParentUrn || getGlossaryRootToUpdate(entityType);
updateGlossarySidebar([oldParentToUpdate, newParentToUpdate], urnsToUpdate, setUrnsToUpdate);
}
}, 2000);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { EntityType } from '../../../../types.generated';
import { useEntityRegistry } from '../../../useEntityRegistry';
import { getDeleteEntityMutation } from '../../../shared/deleteUtils';
import analytics, { EventType } from '../../../analytics';
import { useGlossaryEntityData } from '../GlossaryEntityContext';
import { getParentNodeToUpdate, updateGlossarySidebar } from '../../../glossary/utils';

/**
* Performs the flow for deleting an entity of a given type.
Expand All @@ -22,6 +24,7 @@ function useDeleteEntity(
) {
const [hasBeenDeleted, setHasBeenDeleted] = useState(false);
const entityRegistry = useEntityRegistry();
const { isInGlossaryContext, urnsToUpdate, setUrnsToUpdate } = useGlossaryEntityData();

const maybeDeleteEntity = getDeleteEntityMutation(type)();
const deleteEntity = (maybeDeleteEntity && maybeDeleteEntity[0]) || undefined;
Expand All @@ -48,6 +51,10 @@ function useDeleteEntity(
() => {
setHasBeenDeleted(true);
onDelete?.();
if (isInGlossaryContext) {
const parentNodeToUpdate = getParentNodeToUpdate(entityData, type);
updateGlossarySidebar([parentNodeToUpdate], urnsToUpdate, setUrnsToUpdate);
}
if (!hideMessage) {
message.success({
content: `Deleted ${entityRegistry.getEntityName(type)}!`,
Expand Down
27 changes: 27 additions & 0 deletions datahub-web-react/src/app/entity/shared/GlossaryEntityContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { useContext } from 'react';
import { GenericEntityProperties } from './types';

export interface GlossaryEntityContextType {
isInGlossaryContext: boolean;
entityData: GenericEntityProperties | null;
setEntityData: (entityData: GenericEntityProperties | null) => void;
// Since we have glossary data in the profile and in the sidebar browser, we need to communicate to the
// sidebar when to refetch for a given node or at the root level (if we're editing a term or node without a parent).
// This will happen when you edit a name, move a term/group, create a new term/group, and delete a term/group
urnsToUpdate: string[];
setUrnsToUpdate: (updatdUrns: string[]) => void;
}

export const GlossaryEntityContext = React.createContext<GlossaryEntityContextType>({
isInGlossaryContext: false,
entityData: null,
setEntityData: () => {},
urnsToUpdate: [],
setUrnsToUpdate: () => {},
});

export const useGlossaryEntityData = () => {
const { isInGlossaryContext, entityData, setEntityData, urnsToUpdate, setUrnsToUpdate } =
useContext(GlossaryEntityContext);
return { isInGlossaryContext, entityData, setEntityData, urnsToUpdate, setUrnsToUpdate };
};
2 changes: 2 additions & 0 deletions datahub-web-react/src/app/entity/shared/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ export const ENTITY_TYPES_WITH_MANUAL_LINEAGE = new Set([
EntityType.DataJob,
]);

export const GLOSSARY_ENTITY_TYPES = [EntityType.GlossaryTerm, EntityType.GlossaryNode];

export const DEFAULT_SYSTEM_ACTOR_URNS = ['urn:li:corpuser:__datahub_system', 'urn:li:corpuser:unknown'];

export const VIEW_ENTITY_PAGE = 'VIEW_ENTITY_PAGE';
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import styled from 'styled-components/macro';
import { useHistory } from 'react-router';
import { EntityType, Exact } from '../../../../../types.generated';
import { Message } from '../../../../shared/Message';
import { getEntityPath, getOnboardingStepIdsForEntityType, useRoutedTab } from './utils';
import {
getEntityPath,
getOnboardingStepIdsForEntityType,
useRoutedTab,
useUpdateGlossaryEntityDataOnChange,
} from './utils';
import {
EntitySidebarSection,
EntitySubHeaderSection,
Expand All @@ -27,9 +32,6 @@ import DynamicTab from '../../tabs/Entity/weaklyTypedAspects/DynamicTab';
import analytics, { EventType } from '../../../../analytics';
import { ProfileSidebarResizer } from './sidebar/ProfileSidebarResizer';
import { EntityMenuItems } from '../../EntityDropdown/EntityDropdown';
import GlossaryBrowser from '../../../../glossary/GlossaryBrowser/GlossaryBrowser';
import GlossarySearch from '../../../../glossary/GlossarySearch';
import { BrowserWrapper, MAX_BROWSER_WIDTH, MIN_BROWSWER_WIDTH } from '../../../../glossary/BusinessGlossaryPage';
import { useIsSeparateSiblingsMode } from '../../siblingUtils';
import { EntityActionItem } from '../../entity/EntityActions';
import { ErrorSection } from '../../../../shared/error/ErrorSection';
Expand Down Expand Up @@ -64,11 +66,10 @@ type Props<T, U> = {
getOverrideProperties: (T) => GenericEntityProperties;
tabs: EntityTab[];
sidebarSections: EntitySidebarSection[];
customNavBar?: React.ReactNode;
subHeader?: EntitySubHeaderSection;
headerDropdownItems?: Set<EntityMenuItems>;
headerActionItems?: Set<EntityActionItem>;
displayGlossaryBrowser?: boolean;
hideBrowseBar?: boolean;
isNameEditable?: boolean;
};

Expand Down Expand Up @@ -150,11 +151,10 @@ export const EntityProfile = <T, U>({
getOverrideProperties,
tabs,
sidebarSections,
customNavBar,
headerDropdownItems,
headerActionItems,
displayGlossaryBrowser,
isNameEditable,
hideBrowseBar,
subHeader,
}: Props<T, U>): JSX.Element => {
const isLineageMode = useIsLineageMode();
Expand All @@ -169,17 +169,10 @@ export const EntityProfile = <T, U>({
}));

const [sidebarWidth, setSidebarWidth] = useState(window.innerWidth * 0.25);
const [browserWidth, setBrowserWith] = useState(window.innerWidth * 0.2);
const [shouldUpdateBrowser, setShouldUpdateBrowser] = useState(false);
const entityStepIds: string[] = getOnboardingStepIdsForEntityType(entityType);
const lineageGraphStepIds: string[] = [LINEAGE_GRAPH_INTRO_ID, LINEAGE_GRAPH_TIME_FILTER_ID];
const stepIds = isLineageMode ? lineageGraphStepIds : entityStepIds;

function refreshBrowser() {
setShouldUpdateBrowser(true);
setTimeout(() => setShouldUpdateBrowser(false), 0);
}

const routeToTab = useCallback(
({
tabName,
Expand All @@ -206,6 +199,8 @@ export const EntityProfile = <T, U>({
const { entityData, dataPossiblyCombinedWithSiblings, dataNotCombinedWithSiblings, loading, error, refetch } =
useGetDataForProfile({ urn, entityType, useEntityQuery, getOverrideProperties });

useUpdateGlossaryEntityDataOnChange(entityData, entityType);

const maybeUpdateEntity = useUpdateQuery?.({
onCompleted: () => refetch(),
});
Expand Down Expand Up @@ -283,7 +278,7 @@ export const EntityProfile = <T, U>({

const isBrowsable = entityRegistry.getBrowseEntityTypes().includes(entityType);
const isLineageEnabled = entityRegistry.getLineageEntityTypes().includes(entityType);
const showBrowseBar = isBrowsable || isLineageEnabled;
const showBrowseBar = (isBrowsable || isLineageEnabled) && !hideBrowseBar;

return (
<EntityContext.Provider
Expand All @@ -303,8 +298,7 @@ export const EntityProfile = <T, U>({
<>
<OnboardingTour stepIds={stepIds} />
<EntityHead />
{customNavBar}
{showBrowseBar && !customNavBar && <EntityProfileNavBar urn={urn} entityType={entityType} />}
{showBrowseBar && <EntityProfileNavBar urn={urn} entityType={entityType} />}
{entityData?.status?.removed === true && (
<Alert
message="This entity is not discoverable via search or lineage graph. Contact your DataHub admin for more information."
Expand All @@ -318,23 +312,6 @@ export const EntityProfile = <T, U>({
<LineageExplorer type={entityType} urn={urn} />
) : (
<>
{displayGlossaryBrowser && (
<>
<BrowserWrapper width={browserWidth}>
<GlossarySearch />
<GlossaryBrowser openToEntity refreshBrowser={shouldUpdateBrowser} />
</BrowserWrapper>
<ProfileSidebarResizer
setSidePanelWidth={(width) =>
setBrowserWith(
Math.min(Math.max(width, MIN_BROWSWER_WIDTH), MAX_BROWSER_WIDTH),
)
}
initialSize={browserWidth}
isSidebarOnLeft
/>
</>
)}
<HeaderAndTabs>
<HeaderAndTabsFlex>
<Header>
Expand All @@ -343,7 +320,6 @@ export const EntityProfile = <T, U>({
headerActionItems={headerActionItems}
isNameEditable={isNameEditable}
subHeader={subHeader}
refreshBrowser={refreshBrowser}
/>
<EntityTabs tabs={visibleTabs} selectedTab={routedTab} />
</Header>
Expand Down
Loading

0 comments on commit 4f78de6

Please sign in to comment.