mirror of
https://github.com/datahub-project/datahub.git
synced 2025-11-09 07:53:33 +00:00
feat(ui): Selector recommendations in Owner, Tag and Domain Modal (#5197)
This commit is contained in:
parent
b76005d640
commit
bf7da0a853
@ -50,16 +50,17 @@ export default function GroupOwnerSideBarSection({ urn, ownership, refetch }: Pr
|
|||||||
</AddOwnerButton>
|
</AddOwnerButton>
|
||||||
)}
|
)}
|
||||||
</SectionWrapper>
|
</SectionWrapper>
|
||||||
<AddOwnersModal
|
{showAddModal && (
|
||||||
urn={urn}
|
<AddOwnersModal
|
||||||
hideOwnerType
|
urn={urn}
|
||||||
type={EntityType.CorpGroup}
|
hideOwnerType
|
||||||
visible={showAddModal}
|
type={EntityType.CorpGroup}
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
onCloseModal={() => {
|
onCloseModal={() => {
|
||||||
setShowAddModal(false);
|
setShowAddModal(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,53 +1,104 @@
|
|||||||
import { Button, Form, message, Modal, Select, Tag } from 'antd';
|
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
|
import { Button, Form, message, Modal, Select, Tag } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { useGetSearchResultsLazyQuery } from '../../../../../../../graphql/search.generated';
|
import { useGetSearchResultsLazyQuery } from '../../../../../../../graphql/search.generated';
|
||||||
import { EntityType, SearchResult } from '../../../../../../../types.generated';
|
import { Entity, EntityType } from '../../../../../../../types.generated';
|
||||||
import { useSetDomainMutation } from '../../../../../../../graphql/mutations.generated';
|
import { useSetDomainMutation } from '../../../../../../../graphql/mutations.generated';
|
||||||
import { useEntityRegistry } from '../../../../../../useEntityRegistry';
|
import { useEntityRegistry } from '../../../../../../useEntityRegistry';
|
||||||
import { useEntityData } from '../../../../EntityContext';
|
import { useEntityData } from '../../../../EntityContext';
|
||||||
import { useEnterKeyListener } from '../../../../../../shared/useEnterKeyListener';
|
import { useEnterKeyListener } from '../../../../../../shared/useEnterKeyListener';
|
||||||
|
import { useGetRecommendations } from '../../../../../../shared/recommendation';
|
||||||
|
import { DomainLabel } from '../../../../../../shared/DomainLabel';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
visible: boolean;
|
onCloseModal: () => void;
|
||||||
onClose: () => void;
|
|
||||||
refetch?: () => Promise<any>;
|
refetch?: () => Promise<any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SearchResultContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 12px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SearchResultContent = styled.div`
|
|
||||||
display: flex;
|
|
||||||
justify-content: start;
|
|
||||||
align-items: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SearchResultDisplayName = styled.div`
|
|
||||||
margin-left: 12px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type SelectedDomain = {
|
type SelectedDomain = {
|
||||||
displayName: string;
|
displayName: string;
|
||||||
type: EntityType;
|
type: EntityType;
|
||||||
urn: string;
|
urn: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SetDomainModal = ({ visible, onClose, refetch }: Props) => {
|
const StyleTag = styled(Tag)`
|
||||||
|
padding: 0px 7px;
|
||||||
|
margin-right: 3px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SetDomainModal = ({ onCloseModal, refetch }: Props) => {
|
||||||
const entityRegistry = useEntityRegistry();
|
const entityRegistry = useEntityRegistry();
|
||||||
const { urn } = useEntityData();
|
const { urn } = useEntityData();
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [selectedDomain, setSelectedDomain] = useState<SelectedDomain | undefined>(undefined);
|
const [selectedDomain, setSelectedDomain] = useState<SelectedDomain | undefined>(undefined);
|
||||||
const [domainSearch, { data: domainSearchData }] = useGetSearchResultsLazyQuery();
|
const [domainSearch, { data: domainSearchData }] = useGetSearchResultsLazyQuery();
|
||||||
const domainSearchResults = domainSearchData?.search?.searchResults || [];
|
const domainSearchResults =
|
||||||
|
domainSearchData?.search?.searchResults?.map((searchResult) => searchResult.entity) || [];
|
||||||
const [setDomainMutation] = useSetDomainMutation();
|
const [setDomainMutation] = useSetDomainMutation();
|
||||||
|
const [recommendedData] = useGetRecommendations([EntityType.Domain]);
|
||||||
const inputEl = useRef(null);
|
const inputEl = useRef(null);
|
||||||
|
|
||||||
|
const onModalClose = () => {
|
||||||
|
setInputValue('');
|
||||||
|
setSelectedDomain(undefined);
|
||||||
|
onCloseModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = (text: string) => {
|
||||||
|
if (text.length > 2) {
|
||||||
|
domainSearch({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
type: EntityType.Domain,
|
||||||
|
query: text,
|
||||||
|
start: 0,
|
||||||
|
count: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Renders a search result in the select dropdown.
|
||||||
|
const renderSearchResult = (entity: Entity) => {
|
||||||
|
const displayName = entityRegistry.getDisplayName(entity.type, entity);
|
||||||
|
return (
|
||||||
|
<Select.Option value={entity.urn} key={entity.urn}>
|
||||||
|
<DomainLabel name={displayName} />
|
||||||
|
</Select.Option>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const domainResult = !inputValue || inputValue.length === 0 ? recommendedData : domainSearchResults;
|
||||||
|
|
||||||
|
const domainSearchOptions = domainResult?.map((result) => {
|
||||||
|
return renderSearchResult(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSelectDomain = (newUrn: string) => {
|
||||||
|
if (inputEl && inputEl.current) {
|
||||||
|
(inputEl.current as any).blur();
|
||||||
|
}
|
||||||
|
const filteredDomains = domainResult?.filter((entity) => entity.urn === newUrn).map((entity) => entity) || [];
|
||||||
|
if (filteredDomains.length) {
|
||||||
|
const domain = filteredDomains[0];
|
||||||
|
setSelectedDomain({
|
||||||
|
displayName: entityRegistry.getDisplayName(EntityType.Domain, domain),
|
||||||
|
type: EntityType.Domain,
|
||||||
|
urn: newUrn,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDeselectDomain = () => {
|
||||||
|
setInputValue('');
|
||||||
|
setSelectedDomain(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
const onOk = async () => {
|
const onOk = async () => {
|
||||||
if (!selectedDomain) {
|
if (!selectedDomain) {
|
||||||
return;
|
return;
|
||||||
@ -68,75 +119,42 @@ export const SetDomainModal = ({ visible, onClose, refetch }: Props) => {
|
|||||||
}
|
}
|
||||||
setSelectedDomain(undefined);
|
setSelectedDomain(undefined);
|
||||||
refetch?.();
|
refetch?.();
|
||||||
onClose();
|
onModalClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSelectDomain = (newUrn: string) => {
|
const selectValue = (selectedDomain && [selectedDomain?.displayName]) || undefined;
|
||||||
if (inputEl && inputEl.current) {
|
|
||||||
(inputEl.current as any).blur();
|
|
||||||
}
|
|
||||||
const filteredDomains =
|
|
||||||
domainSearchResults?.filter((result) => result.entity.urn === newUrn).map((result) => result.entity) || [];
|
|
||||||
if (filteredDomains.length) {
|
|
||||||
const domain = filteredDomains[0];
|
|
||||||
setSelectedDomain({
|
|
||||||
displayName: entityRegistry.getDisplayName(EntityType.Domain, domain),
|
|
||||||
type: EntityType.Domain,
|
|
||||||
urn: newUrn,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSearch = (text: string) => {
|
|
||||||
if (text.length > 2) {
|
|
||||||
domainSearch({
|
|
||||||
variables: {
|
|
||||||
input: {
|
|
||||||
type: EntityType.Domain,
|
|
||||||
query: text,
|
|
||||||
start: 0,
|
|
||||||
count: 5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle the Enter press
|
// Handle the Enter press
|
||||||
useEnterKeyListener({
|
useEnterKeyListener({
|
||||||
querySelectorToExecuteClick: '#setDomainButton',
|
querySelectorToExecuteClick: '#setDomainButton',
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderSearchResult = (result: SearchResult) => {
|
const tagRender = (props) => {
|
||||||
const displayName = entityRegistry.getDisplayName(result.entity.type, result.entity);
|
// eslint-disable-next-line react/prop-types
|
||||||
|
const { label, closable, onClose } = props;
|
||||||
|
const onPreventMouseDown = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<SearchResultContainer>
|
<StyleTag onMouseDown={onPreventMouseDown} closable={closable} onClose={onClose}>
|
||||||
<SearchResultContent>
|
{label}
|
||||||
<SearchResultDisplayName>
|
</StyleTag>
|
||||||
<div>{displayName}</div>
|
|
||||||
</SearchResultDisplayName>
|
|
||||||
</SearchResultContent>
|
|
||||||
<Link
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
to={() => `/${entityRegistry.getPathName(result.entity.type)}/${result.entity.urn}`}
|
|
||||||
>
|
|
||||||
View
|
|
||||||
</Link>{' '}
|
|
||||||
</SearchResultContainer>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectValue = (selectedDomain && [selectedDomain?.displayName]) || [];
|
function handleBlur() {
|
||||||
|
setInputValue('');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="Set Domain"
|
title="Set Domain"
|
||||||
visible={visible}
|
visible
|
||||||
onCancel={onClose}
|
onCancel={onModalClose}
|
||||||
footer={
|
footer={
|
||||||
<>
|
<>
|
||||||
<Button onClick={onClose} type="text">
|
<Button onClick={onModalClose} type="text">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button id="setDomainButton" disabled={selectedDomain === undefined} onClick={onOk}>
|
<Button id="setDomainButton" disabled={selectedDomain === undefined} onClick={onOk}>
|
||||||
@ -149,21 +167,26 @@ export const SetDomainModal = ({ visible, onClose, refetch }: Props) => {
|
|||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Select
|
<Select
|
||||||
autoFocus
|
autoFocus
|
||||||
value={selectValue}
|
defaultOpen
|
||||||
|
filterOption={false}
|
||||||
|
showSearch
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
ref={inputEl}
|
defaultActiveFirstOption={false}
|
||||||
placeholder="Search for Domains..."
|
placeholder="Search for Domains..."
|
||||||
onSelect={(domainUrn: any) => onSelectDomain(domainUrn)}
|
onSelect={(domainUrn: any) => onSelectDomain(domainUrn)}
|
||||||
onDeselect={() => setSelectedDomain(undefined)}
|
onDeselect={onDeselectDomain}
|
||||||
onSearch={handleSearch}
|
onSearch={(value: string) => {
|
||||||
filterOption={false}
|
// eslint-disable-next-line react/prop-types
|
||||||
tagRender={(tagProps) => <Tag>{tagProps.value}</Tag>}
|
handleSearch(value.trim());
|
||||||
|
// eslint-disable-next-line react/prop-types
|
||||||
|
setInputValue(value.trim());
|
||||||
|
}}
|
||||||
|
ref={inputEl}
|
||||||
|
value={selectValue}
|
||||||
|
tagRender={tagRender}
|
||||||
|
onBlur={handleBlur}
|
||||||
>
|
>
|
||||||
{domainSearchResults.map((result) => {
|
{domainSearchOptions}
|
||||||
return (
|
|
||||||
<Select.Option value={result.entity.urn}>{renderSearchResult(result)}</Select.Option>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@ -73,13 +73,14 @@ export const SidebarDomainSection = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<SetDomainModal
|
{showModal && (
|
||||||
visible={showModal}
|
<SetDomainModal
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
onClose={() => {
|
onCloseModal={() => {
|
||||||
setShowModal(false);
|
setShowModal(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,33 +1,15 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { Button, Form, message, Modal, Select, Tag, Typography } from 'antd';
|
import { Button, Form, message, Modal, Select, Tag, Typography } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import {
|
import { CorpUser, Entity, EntityType, OwnerEntityType, OwnershipType } from '../../../../../../../types.generated';
|
||||||
CorpUser,
|
|
||||||
EntityType,
|
|
||||||
OwnerEntityType,
|
|
||||||
OwnershipType,
|
|
||||||
SearchResult,
|
|
||||||
} from '../../../../../../../types.generated';
|
|
||||||
import { useEntityRegistry } from '../../../../../../useEntityRegistry';
|
import { useEntityRegistry } from '../../../../../../useEntityRegistry';
|
||||||
import { CustomAvatar } from '../../../../../../shared/avatar';
|
|
||||||
import analytics, { EventType, EntityActionType } from '../../../../../../analytics';
|
import analytics, { EventType, EntityActionType } from '../../../../../../analytics';
|
||||||
import { OWNERSHIP_DISPLAY_TYPES } from './ownershipUtils';
|
import { OWNERSHIP_DISPLAY_TYPES } from './ownershipUtils';
|
||||||
import { useAddOwnersMutation } from '../../../../../../../graphql/mutations.generated';
|
import { useAddOwnersMutation } from '../../../../../../../graphql/mutations.generated';
|
||||||
import { useGetSearchResultsLazyQuery } from '../../../../../../../graphql/search.generated';
|
import { useGetSearchResultsLazyQuery } from '../../../../../../../graphql/search.generated';
|
||||||
|
import { useGetRecommendations } from '../../../../../../shared/recommendation';
|
||||||
const SearchResultContainer = styled.div`
|
import { OwnerLabel } from '../../../../../../shared/OwnerLabel';
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 2px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SearchResultContent = styled.div`
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SelectInput = styled(Select)`
|
const SelectInput = styled(Select)`
|
||||||
> .ant-select-selector {
|
> .ant-select-selector {
|
||||||
@ -35,10 +17,17 @@ const SelectInput = styled(Select)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const StyleTag = styled(Tag)`
|
||||||
|
padding: 0px 7px 0px 0px;
|
||||||
|
margin-right: 3px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
urn: string;
|
urn: string;
|
||||||
type: EntityType;
|
type: EntityType;
|
||||||
visible: boolean;
|
|
||||||
defaultOwnerType?: OwnershipType;
|
defaultOwnerType?: OwnershipType;
|
||||||
hideOwnerType?: boolean | undefined;
|
hideOwnerType?: boolean | undefined;
|
||||||
onCloseModal: () => void;
|
onCloseModal: () => void;
|
||||||
@ -51,16 +40,9 @@ type SelectedOwner = {
|
|||||||
value;
|
value;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AddOwnersModal = ({
|
export const AddOwnersModal = ({ urn, type, hideOwnerType, defaultOwnerType, onCloseModal, refetch }: Props) => {
|
||||||
urn,
|
|
||||||
type,
|
|
||||||
visible,
|
|
||||||
hideOwnerType,
|
|
||||||
defaultOwnerType,
|
|
||||||
onCloseModal,
|
|
||||||
refetch,
|
|
||||||
}: Props) => {
|
|
||||||
const entityRegistry = useEntityRegistry();
|
const entityRegistry = useEntityRegistry();
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [addOwnersMutation] = useAddOwnersMutation();
|
const [addOwnersMutation] = useAddOwnersMutation();
|
||||||
const ownershipTypes = OWNERSHIP_DISPLAY_TYPES;
|
const ownershipTypes = OWNERSHIP_DISPLAY_TYPES;
|
||||||
const [selectedOwners, setSelectedOwners] = useState<SelectedOwner[]>([]);
|
const [selectedOwners, setSelectedOwners] = useState<SelectedOwner[]>([]);
|
||||||
@ -69,12 +51,11 @@ export const AddOwnersModal = ({
|
|||||||
// User and group dropdown search results!
|
// User and group dropdown search results!
|
||||||
const [userSearch, { data: userSearchData }] = useGetSearchResultsLazyQuery();
|
const [userSearch, { data: userSearchData }] = useGetSearchResultsLazyQuery();
|
||||||
const [groupSearch, { data: groupSearchData }] = useGetSearchResultsLazyQuery();
|
const [groupSearch, { data: groupSearchData }] = useGetSearchResultsLazyQuery();
|
||||||
const userSearchResults = userSearchData?.search?.searchResults || [];
|
const userSearchResults = userSearchData?.search?.searchResults?.map((searchResult) => searchResult.entity) || [];
|
||||||
const groupSearchResults = groupSearchData?.search?.searchResults || [];
|
const groupSearchResults = groupSearchData?.search?.searchResults?.map((searchResult) => searchResult.entity) || [];
|
||||||
const combinedSearchResults = [...userSearchResults, ...groupSearchResults];
|
const combinedSearchResults = [...userSearchResults, ...groupSearchResults];
|
||||||
|
const [recommendedData] = useGetRecommendations([EntityType.CorpGroup, EntityType.CorpUser]);
|
||||||
// Add owners Form
|
const inputEl = useRef(null);
|
||||||
const [form] = Form.useForm();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ownershipTypes) {
|
if (ownershipTypes) {
|
||||||
@ -82,43 +63,6 @@ export const AddOwnersModal = ({
|
|||||||
}
|
}
|
||||||
}, [ownershipTypes]);
|
}, [ownershipTypes]);
|
||||||
|
|
||||||
/**
|
|
||||||
* When a owner search result is selected, add the new owner to the selectedOwners
|
|
||||||
* value: {ownerUrn: string, ownerEntityType: EntityType}
|
|
||||||
*/
|
|
||||||
const onSelectOwner = (selectedValue: { key: string; label: React.ReactNode; value: string }) => {
|
|
||||||
const filteredActors = combinedSearchResults
|
|
||||||
.filter((result) => result.entity.urn === selectedValue.value)
|
|
||||||
.map((result) => result.entity);
|
|
||||||
if (filteredActors.length) {
|
|
||||||
const actor = filteredActors[0];
|
|
||||||
const ownerEntityType =
|
|
||||||
actor && actor.type === EntityType.CorpGroup ? OwnerEntityType.CorpGroup : OwnerEntityType.CorpUser;
|
|
||||||
const newValues = [
|
|
||||||
...selectedOwners,
|
|
||||||
{
|
|
||||||
label: selectedValue.value,
|
|
||||||
value: {
|
|
||||||
ownerUrn: selectedValue.value,
|
|
||||||
ownerEntityType,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
setSelectedOwners(newValues);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// When a owner search result is deselected, remove the Owner
|
|
||||||
const onDeselectOwner = (selectedValue: { key: string; label: React.ReactNode; value: string }) => {
|
|
||||||
const newValues = selectedOwners.filter((owner) => owner.label !== selectedValue.value);
|
|
||||||
setSelectedOwners(newValues);
|
|
||||||
};
|
|
||||||
|
|
||||||
// When a owner type is selected, set the type as selected type.
|
|
||||||
const onSelectOwnerType = (newType: OwnershipType) => {
|
|
||||||
setSelectedOwnerType(newType);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Invokes the search API as the owner types
|
// Invokes the search API as the owner types
|
||||||
const handleSearch = (entityType: EntityType, text: string, searchQuery: any) => {
|
const handleSearch = (entityType: EntityType, text: string, searchQuery: any) => {
|
||||||
if (text.length > 2) {
|
if (text.length > 2) {
|
||||||
@ -142,34 +86,87 @@ export const AddOwnersModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Renders a search result in the select dropdown.
|
// Renders a search result in the select dropdown.
|
||||||
const renderSearchResult = (result: SearchResult) => {
|
const renderSearchResult = (entity: Entity) => {
|
||||||
const avatarUrl =
|
const avatarUrl =
|
||||||
result.entity.type === EntityType.CorpUser
|
entity.type === EntityType.CorpUser
|
||||||
? (result.entity as CorpUser).editableProperties?.pictureLink || undefined
|
? (entity as CorpUser).editableProperties?.pictureLink || undefined
|
||||||
: undefined;
|
: undefined;
|
||||||
const displayName = entityRegistry.getDisplayName(result.entity.type, result.entity);
|
const displayName = entityRegistry.getDisplayName(entity.type, entity);
|
||||||
return (
|
return (
|
||||||
<SearchResultContainer>
|
<Select.Option value={entity.urn} key={entity.urn}>
|
||||||
<SearchResultContent>
|
<OwnerLabel name={displayName} avatarUrl={avatarUrl} type={entity.type} />
|
||||||
<CustomAvatar
|
</Select.Option>
|
||||||
size={24}
|
|
||||||
name={displayName}
|
|
||||||
photoUrl={avatarUrl}
|
|
||||||
isGroup={result.entity.type === EntityType.CorpGroup}
|
|
||||||
/>
|
|
||||||
<div>{displayName}</div>
|
|
||||||
</SearchResultContent>
|
|
||||||
</SearchResultContainer>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ownerResult = !inputValue || inputValue.length === 0 ? recommendedData : combinedSearchResults;
|
||||||
|
|
||||||
|
const ownerSearchOptions = ownerResult?.map((result) => {
|
||||||
|
return renderSearchResult(result);
|
||||||
|
});
|
||||||
|
|
||||||
const onModalClose = () => {
|
const onModalClose = () => {
|
||||||
|
setInputValue('');
|
||||||
setSelectedOwners([]);
|
setSelectedOwners([]);
|
||||||
setSelectedOwnerType(defaultOwnerType || OwnershipType.None);
|
setSelectedOwnerType(defaultOwnerType || OwnershipType.None);
|
||||||
form.resetFields();
|
|
||||||
onCloseModal();
|
onCloseModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a owner search result is selected, add the new owner to the selectedOwners
|
||||||
|
* value: {ownerUrn: string, ownerEntityType: EntityType}
|
||||||
|
*/
|
||||||
|
const onSelectOwner = (selectedValue: { key: string; label: React.ReactNode; value: string }) => {
|
||||||
|
if (inputEl && inputEl.current) {
|
||||||
|
(inputEl.current as any).blur();
|
||||||
|
}
|
||||||
|
const filteredActors = ownerResult
|
||||||
|
?.filter((entity) => entity.urn === selectedValue.value)
|
||||||
|
.map((entity) => entity);
|
||||||
|
if (filteredActors?.length) {
|
||||||
|
const actor = filteredActors[0];
|
||||||
|
const ownerEntityType =
|
||||||
|
actor && actor.type === EntityType.CorpGroup ? OwnerEntityType.CorpGroup : OwnerEntityType.CorpUser;
|
||||||
|
const newValues = [
|
||||||
|
...selectedOwners,
|
||||||
|
{
|
||||||
|
label: selectedValue.value,
|
||||||
|
value: {
|
||||||
|
ownerUrn: selectedValue.value,
|
||||||
|
ownerEntityType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setSelectedOwners(newValues);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// When a owner search result is deselected, remove the Owner
|
||||||
|
const onDeselectOwner = (selectedValue: { key: string; label: React.ReactNode; value: string }) => {
|
||||||
|
setInputValue('');
|
||||||
|
const newValues = selectedOwners.filter((owner) => owner.label !== selectedValue.value);
|
||||||
|
setSelectedOwners(newValues);
|
||||||
|
};
|
||||||
|
|
||||||
|
// When a owner type is selected, set the type as selected type.
|
||||||
|
const onSelectOwnerType = (newType: OwnershipType) => {
|
||||||
|
setSelectedOwnerType(newType);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tagRender = (props) => {
|
||||||
|
// eslint-disable-next-line react/prop-types
|
||||||
|
const { label, closable, onClose } = props;
|
||||||
|
const onPreventMouseDown = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<StyleTag onMouseDown={onPreventMouseDown} closable={closable} onClose={onClose}>
|
||||||
|
{label}
|
||||||
|
</StyleTag>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// Function to handle the modal action's
|
// Function to handle the modal action's
|
||||||
const onOk = async () => {
|
const onOk = async () => {
|
||||||
if (selectedOwners.length === 0) {
|
if (selectedOwners.length === 0) {
|
||||||
@ -210,35 +207,14 @@ export const AddOwnersModal = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const tagRender = (props) => {
|
function handleBlur() {
|
||||||
// eslint-disable-next-line react/prop-types
|
setInputValue('');
|
||||||
const { label, closable, onClose } = props;
|
}
|
||||||
const onPreventMouseDown = (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Tag
|
|
||||||
onMouseDown={onPreventMouseDown}
|
|
||||||
closable={closable}
|
|
||||||
onClose={onClose}
|
|
||||||
style={{
|
|
||||||
padding: '0px 7px 0px 0px',
|
|
||||||
marginRight: 3,
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'start',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="Add Owners"
|
title="Add Owners"
|
||||||
visible={visible}
|
visible
|
||||||
onCancel={onModalClose}
|
onCancel={onModalClose}
|
||||||
keyboard
|
keyboard
|
||||||
footer={
|
footer={
|
||||||
@ -252,27 +228,33 @@ export const AddOwnersModal = ({
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Form layout="vertical" form={form} colon={false}>
|
<Form layout="vertical" colon={false}>
|
||||||
<Form.Item key="owners" name="owners" label={<Typography.Text strong>Owner</Typography.Text>}>
|
<Form.Item key="owners" name="owners" label={<Typography.Text strong>Owner</Typography.Text>}>
|
||||||
<Typography.Paragraph>Find a user or group</Typography.Paragraph>
|
<Typography.Paragraph>Find a user or group</Typography.Paragraph>
|
||||||
<Form.Item name="owner">
|
<Form.Item name="owner">
|
||||||
<SelectInput
|
<SelectInput
|
||||||
labelInValue
|
labelInValue
|
||||||
value={selectedOwners}
|
|
||||||
autoFocus
|
autoFocus
|
||||||
|
defaultOpen
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
filterOption={false}
|
ref={inputEl}
|
||||||
placeholder="Search for users or groups..."
|
placeholder="Search for users or groups..."
|
||||||
|
showSearch
|
||||||
|
filterOption={false}
|
||||||
|
defaultActiveFirstOption={false}
|
||||||
onSelect={(asset: any) => onSelectOwner(asset)}
|
onSelect={(asset: any) => onSelectOwner(asset)}
|
||||||
onDeselect={(asset: any) => onDeselectOwner(asset)}
|
onDeselect={(asset: any) => onDeselectOwner(asset)}
|
||||||
onSearch={handleActorSearch}
|
onSearch={(value: string) => {
|
||||||
|
// eslint-disable-next-line react/prop-types
|
||||||
|
handleActorSearch(value.trim());
|
||||||
|
// eslint-disable-next-line react/prop-types
|
||||||
|
setInputValue(value.trim());
|
||||||
|
}}
|
||||||
tagRender={tagRender}
|
tagRender={tagRender}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
value={selectedOwners}
|
||||||
>
|
>
|
||||||
{combinedSearchResults?.map((result) => (
|
{ownerSearchOptions}
|
||||||
<Select.Option key={result?.entity?.urn} value={result.entity.urn}>
|
|
||||||
{renderSearchResult(result)}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</SelectInput>
|
</SelectInput>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@ -32,17 +32,18 @@ export const SidebarOwnerSection = ({ properties }: { properties?: any }) => {
|
|||||||
<PlusOutlined /> Add Owners
|
<PlusOutlined /> Add Owners
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<AddOwnersModal
|
{showAddModal && (
|
||||||
urn={mutationUrn}
|
<AddOwnersModal
|
||||||
defaultOwnerType={properties?.defaultOwnerType}
|
urn={mutationUrn}
|
||||||
hideOwnerType={properties?.hideOwnerType || false}
|
defaultOwnerType={properties?.defaultOwnerType}
|
||||||
type={entityType}
|
hideOwnerType={properties?.hideOwnerType || false}
|
||||||
visible={showAddModal}
|
type={entityType}
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
onCloseModal={() => {
|
onCloseModal={() => {
|
||||||
setShowAddModal(false);
|
setShowAddModal(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
29
datahub-web-react/src/app/shared/DomainLabel.tsx
Normal file
29
datahub-web-react/src/app/shared/DomainLabel.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const DomainContainerWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DomainContentWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DomainLabel = ({ name }: Props) => {
|
||||||
|
return (
|
||||||
|
<DomainContainerWrapper>
|
||||||
|
<DomainContentWrapper>
|
||||||
|
<div>{name}</div>
|
||||||
|
</DomainContentWrapper>
|
||||||
|
</DomainContainerWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
34
datahub-web-react/src/app/shared/OwnerLabel.tsx
Normal file
34
datahub-web-react/src/app/shared/OwnerLabel.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { EntityType } from '../../types.generated';
|
||||||
|
import { CustomAvatar } from './avatar';
|
||||||
|
|
||||||
|
const OwnerContainerWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const OwnerContentWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
name: string;
|
||||||
|
avatarUrl: string | undefined;
|
||||||
|
type: EntityType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OwnerLabel = ({ name, avatarUrl, type }: Props) => {
|
||||||
|
return (
|
||||||
|
<OwnerContainerWrapper>
|
||||||
|
<OwnerContentWrapper>
|
||||||
|
<CustomAvatar size={24} name={name} photoUrl={avatarUrl} isGroup={type === EntityType.CorpGroup} />
|
||||||
|
<div>{name}</div>
|
||||||
|
</OwnerContentWrapper>
|
||||||
|
</OwnerContainerWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -414,16 +414,17 @@ export default function TagStyleEntity({ urn, useGetSearchResults = useWrappedSe
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<AddOwnersModal
|
{showAddModal && (
|
||||||
hideOwnerType
|
<AddOwnersModal
|
||||||
visible={showAddModal}
|
hideOwnerType
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
onCloseModal={() => {
|
onCloseModal={() => {
|
||||||
setShowAddModal(false);
|
setShowAddModal(false);
|
||||||
}}
|
}}
|
||||||
urn={urn}
|
urn={urn}
|
||||||
type={EntityType.Tag}
|
type={EntityType.Tag}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DetailsLayout>
|
</DetailsLayout>
|
||||||
|
|||||||
18
datahub-web-react/src/app/shared/recommendation.tsx
Normal file
18
datahub-web-react/src/app/shared/recommendation.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { useGetSearchResultsForMultipleQuery } from '../../graphql/search.generated';
|
||||||
|
import { EntityType } from '../../types.generated';
|
||||||
|
|
||||||
|
export const useGetRecommendations = (types: Array<EntityType>) => {
|
||||||
|
const { data } = useGetSearchResultsForMultipleQuery({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
types,
|
||||||
|
query: '*',
|
||||||
|
start: 0,
|
||||||
|
count: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const recommendedData = data?.searchAcrossEntities?.searchResults?.map((searchResult) => searchResult.entity) || [];
|
||||||
|
return [recommendedData];
|
||||||
|
};
|
||||||
@ -1,11 +1,10 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import { message, Button, Modal, Select, Typography, Tag as CustomTag } from 'antd';
|
import { message, Button, Modal, Select, Typography, Tag as CustomTag } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { useGetSearchResultsLazyQuery } from '../../../graphql/search.generated';
|
import { useGetSearchResultsLazyQuery } from '../../../graphql/search.generated';
|
||||||
import { EntityType, SubResourceType, SearchResult, Tag } from '../../../types.generated';
|
import { EntityType, SubResourceType, Tag, Entity } from '../../../types.generated';
|
||||||
import CreateTagModal from './CreateTagModal';
|
import CreateTagModal from './CreateTagModal';
|
||||||
import { useEntityRegistry } from '../../useEntityRegistry';
|
|
||||||
import { useAddTagsMutation, useAddTermsMutation } from '../../../graphql/mutations.generated';
|
import { useAddTagsMutation, useAddTermsMutation } from '../../../graphql/mutations.generated';
|
||||||
import analytics, { EventType, EntityActionType } from '../../analytics';
|
import analytics, { EventType, EntityActionType } from '../../analytics';
|
||||||
import { useEnterKeyListener } from '../useEnterKeyListener';
|
import { useEnterKeyListener } from '../useEnterKeyListener';
|
||||||
@ -13,6 +12,8 @@ import TermLabel from '../TermLabel';
|
|||||||
import TagLabel from '../TagLabel';
|
import TagLabel from '../TagLabel';
|
||||||
import GlossaryBrowser from '../../glossary/GlossaryBrowser/GlossaryBrowser';
|
import GlossaryBrowser from '../../glossary/GlossaryBrowser/GlossaryBrowser';
|
||||||
import ClickOutside from '../ClickOutside';
|
import ClickOutside from '../ClickOutside';
|
||||||
|
import { useEntityRegistry } from '../../useEntityRegistry';
|
||||||
|
import { useGetRecommendations } from '../recommendation';
|
||||||
|
|
||||||
type AddTagsModalProps = {
|
type AddTagsModalProps = {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
@ -27,6 +28,17 @@ const TagSelect = styled(Select)`
|
|||||||
width: 480px;
|
width: 480px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const StyleTag = styled(CustomTag)`
|
||||||
|
margin-right: 3px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
align-items: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
opacity: 1;
|
||||||
|
color: #434343;
|
||||||
|
line-height: 16px;
|
||||||
|
`;
|
||||||
|
|
||||||
export const BrowserWrapper = styled.div<{ isHidden: boolean }>`
|
export const BrowserWrapper = styled.div<{ isHidden: boolean }>`
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@ -61,13 +73,16 @@ export default function AddTagsTermsModal({
|
|||||||
const [disableAdd, setDisableAdd] = useState(false);
|
const [disableAdd, setDisableAdd] = useState(false);
|
||||||
const [urns, setUrns] = useState<string[]>([]);
|
const [urns, setUrns] = useState<string[]>([]);
|
||||||
const [selectedTerms, setSelectedTerms] = useState<any[]>([]);
|
const [selectedTerms, setSelectedTerms] = useState<any[]>([]);
|
||||||
|
const [selectedTags, setSelectedTags] = useState<any[]>([]);
|
||||||
const [isFocusedOnInput, setIsFocusedOnInput] = useState(false);
|
const [isFocusedOnInput, setIsFocusedOnInput] = useState(false);
|
||||||
|
|
||||||
const [addTagsMutation] = useAddTagsMutation();
|
const [addTagsMutation] = useAddTagsMutation();
|
||||||
const [addTermsMutation] = useAddTermsMutation();
|
const [addTermsMutation] = useAddTermsMutation();
|
||||||
|
|
||||||
const [tagTermSearch, { data: tagTermSearchData }] = useGetSearchResultsLazyQuery();
|
const [tagTermSearch, { data: tagTermSearchData }] = useGetSearchResultsLazyQuery();
|
||||||
const tagSearchResults = tagTermSearchData?.search?.searchResults || [];
|
const tagSearchResults = tagTermSearchData?.search?.searchResults?.map((searchResult) => searchResult.entity) || [];
|
||||||
|
const [recommendedData] = useGetRecommendations([EntityType.Tag]);
|
||||||
|
const inputEl = useRef(null);
|
||||||
|
|
||||||
const handleSearch = (text: string) => {
|
const handleSearch = (text: string) => {
|
||||||
if (text.length > 0) {
|
if (text.length > 0) {
|
||||||
@ -84,39 +99,40 @@ export default function AddTagsTermsModal({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderSearchResult = (result: SearchResult) => {
|
const renderSearchResult = (entity: Entity) => {
|
||||||
const displayName =
|
const displayName =
|
||||||
result.entity.type === EntityType.Tag
|
entity.type === EntityType.Tag ? (entity as Tag).name : entityRegistry.getDisplayName(entity.type, entity);
|
||||||
? (result.entity as Tag).name
|
|
||||||
: entityRegistry.getDisplayName(result.entity.type, result.entity);
|
|
||||||
const tagOrTermComponent =
|
const tagOrTermComponent =
|
||||||
result.entity.type === EntityType.Tag ? (
|
entity.type === EntityType.Tag ? (
|
||||||
<TagLabel
|
<TagLabel
|
||||||
name={displayName}
|
name={displayName}
|
||||||
colorHash={(result.entity as Tag).urn}
|
colorHash={(entity as Tag).urn}
|
||||||
color={(result.entity as Tag).properties?.colorHex}
|
color={(entity as Tag).properties?.colorHex}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TermLabel name={displayName} />
|
<TermLabel name={displayName} />
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Select.Option value={result.entity.urn} key={result.entity.urn} name={displayName}>
|
<Select.Option value={entity.urn} key={entity.urn} name={displayName}>
|
||||||
{tagOrTermComponent}
|
{tagOrTermComponent}
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const tagSearchOptions = tagSearchResults.map((result) => {
|
const tagResult =
|
||||||
|
(!inputValue || inputValue.length === 0) && type === EntityType.Tag ? recommendedData : tagSearchResults;
|
||||||
|
|
||||||
|
const tagSearchOptions = tagResult?.map((result) => {
|
||||||
return renderSearchResult(result);
|
return renderSearchResult(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
const inputExistsInTagSearch = tagSearchResults.some((result: SearchResult) => {
|
const inputExistsInTagSearch = tagSearchResults.some((entity: Entity) => {
|
||||||
const displayName = entityRegistry.getDisplayName(result.entity.type, result.entity);
|
const displayName = entityRegistry.getDisplayName(entity.type, entity);
|
||||||
return displayName.toLowerCase() === inputValue.toLowerCase();
|
return displayName.toLowerCase() === inputValue.toLowerCase();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!inputExistsInTagSearch && inputValue.length > 0 && type === EntityType.Tag && urns.length === 0) {
|
if (!inputExistsInTagSearch && inputValue.length > 0 && type === EntityType.Tag && urns.length === 0) {
|
||||||
tagSearchOptions.push(
|
tagSearchOptions?.push(
|
||||||
<Select.Option value={CREATE_TAG_VALUE} key={CREATE_TAG_VALUE}>
|
<Select.Option value={CREATE_TAG_VALUE} key={CREATE_TAG_VALUE}>
|
||||||
<Typography.Link> Create {inputValue}</Typography.Link>
|
<Typography.Link> Create {inputValue}</Typography.Link>
|
||||||
</Select.Option>,
|
</Select.Option>,
|
||||||
@ -125,32 +141,20 @@ export default function AddTagsTermsModal({
|
|||||||
|
|
||||||
const tagRender = (props) => {
|
const tagRender = (props) => {
|
||||||
// eslint-disable-next-line react/prop-types
|
// eslint-disable-next-line react/prop-types
|
||||||
const { label, closable, onClose, value } = props;
|
const { closable, onClose, value } = props;
|
||||||
const onPreventMouseDown = (event) => {
|
const onPreventMouseDown = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
};
|
};
|
||||||
const selectedItem =
|
const selectedItem =
|
||||||
type === EntityType.GlossaryTerm ? selectedTerms.find((term) => term.urn === value).component : label;
|
type === EntityType.GlossaryTerm
|
||||||
|
? selectedTerms.find((term) => term.urn === value).component
|
||||||
|
: selectedTags.find((term) => term.urn === value).component;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomTag
|
<StyleTag onMouseDown={onPreventMouseDown} closable={closable} onClose={onClose}>
|
||||||
onMouseDown={onPreventMouseDown}
|
|
||||||
closable={closable}
|
|
||||||
onClose={onClose}
|
|
||||||
style={{
|
|
||||||
marginRight: 3,
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'start',
|
|
||||||
alignItems: 'center',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
opacity: 1,
|
|
||||||
color: '#434343',
|
|
||||||
lineHeight: '16px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{selectedItem}
|
{selectedItem}
|
||||||
</CustomTag>
|
</StyleTag>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -179,9 +183,26 @@ export default function AddTagsTermsModal({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newUrns = [...(urns || []), urn];
|
const newUrns = [...(urns || []), urn];
|
||||||
|
const selectedSearchOption = tagSearchOptions?.find((option) => option.props.value === urn);
|
||||||
|
const selectedTagOption = tagResult?.find((tag) => tag.urn === urn);
|
||||||
setUrns(newUrns);
|
setUrns(newUrns);
|
||||||
const selectedSearchOption = tagSearchOptions.find((option) => option.props.value === urn);
|
|
||||||
setSelectedTerms([...selectedTerms, { urn, component: <TermLabel name={selectedSearchOption?.props.name} /> }]);
|
setSelectedTerms([...selectedTerms, { urn, component: <TermLabel name={selectedSearchOption?.props.name} /> }]);
|
||||||
|
setSelectedTags([
|
||||||
|
...selectedTags,
|
||||||
|
{
|
||||||
|
urn,
|
||||||
|
component: (
|
||||||
|
<TagLabel
|
||||||
|
name={selectedSearchOption?.props.name}
|
||||||
|
colorHash={(selectedTagOption as Tag).urn}
|
||||||
|
color={(selectedTagOption as Tag).properties?.colorHex}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
if (inputEl && inputEl.current) {
|
||||||
|
(inputEl.current as any).blur();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// When a Tag or term search result is deselected, remove the urn from the Owners
|
// When a Tag or term search result is deselected, remove the urn from the Owners
|
||||||
@ -191,6 +212,7 @@ export default function AddTagsTermsModal({
|
|||||||
setInputValue('');
|
setInputValue('');
|
||||||
setIsFocusedOnInput(true);
|
setIsFocusedOnInput(true);
|
||||||
setSelectedTerms(selectedTerms.filter((term) => term.urn !== urn));
|
setSelectedTerms(selectedTerms.filter((term) => term.urn !== urn));
|
||||||
|
setSelectedTags(selectedTags.filter((term) => term.urn !== urn));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to handle the modal action's
|
// Function to handle the modal action's
|
||||||
@ -313,7 +335,9 @@ export default function AddTagsTermsModal({
|
|||||||
<ClickOutside onClickOutside={() => setIsFocusedOnInput(false)}>
|
<ClickOutside onClickOutside={() => setIsFocusedOnInput(false)}>
|
||||||
<TagSelect
|
<TagSelect
|
||||||
autoFocus
|
autoFocus
|
||||||
|
defaultOpen
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
|
ref={inputEl}
|
||||||
filterOption={false}
|
filterOption={false}
|
||||||
placeholder={`Search for ${entityRegistry.getEntityName(type)?.toLowerCase()}...`}
|
placeholder={`Search for ${entityRegistry.getEntityName(type)?.toLowerCase()}...`}
|
||||||
showSearch
|
showSearch
|
||||||
@ -331,7 +355,7 @@ export default function AddTagsTermsModal({
|
|||||||
onClear={clearInput}
|
onClear={clearInput}
|
||||||
onFocus={() => setIsFocusedOnInput(true)}
|
onFocus={() => setIsFocusedOnInput(true)}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
dropdownStyle={isShowingGlossaryBrowser || !inputValue ? { display: 'none' } : {}}
|
dropdownStyle={isShowingGlossaryBrowser ? { display: 'none' } : {}}
|
||||||
>
|
>
|
||||||
{tagSearchOptions}
|
{tagSearchOptions}
|
||||||
</TagSelect>
|
</TagSelect>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user