Skip to content

Commit

Permalink
Minor: work on glossary hierarchy feedback (#16197)
Browse files Browse the repository at this point in the history
* Minor: work on glossary hierarchy feedback

* minor spacing fix in the select box

* cypress fix

* fix sonar issue
  • Loading branch information
Ashish8689 authored May 10, 2024
1 parent 8dc623e commit 7fe0935
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,8 @@ const createGlossary = (glossaryData) => {

const checkDisplayName = (displayName) => {
cy.get('[data-testid="entity-header-display-name"]')
.filter(':visible')
.scrollIntoView()
.should('exist')
.and('be.visible')
.within(() => {
cy.contains(displayName);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import { OperationPermission } from '../../../context/PermissionProvider/PermissionProvider.interface';
import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm';
import { TagLabel } from '../../../generated/type/tagLabel';

export interface GlossaryTermTabProps {
isGlossary: boolean;
Expand All @@ -26,6 +27,8 @@ export interface GlossaryTermTabProps {

export type ModifiedGlossaryTerm = Omit<GlossaryTerm, 'children'> & {
children?: GlossaryTerm[];
value?: string;
data?: TagLabel;
};

export type MoveGlossaryTermType = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* limitations under the License.
*/
import { CloseOutlined } from '@ant-design/icons';
import Icon from '@ant-design/icons/lib/components/Icon';
import {
Button,
Form,
Expand All @@ -21,24 +22,24 @@ import {
} from 'antd';
import { Key } from 'antd/lib/table/interface';
import { AxiosError } from 'axios';
import { debounce, isEmpty, isUndefined, pick } from 'lodash';
import { debounce, get, isEmpty, isUndefined, pick } from 'lodash';
import { CustomTagProps } from 'rc-select/lib/BaseSelect';
import React, { FC, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PAGE_SIZE_LARGE } from '../../../constants/constants';
import { ReactComponent as ArrowIcon } from '../../../assets/svg/ic-arrow-down.svg';
import { PAGE_SIZE_LARGE, TEXT_BODY_COLOR } from '../../../constants/constants';
import { TAG_START_WITH } from '../../../constants/Tag.constants';
import { Glossary } from '../../../generated/entity/data/glossary';
import { LabelType } from '../../../generated/entity/data/table';
import { TagLabel } from '../../../generated/type/tagLabel';
import {
getGlossariesList,
getGlossaryTerms,
ListGlossaryTermsParams,
queryGlossaryTerms,
searchGlossaryTerms,
} from '../../../rest/glossaryAPI';
import { getEntityName } from '../../../utils/EntityUtils';
import {
buildTree,
convertGlossaryTermsToTreeOptions,
findGlossaryTermByFqn,
} from '../../../utils/GlossaryUtils';
Expand All @@ -47,6 +48,7 @@ import { showErrorToast } from '../../../utils/ToastUtils';
import { ModifiedGlossaryTerm } from '../../Glossary/GlossaryTermTab/GlossaryTermTab.interface';
import TagsV1 from '../../Tag/TagsV1/TagsV1.component';
import Loader from '../Loader/Loader';
import './async-select-list.less';
import {
AsyncSelectListProps,
SelectOption,
Expand Down Expand Up @@ -121,8 +123,8 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
return tagRender(data);
}

const { label, onClose } = data;
const tagLabel = getTagDisplay(label as string);
const { value, onClose } = data;
const tagLabel = getTagDisplay(value as string);
const tag = {
tagFQN: selectedTag?.data.fullyQualifiedName,
...pick(
Expand Down Expand Up @@ -171,10 +173,21 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
);
};

const handleChange: TreeSelectProps['onChange'] = (values: string[]) => {
const selectedValues = values.map((value) => {
const handleChange: TreeSelectProps['onChange'] = (
values: {
disabled: boolean;
halfChecked: boolean;
label: React.ReactNode;
value: string;
}[]
) => {
const selectedValues = values.map(({ value }) => {
const initialData = findGlossaryTermByFqn(
[...glossaries, ...searchOptions] as ModifiedGlossaryTerm[],
[
...glossaries,
...searchOptions,
...(initialOptions ?? []),
] as ModifiedGlossaryTerm[],
value
);

Expand All @@ -199,19 +212,18 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
}
setIsLoading(true);
try {
const { data } = await getGlossaryTerms({
...params,
limit: PAGE_SIZE_LARGE,
fields: 'children',
});
const results = await queryGlossaryTerms(params.glossary);

const activeGlossary = results[0];

setGlossaries((prev) =>
prev.map((glossary) => ({
...glossary,
children:
glossary.id === params?.glossary
? buildTree(data)
: (glossary as ModifiedGlossaryTerm)['children'],
children: get(
glossary.id === activeGlossary?.id ? activeGlossary : glossary,
'children',
[]
),
}))
);
} catch (error) {
Expand Down Expand Up @@ -255,20 +267,30 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
autoFocus
open
showSearch
treeCheckStrictly
treeCheckable
className="async-select-list"
data-testid="tag-selector"
dropdownRender={dropdownRender}
dropdownStyle={{ width: 300 }}
filterTreeNode={false}
loadData={({ id }) => {
loadData={({ id, value }) => {
if (expandableKeys.current.includes(id)) {
return fetchGlossaryTerm({ glossary: id });
return fetchGlossaryTerm({ glossary: value as string });
}

return Promise.resolve();
}}
notFoundContent={isLoading ? <Loader size="small" /> : null}
showCheckedStrategy={TreeSelect.SHOW_ALL}
style={{ width: '100%' }}
switcherIcon={
<Icon
component={ArrowIcon}
data-testid="expand-icon"
style={{ fontSize: '10px', color: TEXT_BODY_COLOR }}
/>
}
tagRender={customTagRender}
treeData={treeData}
treeExpandedKeys={isEmpty(searchOptions) ? undefined : expandedRowKeys}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const PAGE_SIZE = 10;
export const PAGE_SIZE_BASE = 15;
export const PAGE_SIZE_MEDIUM = 25;
export const PAGE_SIZE_LARGE = 50;
export const ES_MAX_PAGE_SIZE = 10000;
export const API_RES_MAX_SIZE = 100000;
export const LIST_SIZE = 5;
export const ADD_USER_CONTAINER_HEIGHT = 250;
Expand Down
33 changes: 32 additions & 1 deletion openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { AxiosResponse } from 'axios';
import { Operation } from 'fast-json-patch';
import { PagingResponse } from 'Models';
import { VotingDataProps } from '../components/Entity/Voting/voting.interface';
import { PAGE_SIZE_MEDIUM } from '../constants/constants';
import { ES_MAX_PAGE_SIZE, PAGE_SIZE_MEDIUM } from '../constants/constants';
import { SearchIndex } from '../enums/search.enum';
import { AddGlossaryToAssetsRequest } from '../generated/api/addGlossaryToAssetsRequest';
import { CreateGlossary } from '../generated/api/data/createGlossary';
Expand Down Expand Up @@ -95,6 +95,37 @@ export const getGlossaryTerms = async (params: ListGlossaryTermsParams) => {
return response.data;
};

export const queryGlossaryTerms = async (glossaryName: string) => {
const apiUrl = `/search/query`;

const { data } = await APIClient.get(apiUrl, {
params: {
index: SearchIndex.GLOSSARY_TERM,
q: '',
from: 0,
size: ES_MAX_PAGE_SIZE,
deleted: false,
track_total_hits: true,
query_filter: JSON.stringify({
query: {
bool: {
must: [
{
term: {
'glossary.name.keyword': glossaryName.toLocaleLowerCase(),
},
},
],
},
},
}),
getHierarchy: true,
},
});

return data;
};

export const getGlossaryTermsById = async (id: string, params?: ListParams) => {
const response = await APIClient.get<GlossaryTerm>(`/glossaryTerms/${id}`, {
params,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
* limitations under the License.
*/

import { Typography } from 'antd';
import { DefaultOptionType } from 'antd/lib/select';
import { isEmpty } from 'lodash';
import React from 'react';
import { StatusType } from '../components/common/StatusBadge/StatusBadge.interface';
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
import { ModifiedGlossary } from '../components/Glossary/useGlossary.store';
Expand Down Expand Up @@ -55,9 +57,9 @@ export const buildTree = (data: GlossaryTerm[]): GlossaryTerm[] => {
const tree: GlossaryTerm[] = [];
data.forEach((obj) => {
const current = nodes[obj.fullyQualifiedName ?? ''];
const parent = nodes[obj.parent?.fullyQualifiedName || ''];
const parent = nodes[obj.parent?.fullyQualifiedName ?? ''];

if (parent && parent.children) {
if (parent?.children) {
// converting glossaryTerm to EntityReference
parent.children.push({ ...current, type: 'glossaryTerm' });
} else {
Expand Down Expand Up @@ -151,8 +153,12 @@ export const findGlossaryTermByFqn = (
fullyQualifiedName: string
): GlossaryTerm | Glossary | ModifiedGlossary | null => {
for (const item of list) {
if (item.fullyQualifiedName === fullyQualifiedName) {
return item;
if ((item.fullyQualifiedName ?? item.value) === fullyQualifiedName) {
return {
...item,
fullyQualifiedName: item.fullyQualifiedName ?? item.data?.tagFQN,
...(item.data ?? {}),
};
}
if (item.children) {
const found = findGlossaryTermByFqn(
Expand Down Expand Up @@ -182,7 +188,11 @@ export const convertGlossaryTermsToTreeOptions = (
return {
id: option.id,
value: option.fullyQualifiedName,
title: getEntityName(option),
title: (
<Typography.Text ellipsis style={{ color: option?.style?.color }}>
{getEntityName(option)}
</Typography.Text>
),
'data-testid': `tag-${option.fullyQualifiedName}`,
checkable: isGlossaryTerm,
isLeaf: isGlossaryTerm ? !hasChildren : false,
Expand Down

0 comments on commit 7fe0935

Please sign in to comment.