fix(web): glossary term create buttons inlined in content area (#13571)

This commit is contained in:
Jay 2025-05-21 00:32:10 -04:00 committed by GitHub
parent d8099da973
commit f4a8d9e7fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 118 additions and 76 deletions

View File

@ -1,14 +1,16 @@
import React from 'react';
import { Plus } from 'phosphor-react';
import React, { useState } from 'react';
import styled from 'styled-components';
import { useEntityData } from '@app/entity/shared/EntityContext';
import CreateGlossaryEntityModal from '@app/entity/shared/EntityDropdown/CreateGlossaryEntityModal';
import useGlossaryChildren from '@app/entityV2/glossaryNode/useGlossaryChildren';
import { sortGlossaryNodes } from '@app/entityV2/glossaryNode/utils';
import { sortGlossaryTerms } from '@app/entityV2/glossaryTerm/utils';
import EmptyGlossarySection from '@app/glossaryV2/EmptyGlossarySection';
import GlossaryEntitiesList from '@app/glossaryV2/GlossaryEntitiesList';
import { useEntityRegistry } from '@app/useEntityRegistry';
import { SearchBar } from '@src/alchemy-components';
import { Button, SearchBar, Tooltip } from '@src/alchemy-components';
import Loading from '@src/app/shared/Loading';
import { EntityType, GlossaryNode, GlossaryTerm } from '@types';
@ -26,11 +28,34 @@ const LoadingWrapper = styled.div`
justify-content: center;
`;
const StyledPlusOutlined = styled(Plus)`
font-size: 12px;
`;
const CreateButtonWrapper = styled.div`
display: flex;
gap: 8px;
margin-left: 16px;
flex-direction: row;
`;
const HeaderWrapper = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin: 16px;
`;
function ChildrenTab() {
const { entityData } = useEntityData();
const entityRegistry = useEntityRegistry();
const entityUrn = entityData?.urn;
const { scrollRef, data, loading, searchQuery, setSearchQuery } = useGlossaryChildren({ entityUrn });
const { scrollRef, data, loading, searchQuery, setSearchQuery, refetch } = useGlossaryChildren({ entityUrn });
const [isCreateNodeModalVisible, setIsCreateNodeModalVisible] = useState(false);
const [isCreateTermModalVisible, setIsCreateTermModalVisible] = useState(false);
if (!entityData) return <></>;
@ -43,32 +68,75 @@ function ChildrenTab() {
const hasTermsOrNodes = !!childNodes?.length || !!childTerms?.length;
if (searchQuery || hasTermsOrNodes) {
return (
<ChildrenTabWrapper>
<SearchBar
placeholder="Search contents..."
onChange={setSearchQuery}
value={searchQuery}
allowClear
width="300px"
style={{ marginLeft: 16, marginTop: 6 }}
return (
<>
{searchQuery || hasTermsOrNodes ? (
<ChildrenTabWrapper>
<HeaderWrapper>
<SearchBar
placeholder="Search..."
onChange={setSearchQuery}
value={searchQuery}
allowClear
width={hasTermsOrNodes ? 'auto' : '300px'}
/>
{hasTermsOrNodes && (
<CreateButtonWrapper>
<Tooltip title="Create New Glossary Term" showArrow={false} placement="bottom">
<Button
data-testid="add-term-button"
onClick={() => setIsCreateTermModalVisible(true)}
>
<StyledPlusOutlined /> Add Term
</Button>
</Tooltip>
<Tooltip title="Create New Term Group" showArrow={false} placement="bottom">
<Button
data-testid="add-term-group-button-v2"
variant="outline"
onClick={() => setIsCreateNodeModalVisible(true)}
>
<StyledPlusOutlined /> Add Term Group
</Button>
</Tooltip>
</CreateButtonWrapper>
)}
</HeaderWrapper>
<GlossaryEntitiesList
nodes={(childNodes as GlossaryNode[]) || []}
terms={(childTerms as GlossaryTerm[]) || []}
/>
{loading && (
<LoadingWrapper>
<Loading marginTop={0} height={24} />
</LoadingWrapper>
)}
<div ref={scrollRef} />
</ChildrenTabWrapper>
) : (
<EmptyGlossarySection
description="No Terms or Term Groups"
onAddTerm={() => setIsCreateTermModalVisible(true)}
onAddtermGroup={() => setIsCreateNodeModalVisible(true)}
/>
<GlossaryEntitiesList
nodes={(childNodes as GlossaryNode[]) || []}
terms={(childTerms as GlossaryTerm[]) || []}
/>
{loading && (
<LoadingWrapper>
<Loading marginTop={0} height={24} />
</LoadingWrapper>
)}
<div ref={scrollRef} />
</ChildrenTabWrapper>
);
}
)}
return <EmptyGlossarySection description="No Terms or Term Groups" />;
{isCreateTermModalVisible && (
<CreateGlossaryEntityModal
entityType={EntityType.GlossaryTerm}
onClose={() => setIsCreateTermModalVisible(false)}
refetchData={refetch}
/>
)}
{isCreateNodeModalVisible && (
<CreateGlossaryEntityModal
entityType={EntityType.GlossaryNode}
onClose={() => setIsCreateNodeModalVisible(false)}
refetchData={refetch}
/>
)}
</>
);
}
export default ChildrenTab;

View File

@ -12,7 +12,6 @@ import { SidebarAboutSection } from '@app/entityV2/shared/containers/profile/sid
import { SidebarOwnerSection } from '@app/entityV2/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection';
import StatusSection from '@app/entityV2/shared/containers/profile/sidebar/shared/StatusSection';
import { getDataForEntityType } from '@app/entityV2/shared/containers/profile/utils';
import { EntityActionItem } from '@app/entityV2/shared/entity/EntityActions';
import SidebarNotesSection from '@app/entityV2/shared/sidebarSection/SidebarNotesSection';
import SidebarStructuredProperties from '@app/entityV2/shared/sidebarSection/SidebarStructuredProperties';
import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab';
@ -109,9 +108,12 @@ class GlossaryNodeEntity implements Entity<GlossaryNode> {
},
]}
sidebarSections={this.getSidebarSections()}
headerActionItems={
new Set([EntityActionItem.ADD_CHILD_GLOSSARY_NODE, EntityActionItem.ADD_CHILD_GLOSSARY_TERM])
}
// NOTE: Hiding this for now as we've moved the actions to the content of ChildrenTab.tsx
// The buttons are too big and causes other actions to overflow.
// This component requires deeper refactoring to dynamically adapt to smaller screens.
// headerActionItems={
// new Set([EntityActionItem.ADD_CHILD_GLOSSARY_NODE, EntityActionItem.ADD_CHILD_GLOSSARY_TERM])
// }
headerDropdownItems={headerDropdownItems}
sidebarTabs={this.getSidebarTabs()}
/>

View File

@ -44,7 +44,11 @@ export default function useGlossaryChildren({ entityUrn, skip }: Props) {
const [searchData, setSearchData] = useState<Entity[]>([]);
const [dataUrnsSet, setDataUrnsSet] = useState<Set<string>>(new Set());
const [data, setData] = useState<Entity[]>([]);
const { data: scrollData, loading } = useScrollAcrossEntitiesQuery({
const {
data: scrollData,
loading,
refetch,
} = useScrollAcrossEntitiesQuery({
variables: {
...getGlossaryChildrenScrollInput(entityUrn || '', scrollId),
},
@ -140,5 +144,6 @@ export default function useGlossaryChildren({ entityUrn, skip }: Props) {
loading: loading || (shouldDoAutoComplete && autoCompleteLoading),
searchQuery,
setSearchQuery,
refetch,
};
}

View File

@ -99,13 +99,12 @@ const BusinessGlossaryPage = () => {
)}
<GlossaryContentProvider
setIsCreateNodeModalVisible={setIsCreateNodeModalVisible}
setIsCreateTermModalVisible={setIsCreateTermModalVisible}
hasTermsOrNodes={hasTermsOrNodes}
nodes={nodes || []}
terms={terms || []}
termsLoading={termsLoading}
nodesLoading={nodesLoading}
refetchForNodes={refetchForNodes}
refetchForTerms={refetchForTerms}
/>
</GlossaryWrapper>
</MainWrapper>

View File

@ -1,14 +1,8 @@
import { PlusOutlined } from '@ant-design/icons';
import { Button, Empty, Typography } from 'antd';
import React, { useState } from 'react';
import React from 'react';
import styled from 'styled-components/macro';
import { useUserContext } from '@app/context/useUserContext';
import { useEntityData } from '@app/entity/shared/EntityContext';
import CreateGlossaryEntityModal from '@app/entityV2/shared/EntityDropdown/CreateGlossaryEntityModal';
import { EntityType } from '@types';
const StyledEmpty = styled(Empty)`
padding: 80px 40px;
.ant-empty-footer {
@ -25,20 +19,12 @@ const StyledButton = styled(Button)`
interface Props {
title?: string;
description?: string;
refetchForTerms?: () => void;
refetchForNodes?: () => void;
onAddTerm: () => void;
onAddtermGroup: () => void;
}
function EmptyGlossarySection(props: Props) {
const { title, description, refetchForTerms, refetchForNodes } = props;
const [isCreateTermModalVisible, setIsCreateTermModalVisible] = useState(false);
const [isCreateNodeModalVisible, setIsCreateNodeModalVisible] = useState(false);
const user = useUserContext();
const canManageGlossaries = user?.platformPrivileges?.manageGlossaries;
const { entityData } = useEntityData();
const canCreateGlossaryEntity = !!entityData?.privileges?.canManageChildren || canManageGlossaries;
const { title, description, onAddTerm, onAddtermGroup } = props;
return (
<>
@ -51,29 +37,13 @@ function EmptyGlossarySection(props: Props) {
}
>
{/* not disabled on acryl-main due to ability to propose */}
<StyledButton onClick={() => setIsCreateTermModalVisible(true)}>
<StyledButton data-testid="add-term-button" onClick={onAddTerm}>
<PlusOutlined /> Add Term
</StyledButton>
<StyledButton onClick={() => setIsCreateNodeModalVisible(true)}>
<StyledButton data-testid="add-term-group-button" onClick={onAddtermGroup}>
<PlusOutlined /> Add Term Group
</StyledButton>
</StyledEmpty>
{isCreateTermModalVisible && (
<CreateGlossaryEntityModal
entityType={EntityType.GlossaryTerm}
canCreateGlossaryEntity={!!canCreateGlossaryEntity}
onClose={() => setIsCreateTermModalVisible(false)}
refetchData={refetchForTerms}
/>
)}
{isCreateNodeModalVisible && (
<CreateGlossaryEntityModal
entityType={EntityType.GlossaryNode}
canCreateGlossaryEntity={!!canCreateGlossaryEntity}
onClose={() => setIsCreateNodeModalVisible(false)}
refetchData={refetchForNodes}
/>
)}
</>
);
}

View File

@ -36,25 +36,23 @@ const ListWrapper = styled.div`
interface Props {
setIsCreateNodeModalVisible: React.Dispatch<React.SetStateAction<boolean>>;
setIsCreateTermModalVisible: React.Dispatch<React.SetStateAction<boolean>>;
hasTermsOrNodes: boolean;
nodes: (GlossaryNode | GlossaryNodeFragment)[];
terms: (GlossaryTerm | ChildGlossaryTermFragment)[];
termsLoading: boolean;
nodesLoading: boolean;
refetchForTerms: () => void;
refetchForNodes: () => void;
}
const GlossaryContentProvider = (props: Props) => {
const {
setIsCreateNodeModalVisible,
setIsCreateTermModalVisible,
hasTermsOrNodes,
nodes,
terms,
termsLoading,
nodesLoading,
refetchForTerms,
refetchForNodes,
} = props;
return (
@ -84,8 +82,8 @@ const GlossaryContentProvider = (props: Props) => {
<EmptyGlossarySection
title="Empty Glossary"
description="Create Terms and Term Groups to organize data assets using a shared vocabulary."
refetchForTerms={refetchForTerms}
refetchForNodes={refetchForNodes}
onAddTerm={() => setIsCreateTermModalVisible(true)}
onAddtermGroup={() => setIsCreateNodeModalVisible(true)}
/>
)}
</MainContentWrapper>