mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-22 14:24:35 +00:00
Glossary expand fix (#16066)
* Fix #16046 : modify glossaryTerms api endpoint to support querying immediate children with childrenCount * fix(ui): support lazy loading for n level of glossary * fix modal update for glossaryTerm * fixed expand/collapse button bug and update glossary page issue --------- Co-authored-by: sonikashah <sonikashah94@gmail.com> Co-authored-by: Shailesh Parmar <shailesh.parmar.webdev@gmail.com>
This commit is contained in:
parent
97d3625302
commit
0701fed67e
@ -32,6 +32,7 @@ import TabsLabel from '../../common/TabsLabel/TabsLabel.component';
|
|||||||
import GlossaryDetailsRightPanel from '../GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component';
|
import GlossaryDetailsRightPanel from '../GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component';
|
||||||
import GlossaryHeader from '../GlossaryHeader/GlossaryHeader.component';
|
import GlossaryHeader from '../GlossaryHeader/GlossaryHeader.component';
|
||||||
import GlossaryTermTab from '../GlossaryTermTab/GlossaryTermTab.component';
|
import GlossaryTermTab from '../GlossaryTermTab/GlossaryTermTab.component';
|
||||||
|
import { useGlossaryStore } from '../useGlossary.store';
|
||||||
import './glossary-details.less';
|
import './glossary-details.less';
|
||||||
import {
|
import {
|
||||||
GlossaryDetailsProps,
|
GlossaryDetailsProps,
|
||||||
@ -40,11 +41,9 @@ import {
|
|||||||
|
|
||||||
const GlossaryDetails = ({
|
const GlossaryDetails = ({
|
||||||
permissions,
|
permissions,
|
||||||
glossary,
|
|
||||||
updateGlossary,
|
updateGlossary,
|
||||||
updateVote,
|
updateVote,
|
||||||
handleGlossaryDelete,
|
handleGlossaryDelete,
|
||||||
glossaryTerms,
|
|
||||||
termsLoading,
|
termsLoading,
|
||||||
refreshGlossaryTerms,
|
refreshGlossaryTerms,
|
||||||
onAddGlossaryTerm,
|
onAddGlossaryTerm,
|
||||||
@ -54,7 +53,7 @@ const GlossaryDetails = ({
|
|||||||
}: GlossaryDetailsProps) => {
|
}: GlossaryDetailsProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const { activeGlossary: glossary } = useGlossaryStore();
|
||||||
const { tab: activeTab } = useParams<{ tab: string }>();
|
const { tab: activeTab } = useParams<{ tab: string }>();
|
||||||
const [feedCount, setFeedCount] = useState<FeedCounts>(
|
const [feedCount, setFeedCount] = useState<FeedCounts>(
|
||||||
FEED_COUNT_INITIAL_DATA
|
FEED_COUNT_INITIAL_DATA
|
||||||
@ -152,7 +151,7 @@ const GlossaryDetails = ({
|
|||||||
entityName={getEntityName(glossary)}
|
entityName={getEntityName(glossary)}
|
||||||
entityType={EntityType.GLOSSARY}
|
entityType={EntityType.GLOSSARY}
|
||||||
hasEditAccess={permissions.EditDescription || permissions.EditAll}
|
hasEditAccess={permissions.EditDescription || permissions.EditAll}
|
||||||
isDescriptionExpanded={isEmpty(glossaryTerms)}
|
isDescriptionExpanded={isEmpty(glossary.children)}
|
||||||
isEdit={isDescriptionEditable}
|
isEdit={isDescriptionEditable}
|
||||||
owner={glossary?.owner}
|
owner={glossary?.owner}
|
||||||
showActions={!glossary.deleted}
|
showActions={!glossary.deleted}
|
||||||
@ -164,10 +163,8 @@ const GlossaryDetails = ({
|
|||||||
|
|
||||||
<GlossaryTermTab
|
<GlossaryTermTab
|
||||||
isGlossary
|
isGlossary
|
||||||
childGlossaryTerms={glossaryTerms}
|
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
refreshGlossaryTerms={refreshGlossaryTerms}
|
refreshGlossaryTerms={refreshGlossaryTerms}
|
||||||
selectedData={glossary}
|
|
||||||
termsLoading={termsLoading}
|
termsLoading={termsLoading}
|
||||||
onAddGlossaryTerm={onAddGlossaryTerm}
|
onAddGlossaryTerm={onAddGlossaryTerm}
|
||||||
onEditGlossaryTerm={onEditGlossaryTerm}
|
onEditGlossaryTerm={onEditGlossaryTerm}
|
||||||
@ -192,7 +189,6 @@ const GlossaryDetails = ({
|
|||||||
isVersionView,
|
isVersionView,
|
||||||
permissions,
|
permissions,
|
||||||
glossary,
|
glossary,
|
||||||
glossaryTerms,
|
|
||||||
termsLoading,
|
termsLoading,
|
||||||
description,
|
description,
|
||||||
isDescriptionEditable,
|
isDescriptionEditable,
|
||||||
|
|||||||
@ -24,8 +24,7 @@ export enum GlossaryTabs {
|
|||||||
export type GlossaryDetailsProps = {
|
export type GlossaryDetailsProps = {
|
||||||
isVersionView?: boolean;
|
isVersionView?: boolean;
|
||||||
permissions: OperationPermission;
|
permissions: OperationPermission;
|
||||||
glossary: Glossary;
|
|
||||||
glossaryTerms: GlossaryTerm[];
|
|
||||||
termsLoading: boolean;
|
termsLoading: boolean;
|
||||||
updateGlossary: (value: Glossary) => Promise<void>;
|
updateGlossary: (value: Glossary) => Promise<void>;
|
||||||
updateVote?: (data: VotingDataProps) => Promise<void>;
|
updateVote?: (data: VotingDataProps) => Promise<void>;
|
||||||
|
|||||||
@ -12,27 +12,22 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { FilterOutlined } from '@ant-design/icons';
|
import { FilterOutlined } from '@ant-design/icons';
|
||||||
import {
|
import Icon from '@ant-design/icons/lib/components/Icon';
|
||||||
Button,
|
import { Button, Col, Modal, Row, Space, TableProps, Tooltip } from 'antd';
|
||||||
Col,
|
|
||||||
Modal,
|
|
||||||
Row,
|
|
||||||
Space,
|
|
||||||
Table,
|
|
||||||
TableProps,
|
|
||||||
Tooltip,
|
|
||||||
} from 'antd';
|
|
||||||
import { ColumnsType, ExpandableConfig } from 'antd/lib/table/interface';
|
import { ColumnsType, ExpandableConfig } from 'antd/lib/table/interface';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { compare } from 'fast-json-patch';
|
import { compare } from 'fast-json-patch';
|
||||||
import { isEmpty, isUndefined } from 'lodash';
|
import { cloneDeep, isEmpty, isUndefined } from 'lodash';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import { DndProvider } from 'react-dnd';
|
import { DndProvider } from 'react-dnd';
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { ReactComponent as IconDrag } from '../../../assets/svg/drag.svg';
|
||||||
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
|
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
|
||||||
|
import { ReactComponent as IconDown } from '../../../assets/svg/ic-arrow-down.svg';
|
||||||
|
import { ReactComponent as IconRight } from '../../../assets/svg/ic-arrow-right.svg';
|
||||||
import { ReactComponent as DownUpArrowIcon } from '../../../assets/svg/ic-down-up-arrow.svg';
|
import { ReactComponent as DownUpArrowIcon } from '../../../assets/svg/ic-down-up-arrow.svg';
|
||||||
import { ReactComponent as UpDownArrowIcon } from '../../../assets/svg/ic-up-down-arrow.svg';
|
import { ReactComponent as UpDownArrowIcon } from '../../../assets/svg/ic-up-down-arrow.svg';
|
||||||
import { ReactComponent as PlusOutlinedIcon } from '../../../assets/svg/plus-outlined.svg';
|
import { ReactComponent as PlusOutlinedIcon } from '../../../assets/svg/plus-outlined.svg';
|
||||||
@ -40,7 +35,11 @@ import ErrorPlaceHolder from '../../../components/common/ErrorWithPlaceholder/Er
|
|||||||
import { OwnerLabel } from '../../../components/common/OwnerLabel/OwnerLabel.component';
|
import { OwnerLabel } from '../../../components/common/OwnerLabel/OwnerLabel.component';
|
||||||
import RichTextEditorPreviewer from '../../../components/common/RichTextEditor/RichTextEditorPreviewer';
|
import RichTextEditorPreviewer from '../../../components/common/RichTextEditor/RichTextEditorPreviewer';
|
||||||
import StatusBadge from '../../../components/common/StatusBadge/StatusBadge.component';
|
import StatusBadge from '../../../components/common/StatusBadge/StatusBadge.component';
|
||||||
import { DE_ACTIVE_COLOR } from '../../../constants/constants';
|
import {
|
||||||
|
API_RES_MAX_SIZE,
|
||||||
|
DE_ACTIVE_COLOR,
|
||||||
|
TEXT_BODY_COLOR,
|
||||||
|
} from '../../../constants/constants';
|
||||||
import { GLOSSARIES_DOCS } from '../../../constants/docs.constants';
|
import { GLOSSARIES_DOCS } from '../../../constants/docs.constants';
|
||||||
import { TABLE_CONSTANTS } from '../../../constants/Teams.constants';
|
import { TABLE_CONSTANTS } from '../../../constants/Teams.constants';
|
||||||
import { ERROR_PLACEHOLDER_TYPE } from '../../../enums/common.enum';
|
import { ERROR_PLACEHOLDER_TYPE } from '../../../enums/common.enum';
|
||||||
@ -50,20 +49,28 @@ import {
|
|||||||
Status,
|
Status,
|
||||||
} from '../../../generated/entity/data/glossaryTerm';
|
} from '../../../generated/entity/data/glossaryTerm';
|
||||||
import { useApplicationStore } from '../../../hooks/useApplicationStore';
|
import { useApplicationStore } from '../../../hooks/useApplicationStore';
|
||||||
import { patchGlossaryTerm } from '../../../rest/glossaryAPI';
|
import {
|
||||||
|
getFirstLevelGlossaryTerms,
|
||||||
|
getGlossaryTerms,
|
||||||
|
GlossaryTermWithChildren,
|
||||||
|
patchGlossaryTerm,
|
||||||
|
} from '../../../rest/glossaryAPI';
|
||||||
import { Transi18next } from '../../../utils/CommonUtils';
|
import { Transi18next } from '../../../utils/CommonUtils';
|
||||||
import { getEntityName } from '../../../utils/EntityUtils';
|
import { getEntityName } from '../../../utils/EntityUtils';
|
||||||
import Fqn from '../../../utils/Fqn';
|
import Fqn from '../../../utils/Fqn';
|
||||||
import {
|
import {
|
||||||
buildTree,
|
buildTree,
|
||||||
|
findExpandableKeysForArray,
|
||||||
|
findGlossaryTermByFqn,
|
||||||
StatusClass,
|
StatusClass,
|
||||||
StatusFilters,
|
StatusFilters,
|
||||||
} from '../../../utils/GlossaryUtils';
|
} from '../../../utils/GlossaryUtils';
|
||||||
import { getGlossaryPath } from '../../../utils/RouterUtils';
|
import { getGlossaryPath } from '../../../utils/RouterUtils';
|
||||||
import { getTableExpandableConfig } from '../../../utils/TableUtils';
|
|
||||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||||
import { DraggableBodyRowProps } from '../../common/Draggable/DraggableBodyRowProps.interface';
|
import { DraggableBodyRowProps } from '../../common/Draggable/DraggableBodyRowProps.interface';
|
||||||
import Loader from '../../common/Loader/Loader';
|
import Loader from '../../common/Loader/Loader';
|
||||||
|
import Table from '../../common/Table/Table';
|
||||||
|
import { ModifiedGlossary, useGlossaryStore } from '../useGlossary.store';
|
||||||
import {
|
import {
|
||||||
GlossaryTermTabProps,
|
GlossaryTermTabProps,
|
||||||
ModifiedGlossaryTerm,
|
ModifiedGlossaryTerm,
|
||||||
@ -71,37 +78,38 @@ import {
|
|||||||
} from './GlossaryTermTab.interface';
|
} from './GlossaryTermTab.interface';
|
||||||
|
|
||||||
const GlossaryTermTab = ({
|
const GlossaryTermTab = ({
|
||||||
childGlossaryTerms = [],
|
|
||||||
refreshGlossaryTerms,
|
refreshGlossaryTerms,
|
||||||
permissions,
|
permissions,
|
||||||
isGlossary,
|
isGlossary,
|
||||||
selectedData,
|
|
||||||
termsLoading,
|
termsLoading,
|
||||||
onAddGlossaryTerm,
|
onAddGlossaryTerm,
|
||||||
onEditGlossaryTerm,
|
onEditGlossaryTerm,
|
||||||
className,
|
className,
|
||||||
}: GlossaryTermTabProps) => {
|
}: GlossaryTermTabProps) => {
|
||||||
|
const { activeGlossary, updateActiveGlossary } = useGlossaryStore();
|
||||||
const { theme } = useApplicationStore();
|
const { theme } = useApplicationStore();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const [glossaryTerms, setGlossaryTerms] = useState<ModifiedGlossaryTerm[]>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
|
const glossaryTerms = activeGlossary?.children as ModifiedGlossaryTerm[];
|
||||||
|
|
||||||
const [movedGlossaryTerm, setMovedGlossaryTerm] =
|
const [movedGlossaryTerm, setMovedGlossaryTerm] =
|
||||||
useState<MoveGlossaryTermType>();
|
useState<MoveGlossaryTermType>();
|
||||||
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
||||||
const [isTableLoading, setIsTableLoading] = useState(false);
|
const [isTableLoading, setIsTableLoading] = useState(false);
|
||||||
const [isTableHovered, setIsTableHovered] = useState(false);
|
const [isTableHovered, setIsTableHovered] = useState(false);
|
||||||
|
const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
|
||||||
|
|
||||||
const glossaryTermStatus: Status | null = useMemo(() => {
|
const glossaryTermStatus: Status | null = useMemo(() => {
|
||||||
if (!isGlossary) {
|
if (!isGlossary) {
|
||||||
return (selectedData as GlossaryTerm).status ?? Status.Approved;
|
return (activeGlossary as GlossaryTerm).status ?? Status.Approved;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}, [isGlossary, selectedData]);
|
}, [isGlossary, activeGlossary]);
|
||||||
|
|
||||||
|
const expandableKeys = useMemo(() => {
|
||||||
|
return findExpandableKeysForArray(glossaryTerms);
|
||||||
|
}, [glossaryTerms]);
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
const data: ColumnsType<ModifiedGlossaryTerm> = [
|
const data: ColumnsType<ModifiedGlossaryTerm> = [
|
||||||
@ -240,22 +248,71 @@ const GlossaryTermTab = ({
|
|||||||
}, [glossaryTerms, permissions]);
|
}, [glossaryTerms, permissions]);
|
||||||
|
|
||||||
const handleAddGlossaryTermClick = () => {
|
const handleAddGlossaryTermClick = () => {
|
||||||
onAddGlossaryTerm(!isGlossary ? (selectedData as GlossaryTerm) : undefined);
|
onAddGlossaryTerm(
|
||||||
|
!isGlossary ? (activeGlossary as GlossaryTerm) : undefined
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const expandableConfig: ExpandableConfig<ModifiedGlossaryTerm> = useMemo(
|
const expandableConfig: ExpandableConfig<ModifiedGlossaryTerm> = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...getTableExpandableConfig<ModifiedGlossaryTerm>(true),
|
expandIcon: ({ expanded, onExpand, record }) => {
|
||||||
expandedRowKeys,
|
const { children, childrenCount } = record;
|
||||||
onExpand: (expanded, record) => {
|
|
||||||
setExpandedRowKeys(
|
return childrenCount ?? children?.length ?? 0 > 0 ? (
|
||||||
expanded
|
<>
|
||||||
? [...expandedRowKeys, record.fullyQualifiedName || '']
|
<IconDrag className="m-r-xs drag-icon" height={12} width={8} />
|
||||||
: expandedRowKeys.filter((key) => key !== record.fullyQualifiedName)
|
<Icon
|
||||||
|
className="m-r-xs vertical-baseline"
|
||||||
|
component={expanded ? IconDown : IconRight}
|
||||||
|
data-testid="expand-icon"
|
||||||
|
style={{ fontSize: '10px', color: TEXT_BODY_COLOR }}
|
||||||
|
onClick={(e) => onExpand(record, e)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<IconDrag className="m-r-xs drag-icon" height={12} width={8} />
|
||||||
|
<span className="expand-cell-empty-icon-container" />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
expandedRowKeys: expandedRowKeys,
|
||||||
|
onExpand: async (expanded, record) => {
|
||||||
|
if (expanded) {
|
||||||
|
let children = record.children as GlossaryTermWithChildren[];
|
||||||
|
if (!children?.length) {
|
||||||
|
const { data } = await getFirstLevelGlossaryTerms(
|
||||||
|
record.fullyQualifiedName || ''
|
||||||
|
);
|
||||||
|
const terms = cloneDeep(glossaryTerms) ?? [];
|
||||||
|
|
||||||
|
const item = findGlossaryTermByFqn(
|
||||||
|
terms,
|
||||||
|
record.fullyQualifiedName ?? ''
|
||||||
|
);
|
||||||
|
|
||||||
|
(item as ModifiedGlossary).children = data;
|
||||||
|
|
||||||
|
updateActiveGlossary({ children: terms });
|
||||||
|
|
||||||
|
children = data;
|
||||||
|
}
|
||||||
|
setExpandedRowKeys([
|
||||||
|
...expandedRowKeys,
|
||||||
|
record.fullyQualifiedName || '',
|
||||||
|
]);
|
||||||
|
|
||||||
|
return children;
|
||||||
|
} else {
|
||||||
|
setExpandedRowKeys(
|
||||||
|
expandedRowKeys.filter((key) => key !== record.fullyQualifiedName)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Loader />;
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
[expandedRowKeys]
|
[glossaryTerms, updateActiveGlossary, expandedRowKeys]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMoveRow = useCallback(
|
const handleMoveRow = useCallback(
|
||||||
@ -324,33 +381,47 @@ const GlossaryTermTab = ({
|
|||||||
handleTableHover,
|
handleTableHover,
|
||||||
} as DraggableBodyRowProps<GlossaryTerm>);
|
} as DraggableBodyRowProps<GlossaryTerm>);
|
||||||
|
|
||||||
const toggleExpandAll = () => {
|
|
||||||
if (expandedRowKeys.length === childGlossaryTerms.length) {
|
|
||||||
setExpandedRowKeys([]);
|
|
||||||
} else {
|
|
||||||
setExpandedRowKeys(
|
|
||||||
childGlossaryTerms.map((item) => item.fullyQualifiedName || '')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDragConfirmationModalClose = useCallback(() => {
|
const onDragConfirmationModalClose = useCallback(() => {
|
||||||
setIsModalOpen(false);
|
setIsModalOpen(false);
|
||||||
setIsTableHovered(false);
|
setIsTableHovered(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
const fetchAllTerms = async () => {
|
||||||
if (childGlossaryTerms) {
|
setIsTableLoading(true);
|
||||||
const data = buildTree(childGlossaryTerms);
|
const { data } = await getGlossaryTerms({
|
||||||
setGlossaryTerms(data as ModifiedGlossaryTerm[]);
|
glossary: activeGlossary?.id || '',
|
||||||
setExpandedRowKeys(
|
limit: API_RES_MAX_SIZE,
|
||||||
childGlossaryTerms.map((item) => item.fullyQualifiedName || '')
|
fields: 'children,owner,parent',
|
||||||
);
|
});
|
||||||
|
updateActiveGlossary({
|
||||||
|
children: buildTree(data) as ModifiedGlossary['children'],
|
||||||
|
});
|
||||||
|
const keys = data.reduce((prev, curr) => {
|
||||||
|
if (curr.children?.length) {
|
||||||
|
prev.push(curr.fullyQualifiedName ?? '');
|
||||||
}
|
}
|
||||||
setIsLoading(false);
|
|
||||||
}, [childGlossaryTerms]);
|
|
||||||
|
|
||||||
if (termsLoading || isLoading) {
|
return prev;
|
||||||
|
}, [] as string[]);
|
||||||
|
|
||||||
|
setExpandedRowKeys(keys);
|
||||||
|
|
||||||
|
setIsTableLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleExpandAll = () => {
|
||||||
|
if (expandedRowKeys.length === expandableKeys.length) {
|
||||||
|
setExpandedRowKeys([]);
|
||||||
|
} else {
|
||||||
|
fetchAllTerms();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isAllExpanded = useMemo(() => {
|
||||||
|
return expandedRowKeys.length === expandableKeys.length;
|
||||||
|
}, [expandedRowKeys, expandableKeys]);
|
||||||
|
|
||||||
|
if (termsLoading) {
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,15 +453,13 @@ const GlossaryTermTab = ({
|
|||||||
type="text"
|
type="text"
|
||||||
onClick={toggleExpandAll}>
|
onClick={toggleExpandAll}>
|
||||||
<Space align="center" size={4}>
|
<Space align="center" size={4}>
|
||||||
{expandedRowKeys.length === childGlossaryTerms.length ? (
|
{isAllExpanded ? (
|
||||||
<DownUpArrowIcon color={DE_ACTIVE_COLOR} height="14px" />
|
<DownUpArrowIcon color={DE_ACTIVE_COLOR} height="14px" />
|
||||||
) : (
|
) : (
|
||||||
<UpDownArrowIcon color={DE_ACTIVE_COLOR} height="14px" />
|
<UpDownArrowIcon color={DE_ACTIVE_COLOR} height="14px" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{expandedRowKeys.length === childGlossaryTerms.length
|
{isAllExpanded ? t('label.collapse-all') : t('label.expand-all')}
|
||||||
? t('label.collapse-all')
|
|
||||||
: t('label.expand-all')}
|
|
||||||
</Space>
|
</Space>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -437,7 +506,9 @@ const GlossaryTermTab = ({
|
|||||||
renderElement={<strong />}
|
renderElement={<strong />}
|
||||||
values={{
|
values={{
|
||||||
from: movedGlossaryTerm?.from.name,
|
from: movedGlossaryTerm?.from.name,
|
||||||
to: movedGlossaryTerm?.to?.name ?? getEntityName(selectedData),
|
to:
|
||||||
|
movedGlossaryTerm?.to?.name ??
|
||||||
|
(activeGlossary && getEntityName(activeGlossary)),
|
||||||
entity: isUndefined(movedGlossaryTerm?.to)
|
entity: isUndefined(movedGlossaryTerm?.to)
|
||||||
? ''
|
? ''
|
||||||
: t('label.term-lowercase'),
|
: t('label.term-lowercase'),
|
||||||
|
|||||||
@ -12,12 +12,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { OperationPermission } from '../../../context/PermissionProvider/PermissionProvider.interface';
|
import { OperationPermission } from '../../../context/PermissionProvider/PermissionProvider.interface';
|
||||||
import { Glossary } from '../../../generated/entity/data/glossary';
|
|
||||||
import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm';
|
import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm';
|
||||||
|
|
||||||
export interface GlossaryTermTabProps {
|
export interface GlossaryTermTabProps {
|
||||||
selectedData: Glossary | GlossaryTerm;
|
|
||||||
childGlossaryTerms: GlossaryTerm[];
|
|
||||||
isGlossary: boolean;
|
isGlossary: boolean;
|
||||||
termsLoading: boolean;
|
termsLoading: boolean;
|
||||||
refreshGlossaryTerms: () => void;
|
refreshGlossaryTerms: () => void;
|
||||||
|
|||||||
@ -25,13 +25,14 @@ import {
|
|||||||
mockedGlossaryTerms,
|
mockedGlossaryTerms,
|
||||||
MOCK_PERMISSIONS,
|
MOCK_PERMISSIONS,
|
||||||
} from '../../../mocks/Glossary.mock';
|
} from '../../../mocks/Glossary.mock';
|
||||||
|
import { useGlossaryStore } from '../useGlossary.store';
|
||||||
import GlossaryTermTab from './GlossaryTermTab.component';
|
import GlossaryTermTab from './GlossaryTermTab.component';
|
||||||
|
|
||||||
const mockOnAddGlossaryTerm = jest.fn();
|
const mockOnAddGlossaryTerm = jest.fn();
|
||||||
const mockRefreshGlossaryTerms = jest.fn();
|
const mockRefreshGlossaryTerms = jest.fn();
|
||||||
const mockOnEditGlossaryTerm = jest.fn();
|
const mockOnEditGlossaryTerm = jest.fn();
|
||||||
|
|
||||||
const mockProps1 = {
|
const mockProps = {
|
||||||
childGlossaryTerms: [],
|
childGlossaryTerms: [],
|
||||||
isGlossary: false,
|
isGlossary: false,
|
||||||
permissions: MOCK_PERMISSIONS,
|
permissions: MOCK_PERMISSIONS,
|
||||||
@ -42,11 +43,6 @@ const mockProps1 = {
|
|||||||
onEditGlossaryTerm: mockOnEditGlossaryTerm,
|
onEditGlossaryTerm: mockOnEditGlossaryTerm,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockProps2 = {
|
|
||||||
...mockProps1,
|
|
||||||
childGlossaryTerms: mockedGlossaryTerms,
|
|
||||||
};
|
|
||||||
|
|
||||||
jest.mock('../../../rest/glossaryAPI', () => ({
|
jest.mock('../../../rest/glossaryAPI', () => ({
|
||||||
getGlossaryTerms: jest
|
getGlossaryTerms: jest
|
||||||
.fn()
|
.fn()
|
||||||
@ -75,10 +71,16 @@ jest.mock('../../common/Loader/Loader', () =>
|
|||||||
jest.mock('../../common/OwnerLabel/OwnerLabel.component', () => ({
|
jest.mock('../../common/OwnerLabel/OwnerLabel.component', () => ({
|
||||||
OwnerLabel: jest.fn().mockImplementation(() => <div>OwnerLabel</div>),
|
OwnerLabel: jest.fn().mockImplementation(() => <div>OwnerLabel</div>),
|
||||||
}));
|
}));
|
||||||
|
jest.mock('../useGlossary.store', () => ({
|
||||||
|
useGlossaryStore: jest.fn().mockImplementation(() => ({
|
||||||
|
activeGlossary: mockedGlossaryTerms[0],
|
||||||
|
updateActiveGlossary: jest.fn(),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('Test GlossaryTermTab component', () => {
|
describe('Test GlossaryTermTab component', () => {
|
||||||
it('should show the ErrorPlaceHolder component, if no glossary is present', () => {
|
it('should show the ErrorPlaceHolder component, if no glossary is present', () => {
|
||||||
const { container } = render(<GlossaryTermTab {...mockProps1} />, {
|
const { container } = render(<GlossaryTermTab {...mockProps} />, {
|
||||||
wrapper: MemoryRouter,
|
wrapper: MemoryRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,7 +88,7 @@ describe('Test GlossaryTermTab component', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should call the onAddGlossaryTerm fn onClick of add button in ErrorPlaceHolder', () => {
|
it('should call the onAddGlossaryTerm fn onClick of add button in ErrorPlaceHolder', () => {
|
||||||
const { container } = render(<GlossaryTermTab {...mockProps1} />, {
|
const { container } = render(<GlossaryTermTab {...mockProps} />, {
|
||||||
wrapper: MemoryRouter,
|
wrapper: MemoryRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -96,7 +98,14 @@ describe('Test GlossaryTermTab component', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should contain all necessary fields value in table when glossary data is not empty', async () => {
|
it('should contain all necessary fields value in table when glossary data is not empty', async () => {
|
||||||
const { container } = render(<GlossaryTermTab {...mockProps2} />, {
|
(useGlossaryStore as unknown as jest.Mock).mockImplementation(() => ({
|
||||||
|
activeGlossary: {
|
||||||
|
...mockedGlossaryTerms[0],
|
||||||
|
children: mockedGlossaryTerms,
|
||||||
|
},
|
||||||
|
updateActiveGlossary: jest.fn(),
|
||||||
|
}));
|
||||||
|
const { container } = render(<GlossaryTermTab {...mockProps} />, {
|
||||||
wrapper: MemoryRouter,
|
wrapper: MemoryRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,7 @@ import { AssetSelectionModal } from '../../DataAssets/AssetsSelectionModal/Asset
|
|||||||
import { GlossaryTabs } from '../GlossaryDetails/GlossaryDetails.interface';
|
import { GlossaryTabs } from '../GlossaryDetails/GlossaryDetails.interface';
|
||||||
import GlossaryHeader from '../GlossaryHeader/GlossaryHeader.component';
|
import GlossaryHeader from '../GlossaryHeader/GlossaryHeader.component';
|
||||||
import GlossaryTermTab from '../GlossaryTermTab/GlossaryTermTab.component';
|
import GlossaryTermTab from '../GlossaryTermTab/GlossaryTermTab.component';
|
||||||
|
import { useGlossaryStore } from '../useGlossary.store';
|
||||||
import { GlossaryTermsV1Props } from './GlossaryTermsV1.interface';
|
import { GlossaryTermsV1Props } from './GlossaryTermsV1.interface';
|
||||||
import AssetsTabs, { AssetsTabRef } from './tabs/AssetsTabs.component';
|
import AssetsTabs, { AssetsTabRef } from './tabs/AssetsTabs.component';
|
||||||
import { AssetsOfEntity } from './tabs/AssetsTabs.interface';
|
import { AssetsOfEntity } from './tabs/AssetsTabs.interface';
|
||||||
@ -58,7 +59,6 @@ import GlossaryOverviewTab from './tabs/GlossaryOverviewTab.component';
|
|||||||
|
|
||||||
const GlossaryTermsV1 = ({
|
const GlossaryTermsV1 = ({
|
||||||
glossaryTerm,
|
glossaryTerm,
|
||||||
childGlossaryTerms,
|
|
||||||
handleGlossaryTermUpdate,
|
handleGlossaryTermUpdate,
|
||||||
handleGlossaryTermDelete,
|
handleGlossaryTermDelete,
|
||||||
permissions,
|
permissions,
|
||||||
@ -82,6 +82,8 @@ const GlossaryTermsV1 = ({
|
|||||||
FEED_COUNT_INITIAL_DATA
|
FEED_COUNT_INITIAL_DATA
|
||||||
);
|
);
|
||||||
const [assetCount, setAssetCount] = useState<number>(0);
|
const [assetCount, setAssetCount] = useState<number>(0);
|
||||||
|
const { activeGlossary } = useGlossaryStore();
|
||||||
|
const childGlossaryTerms = activeGlossary?.children ?? [];
|
||||||
|
|
||||||
const assetPermissions = useMemo(() => {
|
const assetPermissions = useMemo(() => {
|
||||||
const glossaryTermStatus = glossaryTerm.status ?? Status.Approved;
|
const glossaryTermStatus = glossaryTerm.status ?? Status.Approved;
|
||||||
@ -198,12 +200,10 @@ const GlossaryTermsV1 = ({
|
|||||||
key: 'terms',
|
key: 'terms',
|
||||||
children: (
|
children: (
|
||||||
<GlossaryTermTab
|
<GlossaryTermTab
|
||||||
childGlossaryTerms={childGlossaryTerms}
|
|
||||||
className="p-md glossary-term-table-container"
|
className="p-md glossary-term-table-container"
|
||||||
isGlossary={false}
|
isGlossary={false}
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
refreshGlossaryTerms={refreshGlossaryTerms}
|
refreshGlossaryTerms={refreshGlossaryTerms}
|
||||||
selectedData={glossaryTerm}
|
|
||||||
termsLoading={termsLoading}
|
termsLoading={termsLoading}
|
||||||
onAddGlossaryTerm={onAddGlossaryTerm}
|
onAddGlossaryTerm={onAddGlossaryTerm}
|
||||||
onEditGlossaryTerm={onEditGlossaryTerm}
|
onEditGlossaryTerm={onEditGlossaryTerm}
|
||||||
|
|||||||
@ -19,7 +19,6 @@ export interface GlossaryTermsV1Props {
|
|||||||
isVersionView?: boolean;
|
isVersionView?: boolean;
|
||||||
permissions: OperationPermission;
|
permissions: OperationPermission;
|
||||||
glossaryTerm: GlossaryTerm;
|
glossaryTerm: GlossaryTerm;
|
||||||
childGlossaryTerms: GlossaryTerm[];
|
|
||||||
handleGlossaryTermUpdate: (data: GlossaryTerm) => Promise<void>;
|
handleGlossaryTermUpdate: (data: GlossaryTerm) => Promise<void>;
|
||||||
handleGlossaryTermDelete: (id: string) => Promise<void>;
|
handleGlossaryTermDelete: (id: string) => Promise<void>;
|
||||||
refreshGlossaryTerms: () => void;
|
refreshGlossaryTerms: () => void;
|
||||||
|
|||||||
@ -88,7 +88,7 @@ const mockProps = {
|
|||||||
|
|
||||||
describe('Test Glossary-term component', () => {
|
describe('Test Glossary-term component', () => {
|
||||||
it('Should render Glossary-term component', async () => {
|
it('Should render Glossary-term component', async () => {
|
||||||
render(<GlossaryTerms {...mockProps} childGlossaryTerms={[]} />);
|
render(<GlossaryTerms {...mockProps} />);
|
||||||
|
|
||||||
const glossaryTerm = screen.getByTestId('glossary-term');
|
const glossaryTerm = screen.getByTestId('glossary-term');
|
||||||
const tabs = await screen.findAllByRole('tab');
|
const tabs = await screen.findAllByRole('tab');
|
||||||
|
|||||||
@ -19,10 +19,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { withActivityFeed } from '../../components/AppRouter/withActivityFeed';
|
import { withActivityFeed } from '../../components/AppRouter/withActivityFeed';
|
||||||
import { HTTP_STATUS_CODE } from '../../constants/Auth.constants';
|
import { HTTP_STATUS_CODE } from '../../constants/Auth.constants';
|
||||||
import {
|
import { getGlossaryTermDetailsPath } from '../../constants/constants';
|
||||||
API_RES_MAX_SIZE,
|
|
||||||
getGlossaryTermDetailsPath,
|
|
||||||
} from '../../constants/constants';
|
|
||||||
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
|
||||||
import {
|
import {
|
||||||
OperationPermission,
|
OperationPermission,
|
||||||
@ -33,13 +30,12 @@ import {
|
|||||||
CreateThread,
|
CreateThread,
|
||||||
ThreadType,
|
ThreadType,
|
||||||
} from '../../generated/api/feed/createThread';
|
} from '../../generated/api/feed/createThread';
|
||||||
import { Glossary } from '../../generated/entity/data/glossary';
|
|
||||||
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
|
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
|
||||||
import { VERSION_VIEW_GLOSSARY_PERMISSION } from '../../mocks/Glossary.mock';
|
import { VERSION_VIEW_GLOSSARY_PERMISSION } from '../../mocks/Glossary.mock';
|
||||||
import { postThread } from '../../rest/feedsAPI';
|
import { postThread } from '../../rest/feedsAPI';
|
||||||
import {
|
import {
|
||||||
addGlossaryTerm,
|
addGlossaryTerm,
|
||||||
getGlossaryTerms,
|
getFirstLevelGlossaryTerms,
|
||||||
ListGlossaryTermsParams,
|
ListGlossaryTermsParams,
|
||||||
patchGlossaryTerm,
|
patchGlossaryTerm,
|
||||||
} from '../../rest/glossaryAPI';
|
} from '../../rest/glossaryAPI';
|
||||||
@ -57,6 +53,7 @@ import GlossaryTermsV1 from './GlossaryTerms/GlossaryTermsV1.component';
|
|||||||
import { GlossaryV1Props } from './GlossaryV1.interfaces';
|
import { GlossaryV1Props } from './GlossaryV1.interfaces';
|
||||||
import './glossaryV1.less';
|
import './glossaryV1.less';
|
||||||
import ImportGlossary from './ImportGlossary/ImportGlossary';
|
import ImportGlossary from './ImportGlossary/ImportGlossary';
|
||||||
|
import { useGlossaryStore } from './useGlossary.store';
|
||||||
|
|
||||||
const GlossaryV1 = ({
|
const GlossaryV1 = ({
|
||||||
isGlossaryActive,
|
isGlossaryActive,
|
||||||
@ -74,13 +71,15 @@ const GlossaryV1 = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { action, tab } =
|
const { action, tab } =
|
||||||
useParams<{ action: EntityAction; glossaryName: string; tab: string }>();
|
useParams<{ action: EntityAction; glossaryName: string; tab: string }>();
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [threadLink, setThreadLink] = useState<string>('');
|
const [threadLink, setThreadLink] = useState<string>('');
|
||||||
const [threadType, setThreadType] = useState<ThreadType>(
|
const [threadType, setThreadType] = useState<ThreadType>(
|
||||||
ThreadType.Conversation
|
ThreadType.Conversation
|
||||||
);
|
);
|
||||||
const { postFeed, deleteFeed, updateFeed } = useActivityFeedProvider();
|
const { postFeed, deleteFeed, updateFeed } = useActivityFeedProvider();
|
||||||
|
const [activeGlossaryTerm, setActiveGlossaryTerm] =
|
||||||
|
useState<GlossaryTerm | null>(null);
|
||||||
const { getEntityPermission } = usePermissionProvider();
|
const { getEntityPermission } = usePermissionProvider();
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [isTermsLoading, setIsTermsLoading] = useState(false);
|
const [isTermsLoading, setIsTermsLoading] = useState(false);
|
||||||
@ -94,13 +93,12 @@ const GlossaryV1 = ({
|
|||||||
useState<OperationPermission>(DEFAULT_ENTITY_PERMISSION);
|
useState<OperationPermission>(DEFAULT_ENTITY_PERMISSION);
|
||||||
|
|
||||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||||
const [activeGlossaryTerm, setActiveGlossaryTerm] = useState<
|
|
||||||
GlossaryTerm | undefined
|
|
||||||
>();
|
|
||||||
const [editMode, setEditMode] = useState(false);
|
const [editMode, setEditMode] = useState(false);
|
||||||
|
|
||||||
const [glossaryTerms, setGlossaryTerms] = useState<GlossaryTerm[]>([]);
|
const { activeGlossary, updateActiveGlossary } = useGlossaryStore();
|
||||||
const { id } = selectedData ?? {};
|
|
||||||
|
const { id, fullyQualifiedName } = activeGlossary ?? {};
|
||||||
|
|
||||||
const isImportAction = useMemo(
|
const isImportAction = useMemo(
|
||||||
() => action === EntityAction.IMPORT,
|
() => action === EntityAction.IMPORT,
|
||||||
@ -124,12 +122,14 @@ const GlossaryV1 = ({
|
|||||||
) => {
|
) => {
|
||||||
refresh ? setIsTermsLoading(true) : setIsLoading(true);
|
refresh ? setIsTermsLoading(true) : setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const { data } = await getGlossaryTerms({
|
const { data } = await getFirstLevelGlossaryTerms(
|
||||||
...params,
|
params?.glossary ?? params?.parent ?? ''
|
||||||
limit: API_RES_MAX_SIZE,
|
);
|
||||||
fields: 'children,owner,parent',
|
updateActiveGlossary({
|
||||||
|
children: data.map((data) =>
|
||||||
|
data.childrenCount ?? 0 > 0 ? { ...data, children: [] } : data
|
||||||
|
),
|
||||||
});
|
});
|
||||||
setGlossaryTerms(data);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showErrorToast(error as AxiosError);
|
showErrorToast(error as AxiosError);
|
||||||
} finally {
|
} finally {
|
||||||
@ -187,16 +187,18 @@ const GlossaryV1 = ({
|
|||||||
const loadGlossaryTerms = useCallback(
|
const loadGlossaryTerms = useCallback(
|
||||||
(refresh = false) => {
|
(refresh = false) => {
|
||||||
fetchGlossaryTerm(
|
fetchGlossaryTerm(
|
||||||
isGlossaryActive ? { glossary: id } : { parent: id },
|
isGlossaryActive
|
||||||
|
? { glossary: fullyQualifiedName }
|
||||||
|
: { parent: fullyQualifiedName },
|
||||||
refresh
|
refresh
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[id, isGlossaryActive]
|
[fullyQualifiedName, isGlossaryActive]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleGlossaryTermModalAction = (
|
const handleGlossaryTermModalAction = (
|
||||||
editMode: boolean,
|
editMode: boolean,
|
||||||
glossaryTerm: GlossaryTerm | undefined
|
glossaryTerm: GlossaryTerm | null
|
||||||
) => {
|
) => {
|
||||||
setEditMode(editMode);
|
setEditMode(editMode);
|
||||||
setActiveGlossaryTerm(glossaryTerm);
|
setActiveGlossaryTerm(glossaryTerm);
|
||||||
@ -349,8 +351,6 @@ const GlossaryV1 = ({
|
|||||||
!isEmpty(selectedData) &&
|
!isEmpty(selectedData) &&
|
||||||
(isGlossaryActive ? (
|
(isGlossaryActive ? (
|
||||||
<GlossaryDetails
|
<GlossaryDetails
|
||||||
glossary={selectedData as Glossary}
|
|
||||||
glossaryTerms={glossaryTerms}
|
|
||||||
handleGlossaryDelete={onGlossaryDelete}
|
handleGlossaryDelete={onGlossaryDelete}
|
||||||
isVersionView={isVersionsView}
|
isVersionView={isVersionsView}
|
||||||
permissions={glossaryPermission}
|
permissions={glossaryPermission}
|
||||||
@ -359,16 +359,15 @@ const GlossaryV1 = ({
|
|||||||
updateGlossary={updateGlossary}
|
updateGlossary={updateGlossary}
|
||||||
updateVote={updateVote}
|
updateVote={updateVote}
|
||||||
onAddGlossaryTerm={(term) =>
|
onAddGlossaryTerm={(term) =>
|
||||||
handleGlossaryTermModalAction(false, term)
|
handleGlossaryTermModalAction(false, term ?? null)
|
||||||
}
|
}
|
||||||
onEditGlossaryTerm={(term) =>
|
onEditGlossaryTerm={(term) =>
|
||||||
handleGlossaryTermModalAction(true, term)
|
handleGlossaryTermModalAction(true, term ?? null)
|
||||||
}
|
}
|
||||||
onThreadLinkSelect={onThreadLinkSelect}
|
onThreadLinkSelect={onThreadLinkSelect}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<GlossaryTermsV1
|
<GlossaryTermsV1
|
||||||
childGlossaryTerms={glossaryTerms}
|
|
||||||
glossaryTerm={selectedData as GlossaryTerm}
|
glossaryTerm={selectedData as GlossaryTerm}
|
||||||
handleGlossaryTermDelete={onGlossaryTermDelete}
|
handleGlossaryTermDelete={onGlossaryTermDelete}
|
||||||
handleGlossaryTermUpdate={onGlossaryTermUpdate}
|
handleGlossaryTermUpdate={onGlossaryTermUpdate}
|
||||||
@ -380,7 +379,7 @@ const GlossaryV1 = ({
|
|||||||
termsLoading={isTermsLoading}
|
termsLoading={isTermsLoading}
|
||||||
updateVote={updateVote}
|
updateVote={updateVote}
|
||||||
onAddGlossaryTerm={(term) =>
|
onAddGlossaryTerm={(term) =>
|
||||||
handleGlossaryTermModalAction(false, term)
|
handleGlossaryTermModalAction(false, term ?? null)
|
||||||
}
|
}
|
||||||
onAssetClick={onAssetClick}
|
onAssetClick={onAssetClick}
|
||||||
onEditGlossaryTerm={(term) =>
|
onEditGlossaryTerm={(term) =>
|
||||||
|
|||||||
@ -124,6 +124,12 @@ jest.mock('./ImportGlossary/ImportGlossary', () =>
|
|||||||
jest.mock('../../components/AppRouter/withActivityFeed', () => ({
|
jest.mock('../../components/AppRouter/withActivityFeed', () => ({
|
||||||
withActivityFeed: jest.fn().mockImplementation((component) => component),
|
withActivityFeed: jest.fn().mockImplementation((component) => component),
|
||||||
}));
|
}));
|
||||||
|
jest.mock('./useGlossary.store', () => ({
|
||||||
|
useGlossaryStore: jest.fn().mockImplementation(() => ({
|
||||||
|
activeGlossary: mockedGlossaryTerms[0],
|
||||||
|
updateActiveGlossary: jest.fn(),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
const mockProps: GlossaryV1Props = {
|
const mockProps: GlossaryV1Props = {
|
||||||
selectedData: mockedGlossaries[0],
|
selectedData: mockedGlossaries[0],
|
||||||
|
|||||||
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Collate.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { Glossary } from '../../generated/entity/data/glossary';
|
||||||
|
import { GlossaryTermWithChildren } from '../../rest/glossaryAPI';
|
||||||
|
|
||||||
|
export type ModifiedGlossary = Glossary & {
|
||||||
|
children?: GlossaryTermWithChildren[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGlossaryStore = create<{
|
||||||
|
glossaries: Glossary[];
|
||||||
|
activeGlossary: ModifiedGlossary;
|
||||||
|
setGlossaries: (glossaries: Glossary[]) => void;
|
||||||
|
setActiveGlossary: (glossary: ModifiedGlossary) => void;
|
||||||
|
updateGlossary: (glossary: Glossary) => void;
|
||||||
|
updateActiveGlossary: (glossary: Partial<ModifiedGlossary>) => void;
|
||||||
|
}>()((set, get) => ({
|
||||||
|
glossaries: [],
|
||||||
|
activeGlossary: {} as ModifiedGlossary,
|
||||||
|
|
||||||
|
setGlossaries: (glossaries: Glossary[]) => {
|
||||||
|
set({ glossaries });
|
||||||
|
},
|
||||||
|
updateGlossary: (glossary: Glossary) => {
|
||||||
|
const { glossaries } = get();
|
||||||
|
|
||||||
|
const newGlossaries = glossaries.map((g) =>
|
||||||
|
g.fullyQualifiedName === glossary.fullyQualifiedName ? glossary : g
|
||||||
|
);
|
||||||
|
|
||||||
|
set({ glossaries: newGlossaries });
|
||||||
|
},
|
||||||
|
setActiveGlossary: (glossary: ModifiedGlossary) => {
|
||||||
|
set({ activeGlossary: glossary });
|
||||||
|
},
|
||||||
|
updateActiveGlossary: (glossary: Partial<ModifiedGlossary>) => {
|
||||||
|
const { activeGlossary } = get();
|
||||||
|
|
||||||
|
set({ activeGlossary: { ...activeGlossary, ...glossary } as Glossary });
|
||||||
|
},
|
||||||
|
}));
|
||||||
@ -189,7 +189,7 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
|
|||||||
label: value,
|
label: value,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
selectedTagsRef.current = selectedValues;
|
selectedTagsRef.current = selectedValues as SelectOption[];
|
||||||
onChange?.(selectedValues);
|
onChange?.(selectedValues);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,10 @@ import { VotingDataProps } from '../../../components/Entity/Voting/voting.interf
|
|||||||
import EntitySummaryPanel from '../../../components/Explore/EntitySummaryPanel/EntitySummaryPanel.component';
|
import EntitySummaryPanel from '../../../components/Explore/EntitySummaryPanel/EntitySummaryPanel.component';
|
||||||
import { EntityDetailsObjectInterface } from '../../../components/Explore/ExplorePage.interface';
|
import { EntityDetailsObjectInterface } from '../../../components/Explore/ExplorePage.interface';
|
||||||
import GlossaryV1 from '../../../components/Glossary/GlossaryV1.component';
|
import GlossaryV1 from '../../../components/Glossary/GlossaryV1.component';
|
||||||
|
import {
|
||||||
|
ModifiedGlossary,
|
||||||
|
useGlossaryStore,
|
||||||
|
} from '../../../components/Glossary/useGlossary.store';
|
||||||
import PageLayoutV1 from '../../../components/PageLayoutV1/PageLayoutV1';
|
import PageLayoutV1 from '../../../components/PageLayoutV1/PageLayoutV1';
|
||||||
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
|
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
|
||||||
import { PAGE_SIZE_LARGE, ROUTES } from '../../../constants/constants';
|
import { PAGE_SIZE_LARGE, ROUTES } from '../../../constants/constants';
|
||||||
@ -55,16 +59,24 @@ const GlossaryPage = () => {
|
|||||||
const { permissions } = usePermissionProvider();
|
const { permissions } = usePermissionProvider();
|
||||||
const { fqn: glossaryFqn } = useFqn();
|
const { fqn: glossaryFqn } = useFqn();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [glossaries, setGlossaries] = useState<Glossary[]>([]);
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [selectedData, setSelectedData] = useState<Glossary | GlossaryTerm>();
|
|
||||||
const [isRightPanelLoading, setIsRightPanelLoading] = useState(true);
|
const [isRightPanelLoading, setIsRightPanelLoading] = useState(true);
|
||||||
const [previewAsset, setPreviewAsset] =
|
const [previewAsset, setPreviewAsset] =
|
||||||
useState<EntityDetailsObjectInterface>();
|
useState<EntityDetailsObjectInterface>();
|
||||||
|
|
||||||
|
const {
|
||||||
|
glossaries,
|
||||||
|
setGlossaries,
|
||||||
|
activeGlossary,
|
||||||
|
setActiveGlossary,
|
||||||
|
updateActiveGlossary,
|
||||||
|
} = useGlossaryStore();
|
||||||
|
|
||||||
const isGlossaryActive = useMemo(() => {
|
const isGlossaryActive = useMemo(() => {
|
||||||
setIsRightPanelLoading(true);
|
setIsRightPanelLoading(true);
|
||||||
setSelectedData(undefined);
|
setActiveGlossary({} as ModifiedGlossary);
|
||||||
|
|
||||||
if (glossaryFqn) {
|
if (glossaryFqn) {
|
||||||
return Fqn.split(glossaryFqn).length === 1;
|
return Fqn.split(glossaryFqn).length === 1;
|
||||||
@ -140,7 +152,7 @@ const GlossaryPage = () => {
|
|||||||
fields:
|
fields:
|
||||||
'relatedTerms,reviewers,tags,owner,children,votes,domain,extension',
|
'relatedTerms,reviewers,tags,owner,children,votes,domain,extension',
|
||||||
});
|
});
|
||||||
setSelectedData(response);
|
setActiveGlossary(response as ModifiedGlossary);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showErrorToast(error as AxiosError);
|
showErrorToast(error as AxiosError);
|
||||||
} finally {
|
} finally {
|
||||||
@ -153,7 +165,7 @@ const GlossaryPage = () => {
|
|||||||
if (!isGlossaryActive) {
|
if (!isGlossaryActive) {
|
||||||
fetchGlossaryTermDetails();
|
fetchGlossaryTermDetails();
|
||||||
} else {
|
} else {
|
||||||
setSelectedData(
|
setActiveGlossary(
|
||||||
glossaries.find(
|
glossaries.find(
|
||||||
(glossary) => glossary.fullyQualifiedName === glossaryFqn
|
(glossary) => glossary.fullyQualifiedName === glossaryFqn
|
||||||
) || glossaries[0]
|
) || glossaries[0]
|
||||||
@ -167,25 +179,17 @@ const GlossaryPage = () => {
|
|||||||
}, [isGlossaryActive, glossaryFqn, glossaries]);
|
}, [isGlossaryActive, glossaryFqn, glossaries]);
|
||||||
|
|
||||||
const updateGlossary = async (updatedData: Glossary) => {
|
const updateGlossary = async (updatedData: Glossary) => {
|
||||||
const jsonPatch = compare(selectedData as Glossary, updatedData);
|
const jsonPatch = compare(activeGlossary as Glossary, updatedData);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await patchGlossaries(
|
const response = await patchGlossaries(
|
||||||
selectedData?.id as string,
|
activeGlossary?.id as string,
|
||||||
jsonPatch
|
jsonPatch
|
||||||
);
|
);
|
||||||
|
|
||||||
setGlossaries((pre) => {
|
updateActiveGlossary(response);
|
||||||
return pre.map((item) => {
|
|
||||||
if (item.name === response.name) {
|
|
||||||
return response;
|
|
||||||
} else {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (selectedData?.name !== updatedData.name) {
|
if (activeGlossary?.name !== updatedData.name) {
|
||||||
history.push(getGlossaryPath(response.fullyQualifiedName));
|
history.push(getGlossaryPath(response.fullyQualifiedName));
|
||||||
fetchGlossaryList();
|
fetchGlossaryList();
|
||||||
}
|
}
|
||||||
@ -198,36 +202,24 @@ const GlossaryPage = () => {
|
|||||||
async (data: VotingDataProps) => {
|
async (data: VotingDataProps) => {
|
||||||
try {
|
try {
|
||||||
const isGlossaryEntity =
|
const isGlossaryEntity =
|
||||||
Fqn.split(selectedData?.fullyQualifiedName ?? '').length <= 1;
|
Fqn.split(activeGlossary?.fullyQualifiedName ?? '').length <= 1;
|
||||||
|
|
||||||
if (isGlossaryEntity) {
|
if (isGlossaryEntity) {
|
||||||
const {
|
const {
|
||||||
entity: { votes },
|
entity: { votes },
|
||||||
} = await updateGlossaryVotes(selectedData?.id ?? '', data);
|
} = await updateGlossaryVotes(activeGlossary?.id ?? '', data);
|
||||||
setSelectedData(
|
updateActiveGlossary({ votes });
|
||||||
(pre) =>
|
|
||||||
pre && {
|
|
||||||
...pre,
|
|
||||||
votes,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
const {
|
const {
|
||||||
entity: { votes },
|
entity: { votes },
|
||||||
} = await updateGlossaryTermVotes(selectedData?.id ?? '', data);
|
} = await updateGlossaryTermVotes(activeGlossary?.id ?? '', data);
|
||||||
setSelectedData(
|
updateActiveGlossary({ votes });
|
||||||
(pre) =>
|
|
||||||
pre && {
|
|
||||||
...pre,
|
|
||||||
votes,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showErrorToast(error as AxiosError);
|
showErrorToast(error as AxiosError);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[setSelectedData, selectedData]
|
[updateActiveGlossary, activeGlossary]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleGlossaryDelete = async (id: string) => {
|
const handleGlossaryDelete = async (id: string) => {
|
||||||
@ -260,18 +252,18 @@ const GlossaryPage = () => {
|
|||||||
|
|
||||||
const handleGlossaryTermUpdate = useCallback(
|
const handleGlossaryTermUpdate = useCallback(
|
||||||
async (updatedData: GlossaryTerm) => {
|
async (updatedData: GlossaryTerm) => {
|
||||||
const jsonPatch = compare(selectedData as GlossaryTerm, updatedData);
|
const jsonPatch = compare(activeGlossary as GlossaryTerm, updatedData);
|
||||||
if (isEmpty(jsonPatch)) {
|
if (isEmpty(jsonPatch)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await patchGlossaryTerm(
|
const response = await patchGlossaryTerm(
|
||||||
selectedData?.id as string,
|
activeGlossary?.id as string,
|
||||||
jsonPatch
|
jsonPatch
|
||||||
);
|
);
|
||||||
if (response) {
|
if (response) {
|
||||||
setSelectedData(response);
|
setActiveGlossary(response as ModifiedGlossary);
|
||||||
if (selectedData?.name !== updatedData.name) {
|
if (activeGlossary?.name !== updatedData.name) {
|
||||||
history.push(getGlossaryPath(response.fullyQualifiedName));
|
history.push(getGlossaryPath(response.fullyQualifiedName));
|
||||||
fetchGlossaryList();
|
fetchGlossaryList();
|
||||||
}
|
}
|
||||||
@ -284,7 +276,7 @@ const GlossaryPage = () => {
|
|||||||
showErrorToast(error as AxiosError);
|
showErrorToast(error as AxiosError);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[selectedData]
|
[activeGlossary]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleGlossaryTermDelete = async (id: string) => {
|
const handleGlossaryTermDelete = async (id: string) => {
|
||||||
@ -373,7 +365,7 @@ const GlossaryPage = () => {
|
|||||||
isSummaryPanelOpen={Boolean(previewAsset)}
|
isSummaryPanelOpen={Boolean(previewAsset)}
|
||||||
isVersionsView={false}
|
isVersionsView={false}
|
||||||
refreshActiveGlossaryTerm={fetchGlossaryTermDetails}
|
refreshActiveGlossaryTerm={fetchGlossaryTermDetails}
|
||||||
selectedData={selectedData as Glossary}
|
selectedData={activeGlossary as Glossary}
|
||||||
updateGlossary={updateGlossary}
|
updateGlossary={updateGlossary}
|
||||||
updateVote={updateVote}
|
updateVote={updateVote}
|
||||||
onAssetClick={handleAssetClick}
|
onAssetClick={handleAssetClick}
|
||||||
|
|||||||
@ -289,3 +289,23 @@ export const searchGlossaryTerms = async (search: string, page = 1) => {
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GlossaryTermWithChildren = Omit<GlossaryTerm, 'children'> & {
|
||||||
|
children?: GlossaryTerm[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFirstLevelGlossaryTerms = async (parentFQN: string) => {
|
||||||
|
const apiUrl = `/glossaryTerms`;
|
||||||
|
|
||||||
|
const { data } = await APIClient.get<
|
||||||
|
PagingResponse<GlossaryTermWithChildren[]>
|
||||||
|
>(apiUrl, {
|
||||||
|
params: {
|
||||||
|
directChildrenOf: parentFQN,
|
||||||
|
fields: 'childrenCount',
|
||||||
|
limit: 100000,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
|
||||||
import { EntityType } from '../enums/entity.enum';
|
import { EntityType } from '../enums/entity.enum';
|
||||||
import {
|
import {
|
||||||
MOCKED_GLOSSARY_TERMS,
|
MOCKED_GLOSSARY_TERMS,
|
||||||
@ -17,7 +18,12 @@ import {
|
|||||||
MOCKED_GLOSSARY_TERMS_TREE,
|
MOCKED_GLOSSARY_TERMS_TREE,
|
||||||
MOCKED_GLOSSARY_TERMS_TREE_1,
|
MOCKED_GLOSSARY_TERMS_TREE_1,
|
||||||
} from '../mocks/Glossary.mock';
|
} from '../mocks/Glossary.mock';
|
||||||
import { buildTree, getQueryFilterToExcludeTerm } from './GlossaryUtils';
|
import {
|
||||||
|
buildTree,
|
||||||
|
findExpandableKeys,
|
||||||
|
findExpandableKeysForArray,
|
||||||
|
getQueryFilterToExcludeTerm,
|
||||||
|
} from './GlossaryUtils';
|
||||||
|
|
||||||
describe('Glossary Utils', () => {
|
describe('Glossary Utils', () => {
|
||||||
it('getQueryFilterToExcludeTerm returns the correct query filter', () => {
|
it('getQueryFilterToExcludeTerm returns the correct query filter', () => {
|
||||||
@ -79,4 +85,78 @@ describe('Glossary Utils', () => {
|
|||||||
MOCKED_GLOSSARY_TERMS_TREE_1
|
MOCKED_GLOSSARY_TERMS_TREE_1
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return an empty array if no glossary term is provided', () => {
|
||||||
|
const expandableKeys = findExpandableKeys();
|
||||||
|
|
||||||
|
expect(expandableKeys).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an array of expandable keys when glossary term has children', () => {
|
||||||
|
const glossaryTerm = {
|
||||||
|
fullyQualifiedName: 'example',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'child1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'grandchild1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
childrenCount: 2,
|
||||||
|
fullyQualifiedName: 'grandchild2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'child2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const expandableKeys = findExpandableKeys(
|
||||||
|
glossaryTerm as ModifiedGlossaryTerm
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(expandableKeys).toEqual(['grandchild2', 'child1', 'example']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an array of expandable keys when glossary term has childrenCount', () => {
|
||||||
|
const glossaryTerm = {
|
||||||
|
fullyQualifiedName: 'example',
|
||||||
|
childrenCount: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const expandableKeys = findExpandableKeys(
|
||||||
|
glossaryTerm as ModifiedGlossaryTerm
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(expandableKeys).toEqual(['example']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find expandable keys for an array of glossary terms', () => {
|
||||||
|
const glossaryTerms = [
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'example1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'child1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'example2',
|
||||||
|
childrenCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'example3',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const expandableKeys = findExpandableKeysForArray(
|
||||||
|
glossaryTerms as ModifiedGlossaryTerm[]
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(expandableKeys).toEqual(['example1', 'example2']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import { DefaultOptionType } from 'antd/lib/select';
|
|||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { StatusType } from '../components/common/StatusBadge/StatusBadge.interface';
|
import { StatusType } from '../components/common/StatusBadge/StatusBadge.interface';
|
||||||
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
|
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
|
||||||
|
import { ModifiedGlossary } from '../components/Glossary/useGlossary.store';
|
||||||
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
|
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
|
||||||
import { EntityType } from '../enums/entity.enum';
|
import { EntityType } from '../enums/entity.enum';
|
||||||
import { Glossary } from '../generated/entity/data/glossary';
|
import { Glossary } from '../generated/entity/data/glossary';
|
||||||
@ -148,7 +149,7 @@ export const getGlossaryBreadcrumbs = (fqn: string) => {
|
|||||||
export const findGlossaryTermByFqn = (
|
export const findGlossaryTermByFqn = (
|
||||||
list: ModifiedGlossaryTerm[],
|
list: ModifiedGlossaryTerm[],
|
||||||
fullyQualifiedName: string
|
fullyQualifiedName: string
|
||||||
): GlossaryTerm | Glossary | null => {
|
): GlossaryTerm | Glossary | ModifiedGlossary | null => {
|
||||||
for (const item of list) {
|
for (const item of list) {
|
||||||
if (item.fullyQualifiedName === fullyQualifiedName) {
|
if (item.fullyQualifiedName === fullyQualifiedName) {
|
||||||
return item;
|
return item;
|
||||||
@ -197,3 +198,53 @@ export const convertGlossaryTermsToTreeOptions = (
|
|||||||
|
|
||||||
return treeData;
|
return treeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the expandable keys in a glossary term.
|
||||||
|
* @param glossaryTerm - The glossary term to search for expandable keys.
|
||||||
|
* @returns An array of expandable keys found in the glossary term.
|
||||||
|
*/
|
||||||
|
export const findExpandableKeys = (
|
||||||
|
glossaryTerm?: ModifiedGlossaryTerm
|
||||||
|
): string[] => {
|
||||||
|
let expandableKeys: string[] = [];
|
||||||
|
|
||||||
|
if (!glossaryTerm) {
|
||||||
|
return expandableKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glossaryTerm.children) {
|
||||||
|
glossaryTerm.children.forEach((child) => {
|
||||||
|
expandableKeys = expandableKeys.concat(
|
||||||
|
findExpandableKeys(child as ModifiedGlossaryTerm)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (glossaryTerm.fullyQualifiedName) {
|
||||||
|
expandableKeys.push(glossaryTerm.fullyQualifiedName);
|
||||||
|
}
|
||||||
|
} else if (glossaryTerm.childrenCount) {
|
||||||
|
if (glossaryTerm.fullyQualifiedName) {
|
||||||
|
expandableKeys.push(glossaryTerm.fullyQualifiedName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expandableKeys;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the expandable keys for an array of glossary terms.
|
||||||
|
*
|
||||||
|
* @param glossaryTerms - An array of ModifiedGlossaryTerm objects.
|
||||||
|
* @returns An array of expandable keys.
|
||||||
|
*/
|
||||||
|
export const findExpandableKeysForArray = (
|
||||||
|
glossaryTerms: ModifiedGlossaryTerm[]
|
||||||
|
): string[] => {
|
||||||
|
let expandableKeys: string[] = [];
|
||||||
|
|
||||||
|
glossaryTerms.forEach((glossaryTerm) => {
|
||||||
|
expandableKeys = expandableKeys.concat(findExpandableKeys(glossaryTerm));
|
||||||
|
});
|
||||||
|
|
||||||
|
return expandableKeys;
|
||||||
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user