mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-26 01:15:08 +00:00
Fix(ui) : search functionality for domain edit in user profile (#22005)
* support search for domain edit in user profile * handle search for persona edit in profile page * fix domain update issue * added test
This commit is contained in:
parent
6077e7348b
commit
64f09e8614
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
import { expect, Page, test as base } from '@playwright/test';
|
||||
import { Domain } from '../../support/domain/Domain';
|
||||
import { TeamClass } from '../../support/team/TeamClass';
|
||||
import { AdminClass } from '../../support/user/AdminClass';
|
||||
import { UserClass } from '../../support/user/UserClass';
|
||||
@ -22,6 +23,14 @@ import { redirectToUserPage } from '../../utils/userDetails';
|
||||
const user1 = new UserClass();
|
||||
const user2 = new UserClass();
|
||||
const admin = new AdminClass();
|
||||
const domain = new Domain({
|
||||
name: `PW%domain`,
|
||||
displayName: `PWDomain`,
|
||||
description: 'playwright domain description',
|
||||
domainType: 'Aggregate',
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
fullyQualifiedName: `PW%domain`,
|
||||
});
|
||||
const team = new TeamClass({
|
||||
name: `a-new-team-${uuid()}`,
|
||||
displayName: `A New Team ${uuid()}`,
|
||||
@ -56,6 +65,7 @@ test.describe('User with different Roles', () => {
|
||||
await user2.create(apiContext);
|
||||
|
||||
await team.create(apiContext);
|
||||
await domain.create(apiContext);
|
||||
|
||||
await afterAction();
|
||||
});
|
||||
@ -67,6 +77,7 @@ test.describe('User with different Roles', () => {
|
||||
await user2.delete(apiContext);
|
||||
|
||||
await team.delete(apiContext);
|
||||
await domain.delete(apiContext);
|
||||
|
||||
await afterAction();
|
||||
});
|
||||
@ -98,6 +109,33 @@ test.describe('User with different Roles', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('User can search for a domain', async ({ adminPage }) => {
|
||||
await redirectToUserPage(adminPage);
|
||||
|
||||
await expect(adminPage.getByTestId('edit-domains')).toBeVisible();
|
||||
|
||||
await adminPage.getByTestId('edit-domains').click();
|
||||
|
||||
await expect(adminPage.locator('.custom-domain-edit-select')).toBeVisible();
|
||||
|
||||
await adminPage.locator('.custom-domain-edit-select').click();
|
||||
|
||||
const searchPromise = adminPage.waitForResponse('/api/v1/search/query?q=*');
|
||||
await adminPage
|
||||
.locator('.custom-domain-edit-select .ant-select-selection-search-input')
|
||||
.fill('PWDomain');
|
||||
|
||||
await searchPromise;
|
||||
|
||||
await adminPage.waitForSelector('.domain-custom-dropdown-class', {
|
||||
state: 'visible',
|
||||
});
|
||||
|
||||
await expect(
|
||||
adminPage.locator('.domain-custom-dropdown-class')
|
||||
).toContainText('PWDomain');
|
||||
});
|
||||
|
||||
test('Admin user can get all the roles hierarchy and edit roles', async ({
|
||||
adminPage,
|
||||
}) => {
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
import { Button, Empty, Select, Space, Tree } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { debounce } from 'lodash';
|
||||
import React, {
|
||||
FC,
|
||||
Key,
|
||||
@ -25,16 +26,21 @@ import { ReactComponent as IconDown } from '../../../assets/svg/ic-arrow-down.sv
|
||||
import { ReactComponent as IconRight } from '../../../assets/svg/ic-arrow-right.svg';
|
||||
import { ReactComponent as ClosePopoverIcon } from '../../../assets/svg/ic-popover-close.svg';
|
||||
import { ReactComponent as SavePopoverIcon } from '../../../assets/svg/ic-popover-save.svg';
|
||||
import { DEBOUNCE_TIMEOUT } from '../../../constants/Lineage.constants';
|
||||
import { EntityType } from '../../../enums/entity.enum';
|
||||
import { Domain } from '../../../generated/entity/domains/domain';
|
||||
import { EntityReference } from '../../../generated/tests/testCase';
|
||||
import { listDomainHierarchy } from '../../../rest/domainAPI';
|
||||
import { listDomainHierarchy, searchDomains } from '../../../rest/domainAPI';
|
||||
import {
|
||||
convertDomainsToTreeOptions,
|
||||
isDomainExist,
|
||||
} from '../../../utils/DomainUtils';
|
||||
import { getEntityReferenceFromEntity } from '../../../utils/EntityUtils';
|
||||
import { findItemByFqn } from '../../../utils/GlossaryUtils';
|
||||
import {
|
||||
escapeESReservedCharacters,
|
||||
getEncodedFqn,
|
||||
} from '../../../utils/StringsUtils';
|
||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
import Loader from '../Loader/Loader';
|
||||
import { TagRenderer } from '../TagRenderer/TagRenderer';
|
||||
@ -56,7 +62,7 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [treeData, setTreeData] = useState<TreeListItem[]>([]);
|
||||
const [domains, setDomains] = useState<Domain[]>([]);
|
||||
const [allDomains, setAllDomains] = useState<Domain[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [isSubmitLoading, setIsSubmitLoading] = useState(false);
|
||||
const [selectedDomains, setSelectedDomains] = useState<Domain[]>([]);
|
||||
@ -64,7 +70,7 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
|
||||
const handleMultiDomainSave = async () => {
|
||||
const selectedFqns = selectedDomains
|
||||
.map((domain) => domain.fullyQualifiedName)
|
||||
.map((domain) => domain?.fullyQualifiedName)
|
||||
.sort((a, b) => (a ?? '').localeCompare(b ?? ''));
|
||||
const initialFqns = (value as string[]).sort((a, b) => a.localeCompare(b));
|
||||
|
||||
@ -109,7 +115,7 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
const combinedData = [...data.data];
|
||||
initialDomains?.forEach((selectedDomain) => {
|
||||
const exists = combinedData.some((domain: Domain) =>
|
||||
isDomainExist(domain, selectedDomain.fullyQualifiedName ?? '')
|
||||
isDomainExist(domain, selectedDomain?.fullyQualifiedName ?? '')
|
||||
);
|
||||
if (!exists) {
|
||||
combinedData.push(selectedDomain as unknown as Domain);
|
||||
@ -117,7 +123,7 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
});
|
||||
|
||||
setTreeData(convertDomainsToTreeOptions(combinedData, 0, isMultiple));
|
||||
setDomains(combinedData);
|
||||
setAllDomains(combinedData);
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
} finally {
|
||||
@ -130,7 +136,7 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
const selectedData = [];
|
||||
for (const item of selectedKeys) {
|
||||
selectedData.push(
|
||||
findItemByFqn(domains, item as string, false) as Domain
|
||||
findItemByFqn(allDomains, item as string, false) as Domain
|
||||
);
|
||||
}
|
||||
|
||||
@ -145,14 +151,14 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
const selectedData = [];
|
||||
for (const item of checked) {
|
||||
selectedData.push(
|
||||
findItemByFqn(domains, item as string, false) as Domain
|
||||
findItemByFqn(allDomains, item as string, false) as Domain
|
||||
);
|
||||
}
|
||||
|
||||
setSelectedDomains(selectedData);
|
||||
} else {
|
||||
const selected = checked.checked.map(
|
||||
(item) => findItemByFqn(domains, item as string, false) as Domain
|
||||
(item) => findItemByFqn(allDomains, item as string, false) as Domain
|
||||
);
|
||||
|
||||
setSelectedDomains(selected);
|
||||
@ -163,6 +169,32 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
return expanded ? <IconDown /> : <IconRight />;
|
||||
}, []);
|
||||
|
||||
const onSearch = debounce(async (value: string) => {
|
||||
setSearchTerm(value);
|
||||
if (value) {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const encodedValue = getEncodedFqn(escapeESReservedCharacters(value));
|
||||
const results: Domain[] = await searchDomains(encodedValue);
|
||||
const updatedTreeData = convertDomainsToTreeOptions(
|
||||
results,
|
||||
0,
|
||||
isMultiple
|
||||
);
|
||||
setTreeData(updatedTreeData);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
} else {
|
||||
const updatedTreeData = convertDomainsToTreeOptions(
|
||||
allDomains,
|
||||
0,
|
||||
isMultiple
|
||||
);
|
||||
setTreeData(updatedTreeData);
|
||||
}
|
||||
}, DEBOUNCE_TIMEOUT);
|
||||
|
||||
const treeContent = useMemo(() => {
|
||||
if (isLoading) {
|
||||
return <Loader />;
|
||||
@ -206,7 +238,7 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
}, [visible]);
|
||||
const handleSelectChange = (selectedFqns: string[]) => {
|
||||
const selectedData = selectedFqns.map(
|
||||
(fqn) => findItemByFqn(domains, fqn, false) as Domain
|
||||
(fqn) => findItemByFqn(allDomains, fqn, false) as Domain
|
||||
);
|
||||
setSelectedDomains(selectedData);
|
||||
};
|
||||
@ -215,11 +247,11 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
setSelectedDomains(initialDomains as unknown as Domain[]);
|
||||
} else if (value) {
|
||||
const selectedData = (value as string[]).map(
|
||||
(fqn) => findItemByFqn(domains, fqn, false) as Domain
|
||||
(fqn) => findItemByFqn(allDomains, fqn, false) as Domain
|
||||
);
|
||||
setSelectedDomains(selectedData);
|
||||
}
|
||||
}, [initialDomains, value, domains]);
|
||||
}, [initialDomains, value, allDomains]);
|
||||
|
||||
return (
|
||||
<div data-testid="domain-selectable-tree" style={{ width: '339px' }}>
|
||||
@ -228,6 +260,7 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
className="custom-domain-edit-select"
|
||||
dropdownRender={() => treeContent}
|
||||
dropdownStyle={{ maxHeight: '200px' }}
|
||||
filterOption={false}
|
||||
maxTagCount={3}
|
||||
maxTagPlaceholder={(omittedValues) => (
|
||||
<span className="max-tag-text">
|
||||
@ -235,9 +268,9 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
</span>
|
||||
)}
|
||||
mode={isMultiple ? 'multiple' : undefined}
|
||||
options={domains.map((domain) => ({
|
||||
value: domain.fullyQualifiedName,
|
||||
label: domain.name,
|
||||
options={allDomains.map((domain) => ({
|
||||
value: domain?.fullyQualifiedName,
|
||||
label: domain?.name,
|
||||
}))}
|
||||
placeholder="Select a domain"
|
||||
popupClassName="domain-custom-dropdown-class"
|
||||
@ -245,11 +278,12 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
tagRender={TagRenderer}
|
||||
value={
|
||||
selectedDomains
|
||||
?.map((domain) => domain.fullyQualifiedName)
|
||||
?.map((domain) => domain?.fullyQualifiedName)
|
||||
.filter(Boolean) as string[]
|
||||
}
|
||||
onChange={handleSelectChange}
|
||||
onDropdownVisibleChange={handleDropdownChange}
|
||||
onSearch={onSearch}
|
||||
/>
|
||||
</div>
|
||||
<Space className="d-flex" size={8}>
|
||||
|
@ -40,3 +40,7 @@
|
||||
.all-domain-container.selected-node {
|
||||
background-color: @primary-1;
|
||||
}
|
||||
.custom-domain-edit-select .ant-select-selection-placeholder,
|
||||
.custom-domain-edit-select .ant-select-selection-search-input::placeholder {
|
||||
padding-left: @size-xs;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user