diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts index bafbd1434cd..5873f18fcf1 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts @@ -211,10 +211,14 @@ entities.forEach((EntityClass) => { }); test('Tag Add, Update and Remove', async ({ page }) => { + test.slow(true); + await entity.tag(page, 'PersonalData.Personal', 'PII.None'); }); test('Glossary Term Add, Update and Remove', async ({ page }) => { + test.slow(true); + await entity.glossaryTerm( page, EntityDataClass.glossaryTerm1.responseData, diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityClass.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityClass.ts index bd9c21b2f46..e34ec5b6eb3 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityClass.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/EntityClass.ts @@ -224,7 +224,7 @@ export class EntityClass { await page .getByTestId('KnowledgePanel.Tags') .getByTestId('tags-container') - .getByTestId('Add') + .getByTestId('add-tag') .isVisible(); } @@ -265,7 +265,7 @@ export class EntityClass { await page .locator(`[${rowSelector}="${rowId}"]`) .getByTestId('tags-container') - .getByTestId('Add') + .getByTestId('add-tag') .isVisible(); } @@ -281,7 +281,7 @@ export class EntityClass { await page .getByTestId('KnowledgePanel.GlossaryTerms') .getByTestId('glossary-container') - .getByTestId('Add') + .getByTestId('add-tag') .isVisible(); } @@ -321,7 +321,7 @@ export class EntityClass { await page .locator(`[${rowSelector}="${rowId}"]`) .getByTestId('glossary-container') - .getByTestId('Add') + .getByTestId('add-tag') .isVisible(); } diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts index 5937c90bfb4..f4927a96453 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/user.ts @@ -532,14 +532,14 @@ export const checkDataConsumerPermissions = async (page: Page) => { // Check right panel add tags button await expect( page.locator( - '[data-testid="KnowledgePanel.Tags"] [data-testid="tags-container"] [data-testid="entity-tags"] .tag-chip-add-button' + '[data-testid="KnowledgePanel.Tags"] [data-testid="tags-container"] [data-testid="add-tag"]' ) ).toBeVisible(); // Check right panel add glossary term button await expect( page.locator( - '[data-testid="KnowledgePanel.GlossaryTerms"] [data-testid="glossary-container"] [data-testid="entity-tags"] .tag-chip-add-button' + '[data-testid="KnowledgePanel.GlossaryTerms"] [data-testid="glossary-container"] [data-testid="add-tag"]' ) ).toBeVisible(); @@ -617,14 +617,14 @@ export const checkStewardPermissions = async (page: Page) => { // Check right panel add tags button await expect( page.locator( - '[data-testid="KnowledgePanel.Tags"] [data-testid="tags-container"] [data-testid="entity-tags"] .tag-chip-add-button' + '[data-testid="KnowledgePanel.Tags"] [data-testid="tags-container"] [data-testid="add-tag"]' ) ).toBeVisible(); // Check right panel add glossary term button await expect( page.locator( - '[data-testid="KnowledgePanel.GlossaryTerms"] [data-testid="glossary-container"] [data-testid="entity-tags"] .tag-chip-add-button' + '[data-testid="KnowledgePanel.GlossaryTerms"] [data-testid="glossary-container"] [data-testid="add-tag"]' ) ).toBeVisible(); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/OwnerLabelV2/OwnerLabelV2.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/OwnerLabelV2/OwnerLabelV2.tsx index 67b6cbce27e..4ef1c98de4a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/OwnerLabelV2/OwnerLabelV2.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/OwnerLabelV2/OwnerLabelV2.tsx @@ -14,13 +14,14 @@ import { Typography } from 'antd'; import { t } from 'i18next'; import { isEmpty } from 'lodash'; import React, { useMemo } from 'react'; -import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg'; import { TabSpecificField } from '../../../enums/entity.enum'; import { EntityReference } from '../../../generated/entity/type'; import { getOwnerVersionLabel } from '../../../utils/EntityVersionUtils'; import ExpandableCard from '../../common/ExpandableCard/ExpandableCard'; -import { EditIconButton } from '../../common/IconButtons/EditIconButton'; -import TagButton from '../../common/TagButton/TagButton.component'; +import { + EditIconButton, + PlusIconButton, +} from '../../common/IconButtons/EditIconButton'; import { UserTeamSelectableList } from '../../common/UserTeamSelectableList/UserTeamSelectableList.component'; import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider'; @@ -48,15 +49,22 @@ export const OwnerLabelV2 = < {t('label.owner-plural')} - {(permissions.EditOwners || permissions.EditAll) && - data.owners && - data.owners.length > 0 && ( - + {(permissions.EditOwners || permissions.EditAll) && ( + + {isEmpty(data.owners) ? ( + + ) : ( - - )} + )} + + )} ), [data, permissions, handleUpdatedOwner] @@ -85,24 +94,6 @@ export const OwnerLabelV2 = < TabSpecificField.OWNERS, permissions.EditOwners || permissions.EditAll )} - - {data.owners?.length === 0 && - (permissions.EditOwners || permissions.EditAll) && ( - handleUpdatedOwner(updatedUser)}> - } - label={t('label.add')} - tooltip="" - /> - - )} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/ReviewerLabelV2/ReviewerLabelV2.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/ReviewerLabelV2/ReviewerLabelV2.tsx index 8c2c12072c4..d429adb08e3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/ReviewerLabelV2/ReviewerLabelV2.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/ReviewerLabelV2/ReviewerLabelV2.tsx @@ -13,14 +13,15 @@ import { Typography } from 'antd'; import { t } from 'i18next'; import React, { useMemo } from 'react'; -import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg'; import { TabSpecificField } from '../../../enums/entity.enum'; import { EntityReference } from '../../../generated/entity/type'; import { ChangeDescription } from '../../../generated/type/changeEvent'; import { getOwnerVersionLabel } from '../../../utils/EntityVersionUtils'; import ExpandableCard from '../../common/ExpandableCard/ExpandableCard'; -import { EditIconButton } from '../../common/IconButtons/EditIconButton'; -import TagButton from '../../common/TagButton/TagButton.component'; +import { + EditIconButton, + PlusIconButton, +} from '../../common/IconButtons/EditIconButton'; import { UserTeamSelectableList } from '../../common/UserTeamSelectableList/UserTeamSelectableList.component'; import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider'; @@ -70,7 +71,7 @@ export const ReviewerLabelV2 = < data-testid="heading-name"> {t('label.reviewer-plural')} - {hasEditReviewerAccess && hasReviewers && ( + {hasEditReviewerAccess && ( - + {hasReviewers ? ( + + ) : ( + + )} )} @@ -102,33 +113,16 @@ export const ReviewerLabelV2 = < }} dataTestId="glossary-reviewer" isExpandDisabled={!hasReviewers}> -
- {getOwnerVersionLabel( - data, - isVersionView ?? false, - TabSpecificField.REVIEWERS, - hasEditReviewerAccess - )} -
- - {hasEditReviewerAccess && !hasReviewers && ( - - } - label={t('label.add')} - tooltip="" - /> - - )} + {hasReviewers ? ( +
+ {getOwnerVersionLabel( + data, + isVersionView ?? false, + TabSpecificField.REVIEWERS, + hasEditReviewerAccess + )} +
+ ) : null} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductSelectForm/DataProductsSelectForm.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductSelectForm/DataProductsSelectForm.interface.ts deleted file mode 100644 index de0ba7d4db3..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductSelectForm/DataProductsSelectForm.interface.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 { DataProduct } from '../../../generated/entity/domains/dataProduct'; -import { Paging } from '../../../generated/type/paging'; -import { DataProductSelectOption } from '../DataProductsSelectList/DataProductSelectList.interface'; - -export type DataProductsSelectFormProps = { - placeholder: string; - defaultValue: string[]; - onChange?: (value: string[]) => void; - onSubmit: (values: DataProduct[]) => Promise; - onCancel: () => void; - fetchApi: ( - search: string, - page: number - ) => Promise<{ - data: DataProductSelectOption[]; - paging: Paging; - }>; -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductSelectForm/DataProductsSelectForm.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductSelectForm/DataProductsSelectForm.test.tsx deleted file mode 100644 index 9ad47480937..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductSelectForm/DataProductsSelectForm.test.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 { render } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import DataProductsSelectForm from './DataProductsSelectForm'; - -const mockOnSubmit = jest.fn(); -const mockOnCancel = jest.fn(); - -describe('Data Products Form Page', () => { - it('renders without errors', () => { - const { getByTestId } = render( - Promise.resolve({ data: [], paging: { total: 0 } })} - placeholder="Select products" - onCancel={mockOnCancel} - onSubmit={mockOnSubmit} - /> - ); - - // Ensure that the component renders without errors - expect(getByTestId('data-product-selector')).toBeInTheDocument(); - }); - - it('calls onCancel function when Cancel button is clicked', () => { - const { getByTestId } = render( - Promise.resolve({ data: [], paging: { total: 0 } })} - placeholder="Select products" - onCancel={mockOnCancel} - onSubmit={mockOnSubmit} - /> - ); - - const cancelButton = getByTestId('cancelAssociatedTag'); - userEvent.click(cancelButton); - - // Ensure that the onCancel function is called when the Cancel button is clicked - expect(mockOnCancel).toHaveBeenCalledTimes(1); - }); - - it('calls onSubmit when the Save button is clicked', () => { - const { getByTestId } = render( - Promise.resolve({ data: [], paging: { total: 0 } })} - placeholder="Select products" - onCancel={mockOnCancel} - onSubmit={mockOnSubmit} - /> - ); - const selectRef = getByTestId('data-product-selector').querySelector( - '.ant-select-selector' - ); - if (selectRef) { - userEvent.click(selectRef); - } - - // Simulate the Save button click - userEvent.click(getByTestId('saveAssociatedTag')); - - // Check if onSubmit was called with the selected value - expect(mockOnSubmit).toHaveBeenCalledTimes(1); - expect(mockOnSubmit).toHaveBeenCalledWith(expect.any(Array)); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductSelectForm/DataProductsSelectForm.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductSelectForm/DataProductsSelectForm.tsx deleted file mode 100644 index 023eed41948..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductSelectForm/DataProductsSelectForm.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 { CheckOutlined, CloseOutlined } from '@ant-design/icons'; -import { Button, Col, Row, Space } from 'antd'; -import React, { useRef, useState } from 'react'; - -import { DataProductsSelectRef } from '../DataProductsSelectList/DataProductSelectList.interface'; -import DataProductsSelectList from '../DataProductsSelectList/DataProductsSelectList'; -import { DataProductsSelectFormProps } from './DataProductsSelectForm.interface'; - -const DataProductsSelectForm = ({ - fetchApi, - defaultValue, - placeholder, - onSubmit, - onCancel, -}: DataProductsSelectFormProps) => { - const [isSubmitLoading, setIsSubmitLoading] = useState(false); - const selectRef = useRef(null); - - const onSave = () => { - setIsSubmitLoading(true); - const value = selectRef.current?.getSelectValue() ?? []; - onSubmit(value); - }; - - return ( - - - - + + + + ); - const onSelectChange = (value: string[]) => { - const entityObj = value.reduce((result: DataProduct[], item) => { - const option = options.find((option) => option.label === item); - if (option) { - result.push(option.value); - } + const onSelectChange = (value: string[]) => { + const entityObj = value.reduce((result: DataProduct[], item) => { + const option = options.find((option) => option.label === item); + if (option) { + result.push(option.value); + } - return result; - }, []); + return result; + }, []); - setSelectedValue(entityObj as DataProduct[]); - }; + setSelectedValue(entityObj as DataProduct[]); + }; - useImperativeHandle(ref, () => ({ - getSelectValue() { - return selectedValue; - }, - })); - - return ( - - ); - } -); + return ( + + ); +}; export default DataProductsSelectList; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/TableQueries/TableQueryRightPanel/TableQueryRightPanel.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/TableQueries/TableQueryRightPanel/TableQueryRightPanel.component.tsx index 8704dd6b801..232fc186eaa 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/TableQueries/TableQueryRightPanel/TableQueryRightPanel.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/TableQueries/TableQueryRightPanel/TableQueryRightPanel.component.tsx @@ -12,24 +12,24 @@ */ import Icon from '@ant-design/icons'; -import { Button, Col, Drawer, Row, Space, Tooltip, Typography } from 'antd'; +import { Col, Drawer, Row, Space, Typography } from 'antd'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; -import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg'; import { ReactComponent as IconUser } from '../../../../assets/svg/user.svg'; -import { DE_ACTIVE_COLOR } from '../../../../constants/constants'; import { EntityType } from '../../../../enums/entity.enum'; import { Query } from '../../../../generated/entity/data/query'; -import { TagLabel } from '../../../../generated/type/tagLabel'; +import { TagLabel, TagSource } from '../../../../generated/type/tagLabel'; import { getEntityName } from '../../../../utils/EntityUtils'; import { getUserPath } from '../../../../utils/RouterUtils'; import DescriptionV1 from '../../../common/EntityDescription/DescriptionV1'; +import ExpandableCard from '../../../common/ExpandableCard/ExpandableCard'; +import { EditIconButton } from '../../../common/IconButtons/EditIconButton'; import Loader from '../../../common/Loader/Loader'; import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component'; import ProfilePicture from '../../../common/ProfilePicture/ProfilePicture'; import { UserTeamSelectableList } from '../../../common/UserTeamSelectableList/UserTeamSelectableList.component'; -import TagsInput from '../../../TagsInput/TagsInput.component'; +import TagsContainerV2 from '../../../Tag/TagsContainerV2/TagsContainerV2'; import { TableQueryRightPanelProps } from './TableQueryRightPanel.interface'; const TableQueryRightPanel = ({ @@ -79,66 +79,72 @@ const TableQueryRightPanel = ({ {isLoading ? ( ) : ( - + - - - - {t('label.owner-plural')} - + + + {t('label.owner-plural')} + - {(EditAll || EditOwners) && ( - - handleUpdateOwner(updatedUsers) - }> - - diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/TableTags/TableTags.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/TableTags/TableTags.component.tsx index 9bad9a7a2a2..a6e6ef9782f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/TableTags/TableTags.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/TableTags/TableTags.component.tsx @@ -51,7 +51,6 @@ const TableTags = ({ entityType={entityType} permission={hasTagEditAccess && !isReadOnly} selectedTags={tags} - showHeader={false} showInlineEditButton={showInlineEditTagButton} sizeCap={TAG_LIST_SIZE} tagType={type} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainExpertsWidget/DomainExpertWidget.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainExpertsWidget/DomainExpertWidget.tsx index dc6984f2fe5..1462d5fda96 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainExpertsWidget/DomainExpertWidget.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainExpertsWidget/DomainExpertWidget.tsx @@ -15,14 +15,15 @@ import classNames from 'classnames'; import { t } from 'i18next'; import { cloneDeep, includes, isEmpty, isEqual } from 'lodash'; import { default as React, useMemo } from 'react'; -import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg'; import { TabSpecificField } from '../../../enums/entity.enum'; import { Domain } from '../../../generated/entity/domains/domain'; import { EntityReference } from '../../../generated/tests/testCase'; import { getOwnerVersionLabel } from '../../../utils/EntityVersionUtils'; import ExpandableCard from '../../common/ExpandableCard/ExpandableCard'; -import { EditIconButton } from '../../common/IconButtons/EditIconButton'; -import TagButton from '../../common/TagButton/TagButton.component'; +import { + EditIconButton, + PlusIconButton, +} from '../../common/IconButtons/EditIconButton'; import { UserSelectableList } from '../../common/UserSelectableList/UserSelectableList.component'; import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider'; @@ -69,53 +70,44 @@ export const DomainExpertWidget = () => { data-testid="domain-expert-heading-name"> {t('label.expert-plural')} - {editOwnerPermission && domain.experts && domain.experts.length > 0 && ( + {editOwnerPermission && ( - + {isEmpty(domain.experts) ? ( + + ) : ( + + )} )} ); - const content = ( - <> -
- {getOwnerVersionLabel( - domain, - isVersionView ?? false, - TabSpecificField.EXPERTS, - editAllPermission - )} -
- -
- {editOwnerPermission && domain.experts?.length === 0 && ( - - } - label={t('label.add')} - tooltip="" - /> - - )} -
- + const content = isEmpty(domain.experts) ? null : ( +
+ {getOwnerVersionLabel( + domain, + isVersionView ?? false, + TabSpecificField.EXPERTS, + editAllPermission + )} +
); return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx index e3698f7796e..7a9d0784334 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx @@ -15,7 +15,6 @@ import { Space, Typography } from 'antd'; import { t } from 'i18next'; import { cloneDeep, isEmpty, isEqual } from 'lodash'; import React, { useCallback, useEffect, useState } from 'react'; -import { ReactComponent as PlusIcon } from '../../../../assets/svg/plus-primary.svg'; import { NO_DATA_PLACEHOLDER } from '../../../../constants/constants'; import { EntityField } from '../../../../constants/Feeds.constants'; import { @@ -30,8 +29,10 @@ import { } from '../../../../utils/EntityVersionUtils'; import { renderReferenceElement } from '../../../../utils/GlossaryUtils'; import ExpandableCard from '../../../common/ExpandableCard/ExpandableCard'; -import { EditIconButton } from '../../../common/IconButtons/EditIconButton'; -import TagButton from '../../../common/TagButton/TagButton.component'; +import { + EditIconButton, + PlusIconButton, +} from '../../../common/IconButtons/EditIconButton'; import { useGenericContext } from '../../../Customization/GenericProvider/GenericProvider'; import GlossaryTermReferencesModal from '../GlossaryTermReferencesModal.component'; @@ -130,47 +131,49 @@ const GlossaryTermReferences = () => { {t('label.reference-plural')} - {references.length > 0 && permissions.EditAll && ( - setIsViewMode(false)} - /> - )} + {permissions.EditAll && + (isEmpty(references) ? ( + { + setIsViewMode(false); + }} + /> + ) : ( + setIsViewMode(false)} + /> + ))}
); return ( - - {isVersionView ? ( - getVersionReferenceElements() - ) : ( -
- {references.map((ref) => renderReferenceElement(ref))} - {permissions.EditAll && references.length === 0 && ( - } - label={t('label.add')} - tooltip="" - onClick={() => { - setIsViewMode(false); - }} - /> - )} - {!permissions.EditAll && references.length === 0 && ( -
{NO_DATA_PLACEHOLDER}
- )} -
- )} + <> + + {isVersionView ? ( + getVersionReferenceElements() + ) : !permissions.EditAll || !isEmpty(references) ? ( +
+ {references.map((ref) => renderReferenceElement(ref))} + {!permissions.EditAll && references.length === 0 && ( +
{NO_DATA_PLACEHOLDER}
+ )} +
+ ) : null} +
{ }} onSave={handleReferencesSave} /> -
+ ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx index 85917efa6cf..a9b351d214c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx @@ -16,7 +16,6 @@ import { Button, Select, Space, Typography } from 'antd'; import { t } from 'i18next'; import { cloneDeep, isEmpty, isEqual } from 'lodash'; import React, { useCallback, useEffect, useState } from 'react'; -import { ReactComponent as PlusIcon } from '../../../../assets/svg/plus-primary.svg'; import { NO_DATA_PLACEHOLDER } from '../../../../constants/constants'; import { EntityField } from '../../../../constants/Feeds.constants'; import { GlossaryTerm } from '../../../../generated/entity/data/glossaryTerm'; @@ -27,7 +26,10 @@ import { getDiffByFieldName, } from '../../../../utils/EntityVersionUtils'; import ExpandableCard from '../../../common/ExpandableCard/ExpandableCard'; -import { EditIconButton } from '../../../common/IconButtons/EditIconButton'; +import { + EditIconButton, + PlusIconButton, +} from '../../../common/IconButtons/EditIconButton'; import TagButton from '../../../common/TagButton/TagButton.component'; import { useGenericContext } from '../../../Customization/GenericProvider/GenericProvider'; @@ -42,32 +44,22 @@ const GlossaryTermSynonyms = () => { permissions, } = useGenericContext(); - const getSynonyms = () => ( -
- {synonyms.map((synonym) => ( - - ))} - {permissions.EditAll && synonyms.length === 0 && ( - } - label={t('label.add')} - tooltip="" - onClick={() => { - setIsViewMode(false); - }} - /> - )} - {!permissions.EditAll && synonyms.length === 0 && ( -
{NO_DATA_PLACEHOLDER}
- )} -
- ); + const getSynonyms = () => + !permissions.EditAll || !isEmpty(synonyms) ? ( +
+ {synonyms.map((synonym) => ( + + ))} + + {!permissions.EditAll && synonyms.length === 0 && ( +
{NO_DATA_PLACEHOLDER}
+ )} +
+ ) : null; const getSynonymsContainer = useCallback(() => { if (!isVersionView) { @@ -174,17 +166,30 @@ const GlossaryTermSynonyms = () => { {t('label.synonym-plural')} - {permissions.EditAll && synonyms.length > 0 && isViewMode && ( - setIsViewMode(false)} - /> - )} + {permissions.EditAll && + isViewMode && + (isEmpty(synonyms) ? ( + { + setIsViewMode(false); + }} + /> + ) : ( + setIsViewMode(false)} + /> + ))} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx index d10704b5ab2..c3e22a53701 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx @@ -18,7 +18,6 @@ import { isArray, isEmpty, isUndefined } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { ReactComponent as IconTerm } from '../../../../assets/svg/book.svg'; -import { ReactComponent as PlusIcon } from '../../../../assets/svg/plus-primary.svg'; import TagSelectForm from '../../../../components/Tag/TagsSelectForm/TagsSelectForm.component'; import { NO_DATA_PLACEHOLDER } from '../../../../constants/constants'; import { EntityField } from '../../../../constants/Feeds.constants'; @@ -41,7 +40,10 @@ import { VersionStatus } from '../../../../utils/EntityVersionUtils.interface'; import { getGlossaryPath } from '../../../../utils/RouterUtils'; import { SelectOption } from '../../../common/AsyncSelectList/AsyncSelectList.interface'; import ExpandableCard from '../../../common/ExpandableCard/ExpandableCard'; -import { EditIconButton } from '../../../common/IconButtons/EditIconButton'; +import { + EditIconButton, + PlusIconButton, +} from '../../../common/IconButtons/EditIconButton'; import TagButton from '../../../common/TagButton/TagButton.component'; import { useGenericContext } from '../../../Customization/GenericProvider/GenericProvider'; @@ -193,21 +195,8 @@ const RelatedTerms = () => { () => isVersionView ? ( getVersionRelatedTerms() - ) : ( + ) : !permissions.EditAll || !isEmpty(selectedOption) ? (
- {permissions.EditAll && selectedOption.length === 0 && ( - } - label={t('label.add')} - tooltip="" - onClick={() => { - setIsIconVisible(false); - }} - /> - )} - {selectedOption.map((entity: EntityReference) => getRelatedTermElement(entity) )} @@ -216,7 +205,7 @@ const RelatedTerms = () => {
{NO_DATA_PLACEHOLDER}
)}
- ), + ) : null, [ permissions, selectedOption, @@ -231,17 +220,29 @@ const RelatedTerms = () => { {t('label.related-term-plural')} - {permissions.EditAll && selectedOption.length > 0 && ( - setIsIconVisible(false)} - /> - )} + {permissions.EditAll && + (isEmpty(selectedOption) ? ( + { + setIsIconVisible(false); + }} + /> + ) : ( + setIsIconVisible(false)} + /> + ))} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Metric/RelatedMetrics/RelatedMetrics.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Metric/RelatedMetrics/RelatedMetrics.tsx index 92f632337c6..c85840e0031 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Metric/RelatedMetrics/RelatedMetrics.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Metric/RelatedMetrics/RelatedMetrics.tsx @@ -10,7 +10,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Button, Col, Row, Space, Typography } from 'antd'; +import { Button, Space, Typography } from 'antd'; import { AxiosError } from 'axios'; import classNames from 'classnames'; import { isEmpty } from 'lodash'; @@ -18,7 +18,6 @@ import React, { FC, useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { NO_DATA_PLACEHOLDER } from '../../../constants/constants'; -import { TAG_CONSTANT, TAG_START_WITH } from '../../../constants/Tag.constants'; import { Metric } from '../../../generated/entity/data/metric'; import { EntityReference } from '../../../generated/type/entityReference'; import entityUtilClassBase from '../../../utils/EntityUtilClassBase'; @@ -26,10 +25,12 @@ import { getEntityName } from '../../../utils/EntityUtils'; import { getEntityIcon } from '../../../utils/TableUtils'; import { showErrorToast } from '../../../utils/ToastUtils'; import ExpandableCard from '../../common/ExpandableCard/ExpandableCard'; -import { EditIconButton } from '../../common/IconButtons/EditIconButton'; +import { + EditIconButton, + PlusIconButton, +} from '../../common/IconButtons/EditIconButton'; import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider'; import { DataAssetOption } from '../../DataAssets/DataAssetAsyncSelectList/DataAssetAsyncSelectList.interface'; -import TagsV1 from '../../Tag/TagsV1/TagsV1.component'; import './related-metrics.less'; import { RelatedMetricsForm } from './RelatedMetricsForm'; @@ -160,59 +161,51 @@ const RelatedMetrics: FC = ({ {t('label.related-metric-plural')} {!isEdit && - !isEmpty(relatedMetrics) && permissions.EditAll && - !metricDetails.deleted && ( + !metricDetails.deleted && + (isEmpty(relatedMetrics) ? ( + setIsEdit(true)} + /> + ) : ( setIsEdit(true)} /> - )} + ))}
); - const content = ( - <> - {isEmpty(relatedMetrics) && - !isEdit && - permissions.EditAll && - !metricDetails.deleted && ( - setIsEdit(true)}> - - - )} - - {isEdit ? ( - setIsEdit(false)} - onSubmit={handleRelatedMetricUpdate} - /> - ) : ( - <> - {isEmpty(relatedMetrics) && - (metricDetails.deleted || isInSummaryPanel) ? ( - {NO_DATA_PLACEHOLDER} - ) : ( -
- {getRelatedMetricListing(visibleRelatedMetrics)} - {isShowMore && getRelatedMetricListing(hiddenRelatedMetrics)} - {!isEmpty(hiddenRelatedMetrics) && showMoreLessElement} -
- )} - - )} - - + const content = isEdit ? ( + setIsEdit(false)} + onSubmit={handleRelatedMetricUpdate} + /> + ) : isEmpty(relatedMetrics) && (metricDetails.deleted || isInSummaryPanel) ? ( + {NO_DATA_PLACEHOLDER} + ) : ( + !isEmpty(relatedMetrics) && ( +
+ {getRelatedMetricListing(visibleRelatedMetrics)} + {isShowMore && getRelatedMetricListing(hiddenRelatedMetrics)} + {!isEmpty(hiddenRelatedMetrics) && showMoreLessElement} +
+ ) ); return ( @@ -221,7 +214,7 @@ const RelatedMetrics: FC = ({ title: header, }} isExpandDisabled={isEmpty(relatedMetrics)}> - {content} + {content} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsContainerV2/TagsContainerV2.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsContainerV2/TagsContainerV2.interface.ts index 69830284318..3c6ff7b24e8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsContainerV2/TagsContainerV2.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsContainerV2/TagsContainerV2.interface.ts @@ -26,7 +26,6 @@ export type TagsContainerV2Props = { columnData?: { fqn: string; }; - showHeader?: boolean; showBottomEditButton?: boolean; showInlineEditButton?: boolean; children?: ReactElement; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsContainerV2/TagsContainerV2.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsContainerV2/TagsContainerV2.tsx index 38f40a9bba1..02fe416bb19 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsContainerV2/TagsContainerV2.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsContainerV2/TagsContainerV2.tsx @@ -43,6 +43,7 @@ import ExpandableCard from '../../common/ExpandableCard/ExpandableCard'; import { CommentIconButton, EditIconButton, + PlusIconButton, RequestIconButton, } from '../../common/IconButtons/EditIconButton'; import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider'; @@ -65,7 +66,6 @@ const TagsContainerV2 = ({ tagType, displayType, layoutType, - showHeader = true, showBottomEditButton, showInlineEditButton, columnData, @@ -173,29 +173,34 @@ const TagsContainerV2 = ({ const addTagButton = useMemo( () => showAddTagButton ? ( - - - + ) : null, - [showAddTagButton] + [showAddTagButton, handleAddClick, t, isGlossaryType] ); const renderTags = useMemo( - () => ( - - - - ), + () => + isEmpty(tags?.[tagType]) && !showNoDataPlaceholder ? null : ( + + + + ), [displayType, showNoDataPlaceholder, tags?.[tagType], layoutType] ); @@ -267,46 +272,43 @@ const TagsContainerV2 = ({ const header = useMemo(() => { return ( - showHeader && ( - - - {isGlossaryType ? t('label.glossary-term') : t('label.tag-plural')} - - {permission && ( - <> - {!isEmpty(tags?.[tagType]) && !isEditTags && ( - - )} - {showTaskHandler && ( - <> - {tagType === TagSource.Classification && requestTagElement} - {conversationThreadElement} - - )} - - )} - - ) + + + {isGlossaryType ? t('label.glossary-term') : t('label.tag-plural')} + + {permission && ( + <> + {addTagButton ?? ( + + )} + {showTaskHandler && ( + <> + {tagType === TagSource.Classification && requestTagElement} + {conversationThreadElement} + + )} + + )} + ); }, [ tags, tagType, - showHeader, isEditTags, permission, showTaskHandler, @@ -372,13 +374,21 @@ const TagsContainerV2 = ({ } else { return isHoriZontalLayout ? ( horizontalLayout - ) : ( + ) : showInlineEditButton || !isEmpty(renderTags) || !newLook ? ( - {addTagButton} + {showAddTagButton && ( + + + + )} {renderTags} - {showInlineEditButton && {editTagButton}} + {showInlineEditButton ? {editTagButton} : null} - ); + ) : null; } }, [ isEditTags, @@ -429,17 +439,7 @@ const TagsContainerV2 = ({ }} dataTestId={isGlossaryType ? 'glossary-container' : 'tags-container'} isExpandDisabled={isEmpty(tags?.[tagType])}> - {suggestionDataRender ?? ( - <> - {tagBody} - {(children || showBottomEditButton) && ( - - {showBottomEditButton && !showInlineEditButton && editTagButton} - {children} - - )} - - )} + {suggestionDataRender ?? tagBody} ); } @@ -448,8 +448,6 @@ const TagsContainerV2 = ({
- {header} - {suggestionDataRender ?? ( <> {tagBody} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TagsInput/TagsInput.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/TagsInput/TagsInput.test.tsx index 45fa3a643f0..1cce8fbead1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TagsInput/TagsInput.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/TagsInput/TagsInput.test.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2024 Collate. + * Copyright 2025 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 @@ -10,136 +10,129 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { act, render, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { LabelType, State, TagSource } from '../../generated/type/tagLabel'; +import { + LabelType, + State, + TagLabel, + TagSource, +} from '../../generated/type/tagLabel'; +import TagsContainerV2 from '../Tag/TagsContainerV2/TagsContainerV2'; import TagsInput from './TagsInput.component'; -const mockOnTagsUpdate = jest.fn(); +jest.mock('../../components/Tag/TagsContainerV2/TagsContainerV2', () => { + return jest + .fn() + .mockImplementation(() => ( +
Mocked TagsContainerV2
+ )); +}); -const tags = [ - { - tagFQN: 'tag1', - displayName: 'Tag 1', - labelType: LabelType.Automated, - source: TagSource.Classification, - state: State.Confirmed, - }, - { - tagFQN: 'tag2', - displayName: 'Tag 2', - description: 'This is a sample tag description.', - labelType: LabelType.Derived, - source: TagSource.Glossary, - state: State.Suggested, - }, -]; +describe('TagsInput Component', () => { + const mockTags: TagLabel[] = [ + { + tagFQN: 'test.tag1', + source: TagSource.Classification, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'test.tag2', + source: TagSource.Classification, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + ]; -describe('TagsInput', () => { - it('should render TagsInput along with tagsViewer in version view', async () => { - await act(async () => { - render( - , - { - wrapper: MemoryRouter, - } - ); - }); + const mockOnTagsUpdate = jest.fn(); - expect( - await screen.findByTestId('tags-input-container') - ).toBeInTheDocument(); - expect(await screen.findByText('label.tag-plural')).toBeInTheDocument(); - expect(await screen.findByText('Tag 1')).toBeInTheDocument(); - expect(await screen.findByText('Tag 2')).toBeInTheDocument(); + it('renders without crashing', () => { + render( + , + { wrapper: MemoryRouter } + ); + + expect(screen.getByTestId('tags-input-container')).toBeInTheDocument(); }); - it('should render tags container when not in in version view', async () => { - await act(async () => { - render( - , - { - wrapper: MemoryRouter, - } - ); - }); + it('renders in version view mode', () => { + render( + , + { wrapper: MemoryRouter } + ); - expect( - await screen.findByTestId('tags-input-container') - ).toBeInTheDocument(); - expect(await screen.findByTestId('tags-container')).toBeInTheDocument(); - - expect(await screen.findByText('label.tag-plural')).toBeInTheDocument(); - expect(await screen.findByText('Tag 1')).toBeInTheDocument(); + expect(screen.getByText('label.tag-plural')).toBeInTheDocument(); }); - it('should render edit button when no editable', async () => { - await act(async () => { - render( - , - { - wrapper: MemoryRouter, - } - ); - }); + it('renders tags in version view mode', () => { + render( + , + { wrapper: MemoryRouter } + ); - expect(await screen.findByTestId('edit-button')).toBeInTheDocument(); - }); - - it('should not render edit button when no editable', async () => { - await act(async () => { - render( - , - { - wrapper: MemoryRouter, - } - ); - }); - - expect(await screen.queryByTestId('edit-button')).not.toBeInTheDocument(); - }); - - it('should not render tags if tags is empty', async () => { - await act(async () => { - render( - , - { - wrapper: MemoryRouter, - } - ); - - expect(await screen.findByTestId('tags-container')).toBeInTheDocument(); - expect(await screen.findByTestId('entity-tags')).toBeInTheDocument(); - expect(await screen.findByText('--')).toBeInTheDocument(); + mockTags.forEach((tag) => { + expect(screen.getByText(tag.tagFQN)).toBeInTheDocument(); }); }); - it('should render add tags if tags is empty and has permission', async () => { - await act(async () => { - render(, { - wrapper: MemoryRouter, - }); + it('renders TagsContainerV2 when not in version view', () => { + render( + , + { wrapper: MemoryRouter } + ); - expect(await screen.findByTestId('entity-tags')).toBeInTheDocument(); - expect(await screen.findByTestId('add-tag')).toBeInTheDocument(); + // Verify TagsContainerV2 is rendered + expect(screen.getByTestId('tags-container')).toBeInTheDocument(); + }); + + it('handles empty tags array', () => { + render(, { + wrapper: MemoryRouter, }); + + expect(screen.getByTestId('tags-input-container')).toBeInTheDocument(); + }); + + it('disables tag editing when editable is false', () => { + render( + , + { wrapper: MemoryRouter } + ); + + expect(TagsContainerV2).toHaveBeenCalledWith( + expect.objectContaining({ + permission: false, + }), + {} + ); + }); + + it('handles undefined tags prop', () => { + render(, { + wrapper: MemoryRouter, + }); + + expect(screen.getByTestId('tags-input-container')).toBeInTheDocument(); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/ExpandableCard/ExpandableCard.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/ExpandableCard/ExpandableCard.test.tsx index 91659041e49..b3ab5abdae2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/ExpandableCard/ExpandableCard.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/ExpandableCard/ExpandableCard.test.tsx @@ -39,10 +39,6 @@ describe('ExpandableCard', () => { expect(screen.getByText('Test Card')).toBeInTheDocument(); expect(screen.getByTestId('test-content')).toBeInTheDocument(); - expect(screen.getByRole('button')).toHaveAttribute( - 'title', - 'label.collapse' - ); }); it('renders with custom data-testid', () => { @@ -104,7 +100,6 @@ describe('ExpandableCard', () => { const expandButton = screen.getByRole('button'); // Initial state (collapsed) - expect(expandButton).toHaveAttribute('title', 'label.collapse'); expect(expandButton.closest('.ant-card')).toHaveClass('expanded'); // Click to collapse @@ -112,7 +107,6 @@ describe('ExpandableCard', () => { fireEvent.click(expandButton); }); - expect(expandButton).toHaveAttribute('title', 'label.expand'); expect(expandButton.closest('.ant-card')).not.toHaveClass('collapsed'); // Click to expand again @@ -120,7 +114,6 @@ describe('ExpandableCard', () => { fireEvent.click(expandButton); }); - expect(expandButton).toHaveAttribute('title', 'label.collapse'); expect(expandButton.closest('.ant-card')).toHaveClass('expanded'); }); @@ -239,8 +232,9 @@ describe('ExpandableCard', () => { fireEvent.click(expandButton); }); - // Should not throw any errors - expect(expandButton).toHaveAttribute('title', 'label.expand'); + const card = screen.getByRole('button').closest('.ant-card'); + + expect(card).not.toHaveClass('expanded'); }); it('works with minimal cardProps', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/ExpandableCard/ExpandableCard.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/ExpandableCard/ExpandableCard.tsx index 8b395d9d196..65e63084415 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/ExpandableCard/ExpandableCard.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/ExpandableCard/ExpandableCard.tsx @@ -44,6 +44,10 @@ const ExpandableCard = ({ return ( { - return ( + const button = (