diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ContainerDetail/ContainerDataModel/ContainerDataModel.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ContainerDetail/ContainerDataModel/ContainerDataModel.tsx index 510b144da38..1a756ca58b6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ContainerDetail/ContainerDataModel/ContainerDataModel.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ContainerDetail/ContainerDataModel/ContainerDataModel.tsx @@ -140,7 +140,7 @@ const ContainerDataModel: FC = ({ - {columnData.description ? ( - + {columnData.field ? ( + ) : ( {t('label.no-entity', { @@ -62,10 +63,11 @@ const TableDescription = ({ /> )} - diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TableDescription/TableDescription.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/TableDescription/TableDescription.interface.ts index 2304839161d..adc1fa0c64c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TableDescription/TableDescription.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/TableDescription/TableDescription.interface.ts @@ -19,7 +19,7 @@ export interface TableDescriptionProps { index: number; columnData: { fqn: string; - description?: string; + field?: string; }; entityFqn: string; entityType: EntityType; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TableTags/TableTags.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/TableTags/TableTags.component.tsx index cf12abf88f6..0e81c268bc9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TableTags/TableTags.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/TableTags/TableTags.component.tsx @@ -13,7 +13,8 @@ import classNames from 'classnames'; import TagsContainerV2 from 'components/Tag/TagsContainerV2/TagsContainerV2'; -import EntityTaskTags from 'pages/TasksPage/EntityTaskTags/EntityTaskTags.component'; +import { EntityField } from 'constants/Feeds.constants'; +import EntityTasks from 'pages/TasksPage/EntityTasks/EntityTasks.component'; import React from 'react'; import { TableTagsComponentProps, TableUnion } from './TableTags.interface'; @@ -48,13 +49,14 @@ const TableTags = ({ }}> <> {!isReadOnly && ( - ({ ), })); -jest.mock('pages/TasksPage/EntityTaskTags/EntityTaskTags.component', () => { - return jest.fn().mockImplementation(() =>
EntityTaskTags
); +jest.mock('pages/TasksPage/EntityTasks/EntityTasks.component', () => { + return jest.fn().mockImplementation(() =>
EntityTasks
); }); const glossaryTags = [ @@ -171,10 +171,10 @@ describe('Test EntityTableTags Component', () => { ); const tagContainer = await screen.findByTestId('Classification-tags-0'); - const entityTaskTags = screen.queryByText('EntityTaskTags'); + const entityTasks = screen.queryByText('EntityTasks'); expect(tagContainer).toBeInTheDocument(); - expect(entityTaskTags).not.toBeInTheDocument(); + expect(entityTasks).not.toBeInTheDocument(); }); it('Should render update and request tags buttons', async () => { @@ -194,9 +194,9 @@ describe('Test EntityTableTags Component', () => { ); const tagContainer = await screen.findByTestId('Classification-tags-0'); - const entityTaskTags = screen.queryByText('EntityTaskTags'); + const entityTasks = screen.queryByText('EntityTasks'); expect(tagContainer).toBeInTheDocument(); - expect(entityTaskTags).toBeInTheDocument(); + expect(entityTasks).toBeInTheDocument(); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicSchema/TopicSchema.tsx b/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicSchema/TopicSchema.tsx index 64bcc913489..736ca868e72 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicSchema/TopicSchema.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicSchema/TopicSchema.tsx @@ -182,7 +182,7 @@ const TopicSchemaFields: FC = ({ { - const history = useHistory(); - const { t } = useTranslation(); - - const { fqnPart, entityField } = useMemo( - () => getEntityTaskDetails(entityType), - [entityType] - ); - - const columnName = useMemo(() => { - const columnName = getPartialNameFromTableFQN(data.fqn ?? '', fqnPart); - - return columnName.includes(FQN_SEPARATOR_CHAR) - ? `"${columnName}"` - : columnName; - }, [data.fqn]); - - const handleDescriptionTask = (hasDescription: boolean) => { - history.push( - (hasDescription ? getUpdateDescriptionPath : getRequestDescriptionPath)( - entityType, - entityFqn, - entityField, - columnName - ) - ); - }; - - const requestDescriptionElement = useMemo(() => { - const hasDescription = Boolean(data?.description); - - return ( - - handleDescriptionTask(hasDescription)} - /> - - ); - }, [data]); - - return ( - - {requestDescriptionElement} - {getFieldThreadElement( - columnName, - EntityField.DESCRIPTION, - entityFieldThreads, - onThreadLinkSelect, - entityType, - entityFqn, - `${entityField}${ENTITY_LINK_SEPARATOR}${columnName}${ENTITY_LINK_SEPARATOR}${EntityField.DESCRIPTION}` - )} - - ); -}; - -export default EntityTaskDescription; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTaskDescription/entityTaskDescription.interface.ts b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTaskDescription/entityTaskDescription.interface.ts deleted file mode 100644 index 04cdbeefa49..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTaskDescription/entityTaskDescription.interface.ts +++ /dev/null @@ -1,27 +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 { EntityType } from 'enums/entity.enum'; -import { ThreadType } from 'generated/api/feed/createThread'; -import { EntityFieldThreads } from 'interface/feed.interface'; - -export interface EntityTaskDescriptionProps { - data: { - fqn?: string; - description?: string; - }; - entityFqn: string; - entityType: EntityType; - entityFieldThreads: EntityFieldThreads[]; - onThreadLinkSelect: (value: string, threadType?: ThreadType) => void; -} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTaskTags/EntityTaskTags.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTasks/EntityTasks.component.tsx similarity index 66% rename from openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTaskTags/EntityTaskTags.component.tsx rename to openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTasks/EntityTasks.component.tsx index cb1a7350dff..b72d9983b9e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTaskTags/EntityTaskTags.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTasks/EntityTasks.component.tsx @@ -14,6 +14,7 @@ import { Space, Tooltip } from 'antd'; import { FQN_SEPARATOR_CHAR } from 'constants/char.constants'; import { DE_ACTIVE_COLOR } from 'constants/constants'; +import { ENTITY_TASKS_TOOLTIP } from 'constants/entity.constants'; import { EntityField } from 'constants/Feeds.constants'; import { TagSource } from 'generated/type/tagLabel'; import { isEmpty } from 'lodash'; @@ -25,20 +26,23 @@ import { ENTITY_LINK_SEPARATOR } from 'utils/EntityUtils'; import { getFieldThreadElement } from 'utils/FeedElementUtils'; import { getEntityTaskDetails, + getRequestDescriptionPath, getRequestTagsPath, + getUpdateDescriptionPath, getUpdateTagsPath, } from 'utils/TasksUtils'; import { ReactComponent as IconRequest } from '../../../assets/svg/request-icon.svg'; -import { EntityTaskTagsProps } from './EntityTaskTags.interface'; +import { EntityTasksProps } from './EntityTasks.interface'; -const EntityTaskTags = ({ +const EntityTasks = ({ data, tagSource, entityFqn, entityType, + entityTaskType, entityFieldThreads, onThreadLinkSelect, -}: EntityTaskTagsProps) => { +}: EntityTasksProps) => { const { t } = useTranslation(); const history = useHistory(); @@ -55,59 +59,70 @@ const EntityTaskTags = ({ : columnName; }, [data.fqn]); - const handleTagTask = (hasTags: boolean) => { - history.push( - (hasTags ? getUpdateTagsPath : getRequestTagsPath)( - entityType, - entityFqn, - entityField, - columnName - ) - ); + const handleTask = (hasData: boolean) => { + if (entityTaskType === EntityField.DESCRIPTION) { + history.push( + (hasData ? getUpdateDescriptionPath : getRequestDescriptionPath)( + entityType, + entityFqn, + entityField, + columnName + ) + ); + } else { + history.push( + (hasData ? getUpdateTagsPath : getRequestTagsPath)( + entityType, + entityFqn, + entityField, + columnName + ) + ); + } }; - const getRequestTagsElement = useMemo(() => { - const hasTags = !isEmpty(data.tags); + const taskElement = useMemo(() => { + const hasData = !isEmpty(data.field); return ( handleTagTask(hasTags)} + onClick={() => handleTask(hasData)} /> ); - }, [data]); + }, [data.field]); return ( - - {/* Request and Update tags */} - {tagSource === TagSource.Classification && getRequestTagsElement} + + {/* Request and Update Tasks */} + {tagSource !== TagSource.Glossary && taskElement} {/* List Conversation */} {getFieldThreadElement( columnName, - EntityField.TAGS, + entityTaskType, entityFieldThreads, onThreadLinkSelect, entityType, entityFqn, - `${entityField}${ENTITY_LINK_SEPARATOR}${columnName}${ENTITY_LINK_SEPARATOR}${EntityField.TAGS}` + `${entityField}${ENTITY_LINK_SEPARATOR}${columnName}${ENTITY_LINK_SEPARATOR}${entityTaskType}` )} ); }; -export default EntityTaskTags; +export default EntityTasks; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTaskTags/entityTaskTags.interface.ts b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTasks/EntityTasks.interface.ts similarity index 82% rename from openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTaskTags/entityTaskTags.interface.ts rename to openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTasks/EntityTasks.interface.ts index eb084cc9873..68aa913b78a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTaskTags/entityTaskTags.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTasks/EntityTasks.interface.ts @@ -11,19 +11,21 @@ * limitations under the License. */ +import { EntityField } from 'constants/Feeds.constants'; import { EntityType } from 'enums/entity.enum'; import { ThreadType } from 'generated/api/feed/createThread'; import { TagLabel, TagSource } from 'generated/type/tagLabel'; import { EntityFieldThreads } from 'interface/feed.interface'; -export interface EntityTaskTagsProps { +export interface EntityTasksProps { data: { fqn: string; - tags: TagLabel[]; + field?: string | TagLabel[]; }; - tagSource: TagSource; + tagSource?: TagSource; entityFqn: string; entityType: EntityType; + entityTaskType: EntityField.TAGS | EntityField.DESCRIPTION; entityFieldThreads: EntityFieldThreads[]; onThreadLinkSelect: (value: string, threadType?: ThreadType) => void; } diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTasks/EntityTasks.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTasks/EntityTasks.test.tsx new file mode 100644 index 00000000000..e30b1fb0929 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/EntityTasks/EntityTasks.test.tsx @@ -0,0 +1,225 @@ +/* + * 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 { act, fireEvent, render, screen } from '@testing-library/react'; +import { EntityField } from 'constants/Feeds.constants'; +import { EntityType, FqnPart } from 'enums/entity.enum'; +import { TagSource } from 'generated/type/tagLabel'; +import React from 'react'; +import EntityTasks from './EntityTasks.component'; +import { EntityTasksProps } from './EntityTasks.interface'; + +const mockRequestTags = { + pathname: '/request-tags/table/sample_data.ecommerce_db.shopify.fact_sale', + search: 'field=columns&value=sale_id', +}; + +const mockUpdateTags = { + pathname: '/update-tags/table/sample_data.ecommerce_db.shopify.fact_sale', + search: 'field=columns&value=sale_id', +}; + +const mockRequestDescription = { + pathname: + '/request-description/table/sample_data.ecommerce_db.shopify.fact_sale', + search: 'field=columns&value=sale_id', +}; + +const mockUpdateDescription = { + pathname: + '/update-description/table/sample_data.ecommerce_db.shopify.fact_sale', + search: 'field=columns&value=sale_id', +}; + +const mockProps: EntityTasksProps = { + data: { + fqn: 'sample_data.ecommerce_db.shopify.fact_session', + field: 'this is test', + }, + tagSource: TagSource.Classification, + entityFqn: '', + entityType: EntityType.TABLE, + entityTaskType: EntityField.TAGS, + entityFieldThreads: [], + onThreadLinkSelect: jest.fn(), +}; + +jest.mock('../../../utils/TasksUtils', () => ({ + getEntityTaskDetails: jest.fn().mockReturnValue({ + fqnPart: FqnPart.NestedColumn, + entityField: EntityField.COLUMNS, + }), + getRequestDescriptionPath: jest + .fn() + .mockImplementation(() => mockRequestDescription), + getRequestTagsPath: jest.fn().mockImplementation(() => mockRequestTags), + getUpdateDescriptionPath: jest + .fn() + .mockImplementation(() => mockUpdateDescription), + getUpdateTagsPath: jest.fn().mockImplementation(() => mockUpdateTags), +})); + +jest.mock('../../../utils/FeedElementUtils', () => ({ + getFieldThreadElement: jest + .fn() + .mockImplementation(() => ( +

List Conversation

+ )), +})); + +jest.mock('../../../utils/CommonUtils', () => ({ + getPartialNameFromTableFQN: jest.fn().mockReturnValue('test'), +})); + +const mockHistory = { + push: jest.fn(), +}; + +jest.mock('react-router-dom', () => ({ + useHistory: jest.fn().mockImplementation(() => mockHistory), +})); + +describe('Entity Task component', () => { + it('Should render the component', async () => { + render(); + + const container = await screen.findByTestId('entity-task'); + + expect(container).toBeInTheDocument(); + }); + + it('Task Element should be visible when tagSource is not glossary', async () => { + render(); + + const container = await screen.findByTestId('entity-task'); + + expect(container).toBeInTheDocument(); + + const taskElement = screen.queryByTestId('task-element'); + + expect(taskElement).toBeInTheDocument(); + }); + + it('Task Element should not visible when tagSource is glossary', async () => { + render(); + + const container = await screen.findByTestId('entity-task'); + + expect(container).toBeInTheDocument(); + + const taskElement = screen.queryByTestId('task-element'); + + expect(taskElement).not.toBeInTheDocument(); + }); + + it('List conversation should be there in component', async () => { + render(); + + const container = await screen.findByTestId('entity-task'); + + expect(container).toBeInTheDocument(); + + const conversation = await screen.findByTestId('list-conversation'); + + expect(conversation).toBeInTheDocument(); + }); + + it('Handle update tags click', async () => { + render(); + + const container = await screen.findByTestId('entity-task'); + + expect(container).toBeInTheDocument(); + + const taskElement = await screen.findByTestId('task-element'); + + expect(taskElement).toBeInTheDocument(); + + await act(async () => { + fireEvent.click(taskElement); + }); + + expect(mockHistory.push).toHaveBeenCalledWith(mockUpdateTags); + }); + + it('Handle request tags click', async () => { + render( + + ); + + const container = await screen.findByTestId('entity-task'); + + expect(container).toBeInTheDocument(); + + const taskElement = await screen.findByTestId('task-element'); + + expect(taskElement).toBeInTheDocument(); + + await act(async () => { + fireEvent.click(taskElement); + }); + + expect(mockHistory.push).toHaveBeenCalledWith(mockRequestTags); + }); + + it('Handle update description click', async () => { + render( + + ); + + const container = await screen.findByTestId('entity-task'); + + expect(container).toBeInTheDocument(); + + const taskElement = await screen.findByTestId('task-element'); + + expect(taskElement).toBeInTheDocument(); + + await act(async () => { + fireEvent.click(taskElement); + }); + + expect(mockHistory.push).toHaveBeenCalledWith(mockUpdateDescription); + }); + + it('Handle request description click', async () => { + render( + + ); + + const container = await screen.findByTestId('entity-task'); + + expect(container).toBeInTheDocument(); + + const taskElement = await screen.findByTestId('task-element'); + + expect(taskElement).toBeInTheDocument(); + + await act(async () => { + fireEvent.click(taskElement); + }); + + expect(mockHistory.push).toHaveBeenCalledWith(mockRequestDescription); + }); +});