mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-31 12:39:01 +00:00
fix: change design of mention list item (#12811)
* fix: change design of mention list item * feat: add avatar element in mentions list * fix: revert tooltip change * fix: cypress tests * fix: cypress * fix: cypress * fix: revert intercept change * fix: add test id * fix: lineage exception * cypress fix * feat: show breadcrumbs in mention design --------- Co-authored-by: Shailesh Parmar <shailesh.parmar.webdev@gmail.com>
This commit is contained in:
parent
64a258fc3d
commit
9eb3f516cf
@ -475,8 +475,7 @@ export const visitEntityDetailsPage = (
|
|||||||
serviceName,
|
serviceName,
|
||||||
entity,
|
entity,
|
||||||
dataTestId,
|
dataTestId,
|
||||||
entityType,
|
entityType
|
||||||
count = 0
|
|
||||||
) => {
|
) => {
|
||||||
interceptURL('GET', '/api/v1/*/name/*', 'getEntityDetails');
|
interceptURL('GET', '/api/v1/*/name/*', 'getEntityDetails');
|
||||||
interceptURL(
|
interceptURL(
|
||||||
@ -484,11 +483,7 @@ export const visitEntityDetailsPage = (
|
|||||||
`/api/v1/search/query?q=*&index=${SEARCH_INDEX[entity]}&from=*&size=**`,
|
`/api/v1/search/query?q=*&index=${SEARCH_INDEX[entity]}&from=*&size=**`,
|
||||||
'explorePageTabSearch'
|
'explorePageTabSearch'
|
||||||
);
|
);
|
||||||
interceptURL(
|
interceptURL('GET', `/api/v1/search/suggest?q=*&index=*`, 'searchQuery');
|
||||||
'GET',
|
|
||||||
`/api/v1/search/suggest?q=*&index=*`,
|
|
||||||
`searchQuery-${entity}-${count}`
|
|
||||||
);
|
|
||||||
interceptURL('GET', `/api/v1/search/*`, 'explorePageSearch');
|
interceptURL('GET', `/api/v1/search/*`, 'explorePageSearch');
|
||||||
const id = dataTestId ?? `${serviceName}-${term}`;
|
const id = dataTestId ?? `${serviceName}-${term}`;
|
||||||
|
|
||||||
@ -500,35 +495,37 @@ export const visitEntityDetailsPage = (
|
|||||||
// searching term in search box
|
// searching term in search box
|
||||||
cy.get('[data-testid="searchBox"]').scrollIntoView().should('be.visible');
|
cy.get('[data-testid="searchBox"]').scrollIntoView().should('be.visible');
|
||||||
cy.get('[data-testid="searchBox"]').type(term);
|
cy.get('[data-testid="searchBox"]').type(term);
|
||||||
verifyResponseStatusCode(`@searchQuery-${entity}-${count}`, 200);
|
cy.wait('@searchQuery').then(() => {
|
||||||
cy.get('body').then(($body) => {
|
cy.wait(500);
|
||||||
// checking if requested term is available in search suggestion
|
cy.get('body').then(($body) => {
|
||||||
if ($body.find(`[data-testid="${id}"] [data-testid="data-name"]`).length) {
|
// checking if requested term is available in search suggestion
|
||||||
// if term is available in search suggestion, redirecting to entity details page
|
if (
|
||||||
cy.get(`[data-testid="${id}"] [data-testid="data-name"]`)
|
$body.find(`[data-testid="${id}"] [data-testid="data-name"]`).length
|
||||||
.should('be.visible')
|
) {
|
||||||
.first()
|
// if term is available in search suggestion, redirecting to entity details page
|
||||||
.click();
|
cy.get(`[data-testid="${id}"] [data-testid="data-name"]`)
|
||||||
} else {
|
.should('be.visible')
|
||||||
// if term is not available in search suggestion, hitting enter to search box so it will redirect to explore page
|
.first()
|
||||||
cy.get('body').click(1, 1);
|
.click();
|
||||||
cy.get('[data-testid="searchBox"]').type('{enter}');
|
} else {
|
||||||
verifyResponseStatusCode('@explorePageSearch', 200);
|
// if term is not available in search suggestion,
|
||||||
|
// hitting enter to search box so it will redirect to explore page
|
||||||
|
cy.get('body').click(1, 1);
|
||||||
|
cy.get('[data-testid="searchBox"]').type('{enter}');
|
||||||
|
verifyResponseStatusCode('@explorePageSearch', 200);
|
||||||
|
|
||||||
cy.get(`[data-testid="${entity}-tab"]`).should('be.visible').click();
|
cy.get(`[data-testid="${entity}-tab"]`).should('be.visible').click();
|
||||||
cy.get(`[data-testid="${entity}-tab"]`).should('be.visible');
|
cy.get(`[data-testid="${entity}-tab"]`).should('be.visible');
|
||||||
verifyResponseStatusCode('@explorePageTabSearch', 200);
|
verifyResponseStatusCode('@explorePageTabSearch', 200);
|
||||||
|
|
||||||
cy.get(`[data-testid="${id}"]`)
|
cy.get(`[data-testid="${id}"]`).scrollIntoView().click();
|
||||||
.scrollIntoView()
|
}
|
||||||
.should('be.visible')
|
});
|
||||||
.click();
|
|
||||||
}
|
verifyResponseStatusCode('@getEntityDetails', 200);
|
||||||
|
cy.get('body').click(1, 1);
|
||||||
|
cy.get('[data-testid="searchBox"]').clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyResponseStatusCode('@getEntityDetails', 200);
|
|
||||||
cy.get('body').click(1, 1);
|
|
||||||
cy.get('[data-testid="searchBox"]').clear();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// add new tag to entity and its table
|
// add new tag to entity and its table
|
||||||
|
@ -211,10 +211,7 @@ describe('DataConsumer Edit policy should work properly', () => {
|
|||||||
visitEntityDetailsPage(
|
visitEntityDetailsPage(
|
||||||
ENTITIES.table.term,
|
ENTITIES.table.term,
|
||||||
ENTITIES.table.serviceName,
|
ENTITIES.table.serviceName,
|
||||||
ENTITIES.table.entity,
|
ENTITIES.table.entity
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
1
|
|
||||||
);
|
);
|
||||||
|
|
||||||
cy.get('[data-testid="add-tag"]')
|
cy.get('[data-testid="add-tag"]')
|
||||||
@ -228,10 +225,7 @@ describe('DataConsumer Edit policy should work properly', () => {
|
|||||||
visitEntityDetailsPage(
|
visitEntityDetailsPage(
|
||||||
ENTITIES.dashboard.term,
|
ENTITIES.dashboard.term,
|
||||||
ENTITIES.dashboard.serviceName,
|
ENTITIES.dashboard.serviceName,
|
||||||
ENTITIES.dashboard.entity,
|
ENTITIES.dashboard.entity
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
1
|
|
||||||
);
|
);
|
||||||
|
|
||||||
cy.get('[data-testid="add-tag"]')
|
cy.get('[data-testid="add-tag"]')
|
||||||
|
@ -526,7 +526,9 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
|
|
||||||
setNodes((prevNodes) => {
|
setNodes((prevNodes) => {
|
||||||
return prevNodes.map((prevNode) => {
|
return prevNodes.map((prevNode) => {
|
||||||
const nodeTraced = prevNode.data.columns[column];
|
const { columns } = prevNode.data;
|
||||||
|
const nodeTraced = columns && columns[column];
|
||||||
|
|
||||||
prevNode.data = {
|
prevNode.data = {
|
||||||
...prevNode.data,
|
...prevNode.data,
|
||||||
selected: !isUndefined(nodeTraced),
|
selected: !isUndefined(nodeTraced),
|
||||||
|
@ -1028,9 +1028,7 @@
|
|||||||
|
|
||||||
.ql-mention-list-item {
|
.ql-mention-list-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
line-height: 44px;
|
padding: 8px 12px;
|
||||||
font-size: 16px;
|
|
||||||
padding: 0 20px;
|
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.ql-mention-list-item.selected {
|
.ql-mention-list-item.selected {
|
||||||
@ -1074,3 +1072,26 @@ button.ql-emoji {
|
|||||||
.ap {
|
.ap {
|
||||||
text-indent: 0px;
|
text-indent: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mention-avatar {
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mention-profile-image {
|
||||||
|
display: inline-block;
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mention-profile-image img {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mention-icon-image > svg {
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 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 { HTMLAttributes } from 'react';
|
||||||
|
|
||||||
|
export interface MentionSuggestionsItem {
|
||||||
|
id: string | undefined;
|
||||||
|
value: string;
|
||||||
|
link: string;
|
||||||
|
name: string;
|
||||||
|
type?: string;
|
||||||
|
avatarEle?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FeedEditorProp extends HTMLAttributes<HTMLDivElement> {
|
||||||
|
defaultValue?: string;
|
||||||
|
editorClass?: string;
|
||||||
|
className?: string;
|
||||||
|
placeHolder?: string;
|
||||||
|
onChangeHandler?: (value: string) => void;
|
||||||
|
onSave?: () => void;
|
||||||
|
focused?: boolean;
|
||||||
|
}
|
@ -18,15 +18,16 @@ import 'quill-mention';
|
|||||||
import QuillMarkdown from 'quilljs-markdown';
|
import QuillMarkdown from 'quilljs-markdown';
|
||||||
import React, {
|
import React, {
|
||||||
forwardRef,
|
forwardRef,
|
||||||
HTMLAttributes,
|
|
||||||
useEffect,
|
useEffect,
|
||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import ReactDOMServer from 'react-dom/server';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import ReactQuill, { Quill } from 'react-quill';
|
import ReactQuill, { Quill } from 'react-quill';
|
||||||
|
import { getEntityIcon } from 'utils/TableUtils';
|
||||||
import {
|
import {
|
||||||
MENTION_ALLOWED_CHARS,
|
MENTION_ALLOWED_CHARS,
|
||||||
MENTION_DENOTATION_CHARS,
|
MENTION_DENOTATION_CHARS,
|
||||||
@ -36,6 +37,7 @@ import { HTMLToMarkdown, matcher } from '../../utils/FeedUtils';
|
|||||||
import { insertMention, insertRef } from '../../utils/QuillUtils';
|
import { insertMention, insertRef } from '../../utils/QuillUtils';
|
||||||
import { editorRef } from '../common/rich-text-editor/RichTextEditor.interface';
|
import { editorRef } from '../common/rich-text-editor/RichTextEditor.interface';
|
||||||
import './FeedEditor.css';
|
import './FeedEditor.css';
|
||||||
|
import { FeedEditorProp } from './FeedEditor.interface';
|
||||||
|
|
||||||
Quill.register('modules/markdownOptions', QuillMarkdown);
|
Quill.register('modules/markdownOptions', QuillMarkdown);
|
||||||
Quill.register('modules/emoji', Emoji);
|
Quill.register('modules/emoji', Emoji);
|
||||||
@ -45,16 +47,6 @@ const strikethrough = (_node: any, delta: typeof Delta) => {
|
|||||||
return delta.compose(new Delta().retain(delta.length(), { strike: true }));
|
return delta.compose(new Delta().retain(delta.length(), { strike: true }));
|
||||||
};
|
};
|
||||||
|
|
||||||
interface FeedEditorProp extends HTMLAttributes<HTMLDivElement> {
|
|
||||||
defaultValue?: string;
|
|
||||||
editorClass?: string;
|
|
||||||
className?: string;
|
|
||||||
placeHolder?: string;
|
|
||||||
onChangeHandler?: (value: string) => void;
|
|
||||||
onSave?: () => void;
|
|
||||||
focused?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const FeedEditor = forwardRef<editorRef, FeedEditorProp>(
|
export const FeedEditor = forwardRef<editorRef, FeedEditorProp>(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -107,6 +99,45 @@ export const FeedEditor = forwardRef<editorRef, FeedEditorProp>(
|
|||||||
source: matcher,
|
source: matcher,
|
||||||
showDenotationChar: false,
|
showDenotationChar: false,
|
||||||
renderLoading: () => `${t('label.loading')}...`,
|
renderLoading: () => `${t('label.loading')}...`,
|
||||||
|
renderItem: (item: Record<string, any>) => {
|
||||||
|
if (!item.type) {
|
||||||
|
return `<div class="d-flex gap-2">
|
||||||
|
${item.avatarEle}
|
||||||
|
<span class="d-flex items-center truncate w-56">${item.name}</span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const breadcrumbsData = item.breadcrumbs
|
||||||
|
? item.breadcrumbs
|
||||||
|
.map((obj: { name: string }) => obj.name)
|
||||||
|
.join('/')
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const breadcrumbEle = breadcrumbsData
|
||||||
|
? `<div class="d-flex flex-wrap">
|
||||||
|
<span class="text-grey-muted truncate w-max-200 text-xss">${breadcrumbsData}</span>
|
||||||
|
</div>`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const icon = ReactDOMServer.renderToString(
|
||||||
|
getEntityIcon(item.type)
|
||||||
|
);
|
||||||
|
|
||||||
|
const typeSpan = !breadcrumbEle
|
||||||
|
? `<span class="text-grey-muted text-xs">${item.type}</span>`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return `<div class="d-flex items-center gap-2">
|
||||||
|
<div class="flex-center mention-icon-image">${icon}</div>
|
||||||
|
<div>
|
||||||
|
${breadcrumbEle}
|
||||||
|
<div class="d-flex flex-col">
|
||||||
|
${typeSpan}
|
||||||
|
<span class="font-medium truncate w-56">${item.name}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
markdownOptions: {},
|
markdownOptions: {},
|
||||||
clipboard: {
|
clipboard: {
|
||||||
|
@ -343,7 +343,10 @@ const NavBar = ({
|
|||||||
width={30}
|
width={30}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="m-auto relative" ref={searchContainerRef}>
|
<div
|
||||||
|
className="m-auto relative"
|
||||||
|
data-testid="navbar-search-container"
|
||||||
|
ref={searchContainerRef}>
|
||||||
<Popover
|
<Popover
|
||||||
content={
|
content={
|
||||||
!isTourRoute &&
|
!isTourRoute &&
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
import { RightOutlined } from '@ant-design/icons';
|
import { RightOutlined } from '@ant-design/icons';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
|
import { MentionSuggestionsItem } from 'components/FeedEditor/FeedEditor.interface';
|
||||||
|
import { SearchedDataProps } from 'components/searched-data/SearchedData.interface';
|
||||||
import { Operation } from 'fast-json-patch';
|
import { Operation } from 'fast-json-patch';
|
||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
@ -58,13 +60,16 @@ import {
|
|||||||
getEntityPlaceHolder,
|
getEntityPlaceHolder,
|
||||||
getPartialNameFromFQN,
|
getPartialNameFromFQN,
|
||||||
getPartialNameFromTableFQN,
|
getPartialNameFromTableFQN,
|
||||||
|
getRandomColor,
|
||||||
} from './CommonUtils';
|
} from './CommonUtils';
|
||||||
import EntityLink from './EntityLink';
|
import EntityLink from './EntityLink';
|
||||||
import { ENTITY_LINK_SEPARATOR } from './EntityUtils';
|
import { ENTITY_LINK_SEPARATOR, getEntityBreadcrumbs } from './EntityUtils';
|
||||||
|
import Fqn from './Fqn';
|
||||||
import { getEncodedFqn } from './StringsUtils';
|
import { getEncodedFqn } from './StringsUtils';
|
||||||
import { getEntityLink } from './TableUtils';
|
import { getEntityLink } from './TableUtils';
|
||||||
import { getRelativeDateByTimeStamp } from './TimeUtils';
|
import { getRelativeDateByTimeStamp } from './TimeUtils';
|
||||||
import { showErrorToast } from './ToastUtils';
|
import { showErrorToast } from './ToastUtils';
|
||||||
|
import { getUserProfilePic } from './UserDataUtils';
|
||||||
|
|
||||||
export const getEntityType = (entityLink: string) => {
|
export const getEntityType = (entityLink: string) => {
|
||||||
return EntityLink.getEntityType(entityLink);
|
return EntityLink.getEntityType(entityLink);
|
||||||
@ -153,54 +158,105 @@ export const getThreadField = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const buildMentionLink = (entityType: string, entityFqn: string) => {
|
export const buildMentionLink = (entityType: string, entityFqn: string) => {
|
||||||
|
if (entityType === EntityType.GLOSSARY_TERM) {
|
||||||
|
return `${document.location.protocol}//${document.location.host}/glossary/${entityFqn}`;
|
||||||
|
} else if (entityType === EntityType.TAG) {
|
||||||
|
const classificationFqn = Fqn.split(entityFqn);
|
||||||
|
|
||||||
|
return `${document.location.protocol}//${document.location.host}/tags/${classificationFqn[0]}`;
|
||||||
|
}
|
||||||
|
|
||||||
return `${document.location.protocol}//${document.location.host}/${entityType}/${entityFqn}`;
|
return `${document.location.protocol}//${document.location.host}/${entityType}/${entityFqn}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function suggestions(searchTerm: string, mentionChar: string) {
|
const getAvatarElementAsString = async (userName: string) => {
|
||||||
|
const res = await getUserProfilePic(true, '', userName);
|
||||||
|
let avatarEle = '';
|
||||||
|
if (!res) {
|
||||||
|
const { color, character } = getRandomColor(userName);
|
||||||
|
avatarEle = `<div
|
||||||
|
class="flex-center flex-shrink align-middle mention-avatar"
|
||||||
|
data-testid="avatar" style="background-color: ${color}">
|
||||||
|
<span>${character}</span>
|
||||||
|
</div>`;
|
||||||
|
} else {
|
||||||
|
avatarEle = `<div
|
||||||
|
class="mention-profile-image">
|
||||||
|
<img
|
||||||
|
alt="user"
|
||||||
|
data-testid="profile-image"
|
||||||
|
referrerPolicy="no-referrer"
|
||||||
|
src="${res}"
|
||||||
|
/>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return avatarEle;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function suggestions(
|
||||||
|
searchTerm: string,
|
||||||
|
mentionChar: string
|
||||||
|
): Promise<MentionSuggestionsItem[]> {
|
||||||
if (mentionChar === '@') {
|
if (mentionChar === '@') {
|
||||||
let atValues = [];
|
let atValues = [];
|
||||||
|
|
||||||
if (!searchTerm) {
|
if (!searchTerm) {
|
||||||
const data = await getSearchedUsers(WILD_CARD_CHAR, 1, 5);
|
const data = await getSearchedUsers(WILD_CARD_CHAR, 1, 5);
|
||||||
const hits = data.data.hits.hits;
|
const hits = data.data.hits.hits;
|
||||||
|
|
||||||
atValues = hits.map((hit) => {
|
atValues = await Promise.all(
|
||||||
const entityType = hit._source.entityType;
|
hits.map(async (hit) => {
|
||||||
|
const avatarEle =
|
||||||
return {
|
(await getAvatarElementAsString(hit._source.name)) ?? '';
|
||||||
id: hit._id,
|
const entityType = hit._source.entityType;
|
||||||
value: getEntityPlaceHolder(
|
const name = getEntityPlaceHolder(
|
||||||
`@${hit._source.name ?? hit._source.displayName}`,
|
`@${hit._source.name ?? hit._source.displayName}`,
|
||||||
hit._source.deleted
|
hit._source.deleted
|
||||||
),
|
);
|
||||||
link: buildMentionLink(
|
|
||||||
entityUrlMap[entityType as keyof typeof entityUrlMap],
|
return {
|
||||||
hit._source.name
|
id: hit._id,
|
||||||
),
|
value: name,
|
||||||
};
|
link: buildMentionLink(
|
||||||
});
|
entityUrlMap[entityType as keyof typeof entityUrlMap],
|
||||||
|
hit._source.name
|
||||||
|
),
|
||||||
|
name: hit._source.name,
|
||||||
|
avatarEle,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const data: any = await getUserSuggestions(searchTerm);
|
const data: any = await getUserSuggestions(searchTerm);
|
||||||
const hits = data.data.suggest['metadata-suggest'][0]['options'];
|
const hits = data.data.suggest['metadata-suggest'][0]['options'];
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
atValues = hits.map((hit: any) => {
|
|
||||||
const entityType = hit._source.entityType;
|
|
||||||
|
|
||||||
return {
|
atValues = await Promise.all(
|
||||||
id: hit._id,
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
value: getEntityPlaceHolder(
|
hits.map(async (hit: any) => {
|
||||||
|
const entityType = hit._source.entityType;
|
||||||
|
const name = getEntityPlaceHolder(
|
||||||
`@${hit._source.name ?? hit._source.display_name}`,
|
`@${hit._source.name ?? hit._source.display_name}`,
|
||||||
hit._source.deleted
|
hit._source.deleted
|
||||||
),
|
);
|
||||||
link: buildMentionLink(
|
|
||||||
entityUrlMap[entityType as keyof typeof entityUrlMap],
|
const avatarEle = await getAvatarElementAsString(hit._source.name);
|
||||||
hit._source.name
|
|
||||||
),
|
return {
|
||||||
};
|
id: hit._id,
|
||||||
});
|
value: name,
|
||||||
|
link: buildMentionLink(
|
||||||
|
entityUrlMap[entityType as keyof typeof entityUrlMap],
|
||||||
|
hit._source.name
|
||||||
|
),
|
||||||
|
name: hit._source.name,
|
||||||
|
avatarEle,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return atValues;
|
return atValues as MentionSuggestionsItem[];
|
||||||
} else {
|
} else {
|
||||||
let hashValues = [];
|
let hashValues = [];
|
||||||
if (!searchTerm) {
|
if (!searchTerm) {
|
||||||
@ -209,6 +265,11 @@ export async function suggestions(searchTerm: string, mentionChar: string) {
|
|||||||
|
|
||||||
hashValues = hits.map((hit) => {
|
hashValues = hits.map((hit) => {
|
||||||
const entityType = hit._source.entityType;
|
const entityType = hit._source.entityType;
|
||||||
|
const breadcrumbs = getEntityBreadcrumbs(
|
||||||
|
hit._source,
|
||||||
|
entityType as EntityType,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: hit._id,
|
id: hit._id,
|
||||||
@ -217,6 +278,9 @@ export async function suggestions(searchTerm: string, mentionChar: string) {
|
|||||||
entityType,
|
entityType,
|
||||||
getEncodedFqn(hit._source.fullyQualifiedName ?? '')
|
getEncodedFqn(hit._source.fullyQualifiedName ?? '')
|
||||||
),
|
),
|
||||||
|
type: entityType,
|
||||||
|
name: hit._source.displayName || hit._source.name,
|
||||||
|
breadcrumbs,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -225,6 +289,11 @@ export async function suggestions(searchTerm: string, mentionChar: string) {
|
|||||||
|
|
||||||
hashValues = hits.map((hit) => {
|
hashValues = hits.map((hit) => {
|
||||||
const entityType = hit._source.entityType;
|
const entityType = hit._source.entityType;
|
||||||
|
const breadcrumbs = getEntityBreadcrumbs(
|
||||||
|
hit._source as SearchedDataProps['data'][number]['_source'],
|
||||||
|
entityType as EntityType,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: hit._id,
|
id: hit._id,
|
||||||
@ -233,6 +302,9 @@ export async function suggestions(searchTerm: string, mentionChar: string) {
|
|||||||
entityType,
|
entityType,
|
||||||
getEncodedFqn(hit._source.fullyQualifiedName ?? '')
|
getEncodedFqn(hit._source.fullyQualifiedName ?? '')
|
||||||
),
|
),
|
||||||
|
type: entityType,
|
||||||
|
name: hit._source.displayName || hit._source.name,
|
||||||
|
breadcrumbs,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -243,7 +315,7 @@ export async function suggestions(searchTerm: string, mentionChar: string) {
|
|||||||
|
|
||||||
export async function matcher(
|
export async function matcher(
|
||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
renderList: (matches: string[], search: string) => void,
|
renderList: (matches: MentionSuggestionsItem[], search: string) => void,
|
||||||
mentionChar: string
|
mentionChar: string
|
||||||
) {
|
) {
|
||||||
const matches = await suggestions(searchTerm, mentionChar);
|
const matches = await suggestions(searchTerm, mentionChar);
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Button, Tooltip } from 'antd';
|
import { Button } from 'antd';
|
||||||
import { FqnPart } from 'enums/entity.enum';
|
import { FqnPart } from 'enums/entity.enum';
|
||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
@ -160,32 +160,30 @@ export const getSuggestionElement = (
|
|||||||
: name;
|
: name;
|
||||||
|
|
||||||
const retn = (
|
const retn = (
|
||||||
<Tooltip title={displayText}>
|
<Button
|
||||||
<Button
|
block
|
||||||
block
|
className="text-left truncate p-0"
|
||||||
className="text-left truncate p-0"
|
data-testid={dataTestId}
|
||||||
data-testid={dataTestId}
|
icon={
|
||||||
icon={
|
<img
|
||||||
<img
|
alt={serviceType}
|
||||||
alt={serviceType}
|
className="m-r-sm"
|
||||||
className="m-r-sm"
|
height="16px"
|
||||||
height="16px"
|
src={serviceTypeLogo(serviceType)}
|
||||||
src={serviceTypeLogo(serviceType)}
|
width="16px"
|
||||||
width="16px"
|
/>
|
||||||
/>
|
}
|
||||||
}
|
key={fqdn}
|
||||||
key={fqdn}
|
type="text">
|
||||||
type="text">
|
<Link
|
||||||
<Link
|
className="text-sm"
|
||||||
className="text-sm"
|
data-testid="data-name"
|
||||||
data-testid="data-name"
|
id={fqdn.replace(/\./g, '')}
|
||||||
id={fqdn.replace(/\./g, '')}
|
to={entityLink}
|
||||||
to={entityLink}
|
onClick={onClickHandler}>
|
||||||
onClick={onClickHandler}>
|
{displayText}
|
||||||
{displayText}
|
</Link>
|
||||||
</Link>
|
</Button>
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return retn;
|
return retn;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
import Icon from '@ant-design/icons';
|
import Icon from '@ant-design/icons';
|
||||||
import { Tooltip } from 'antd';
|
import { Tooltip } from 'antd';
|
||||||
import { ExpandableConfig } from 'antd/lib/table/interface';
|
import { ExpandableConfig } from 'antd/lib/table/interface';
|
||||||
|
import { ReactComponent as IconTerm } from 'assets/svg/book.svg';
|
||||||
import { ReactComponent as ClassificationIcon } from 'assets/svg/classification.svg';
|
import { ReactComponent as ClassificationIcon } from 'assets/svg/classification.svg';
|
||||||
import { ReactComponent as GlossaryIcon } from 'assets/svg/glossary.svg';
|
import { ReactComponent as GlossaryIcon } from 'assets/svg/glossary.svg';
|
||||||
import { ReactComponent as ContainerIcon } from 'assets/svg/ic-storage.svg';
|
import { ReactComponent as ContainerIcon } from 'assets/svg/ic-storage.svg';
|
||||||
@ -285,6 +286,13 @@ export const getEntityIcon = (indexType: string) => {
|
|||||||
case EntityType.DASHBOARD_DATA_MODEL:
|
case EntityType.DASHBOARD_DATA_MODEL:
|
||||||
return <IconDataModel />;
|
return <IconDataModel />;
|
||||||
|
|
||||||
|
case EntityType.TAG:
|
||||||
|
return <ClassificationIcon />;
|
||||||
|
case EntityType.GLOSSARY:
|
||||||
|
return <GlossaryIcon />;
|
||||||
|
case EntityType.GLOSSARY_TERM:
|
||||||
|
return <IconTerm />;
|
||||||
|
|
||||||
case SearchIndex.TABLE:
|
case SearchIndex.TABLE:
|
||||||
case EntityType.TABLE:
|
case EntityType.TABLE:
|
||||||
default:
|
default:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user