diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js index c5e243b659c..dcf1627f78e 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js @@ -371,7 +371,7 @@ export const addNewTagToEntity = (entity, term) => { .should('be.visible') .contains(term); - cy.get('[data-testid="table-body"] > :nth-child(1) > :nth-child(5)') + cy.get(':nth-child(1) > :nth-child(5) [data-testid="tag-container"]') .contains('Tags') .should('be.visible') .click(); @@ -386,7 +386,7 @@ export const addNewTagToEntity = (entity, term) => { .scrollIntoView() .should('be.visible') .click(); - cy.get('[data-testid="table-body"] > :nth-child(1) > :nth-child(5)') + cy.get(':nth-child(1) > :nth-child(5) [data-testid="tag-container"]') .scrollIntoView() .contains(term) .should('exist'); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Glossary.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Glossary.spec.js index 20df30b01bf..a20537e946e 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Glossary.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Glossary.spec.js @@ -366,7 +366,7 @@ describe('Glossary page should work properly', () => { //Remove the added column tag from entity cy.get( - ':nth-child(1) > :nth-child(5) > [data-testid="tags-wrapper"] > :nth-child(1) > :nth-child(1) > [data-testid="tag-container"] > div > span.tw-text-primary > [data-testid="tags"]' + ':nth-child(1) > :nth-child(5) span.tw-text-primary > [data-testid="tags"]' ) .scrollIntoView() .should('be.visible') diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.component.tsx index 86e4de7e7ce..8cf849d863f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.component.tsx @@ -13,13 +13,18 @@ import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Popover } from 'antd'; +import { Popover, Table } from 'antd'; import classNames from 'classnames'; import { cloneDeep, isEmpty, isNil, isUndefined, lowerCase } from 'lodash'; import { EntityFieldThreads, EntityTags, TagOption } from 'Models'; -import React, { Fragment, useEffect, useState } from 'react'; +import React, { + Fragment, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; import { Link, useHistory } from 'react-router-dom'; -import { useExpanded, useTable } from 'react-table'; import { useAuthContext } from '../../authentication/auth-provider/AuthProvider'; import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants'; import { getTableDetailsPath } from '../../constants/constants'; @@ -68,8 +73,9 @@ import RichTextEditorPreviewer from '../common/rich-text-editor/RichTextEditorPr import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; import TagsContainer from '../tags-container/tags-container'; import TagsViewer from '../tags-viewer/tags-viewer'; -import { TABLE_HEADERS } from './EntityTable.constant'; +import { TABLE_HEADERS_V1 } from './EntityTable.constants'; import { EntityTableProps } from './EntityTable.interface'; +import './EntityTable.style.less'; const EntityTable = ({ tableColumns, @@ -88,7 +94,6 @@ const EntityTable = ({ const { isAdminUser, userPermissions } = useAuth(); const { isAuthDisabled } = useAuthContext(); const history = useHistory(); - const columns = TABLE_HEADERS; const [searchedColumns, setSearchedColumns] = useState( [] @@ -99,21 +104,6 @@ const EntityTable = ({ [searchedColumns] ); - const { - getTableProps, - getTableBodyProps, - headerGroups, - rows, - prepareRow, - toggleAllRowsExpanded, - } = useTable( - { - columns, - data, - autoResetExpanded: false, - }, - useExpanded - ); const [editColumn, setEditColumn] = useState<{ column: Column; index: number; @@ -162,6 +152,7 @@ const EntityTable = ({ } else { setTagFetchFailed(true); } + setIsTagLoading(false); }) .catch(() => { setAllTags([]); @@ -208,9 +199,7 @@ const EntityTable = ({ ) => { const getUpdatedTags = (column: Column) => { const prevTags = column?.tags?.filter((tag) => { - return newColumnTags - .map((tag) => tag.fqn) - .includes(tag?.tagFQN as string); + return newColumnTags.map((tag) => tag.fqn).includes(tag.tagFQN); }); const newTags: Array = newColumnTags @@ -304,8 +293,6 @@ const EntityTable = ({ } else if (!isUndefined(column.children)) { const searchedChildren = searchInColumns(column.children, searchText); if (searchedChildren.length > 0) { - toggleAllRowsExpanded(true); - return [ ...searchedCols, { @@ -329,7 +316,7 @@ const EntityTable = ({ hasPemission(Operation.EditDescription, EntityType.TABLE, userPermissions); /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ const getColumnName = (cell: any) => { - const fqn = cell?.row?.original?.fullyQualifiedName || ''; + const fqn = cell?.fullyQualifiedName || ''; const columnName = getPartialNameFromTableFQN(fqn, [FqnPart.NestedColumn]); // wrap it in quotes if dot is present @@ -406,13 +393,12 @@ const EntityTable = ({ handleEditColumn(column, index); }; - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - const getRequestDescriptionElement = (cell: any) => { - const hasDescription = Boolean(cell.value); + const getRequestDescriptionElement = (cell: ModifiedTableColumn) => { + const hasDescription = Boolean(cell.fullyQualifiedName); return ( + + )} + {getRequestDescriptionElement(record)} + {getFieldThreadElement( + getColumnName(record), + EntityField.DESCRIPTION, + entityFieldThreads as EntityFieldThreads[], + onThreadLinkSelect, + EntityType.TABLE, + entityFqn, + `columns${ENTITY_LINK_SEPARATOR}${getColumnName( + record + )}${ENTITY_LINK_SEPARATOR}description`, + Boolean(record) + )} + {getFieldThreadElement( + getColumnName(record), + EntityField.DESCRIPTION, + entityFieldTasks as EntityFieldThreads[], + onThreadLinkSelect, + EntityType.TABLE, + entityFqn, + `columns${ENTITY_LINK_SEPARATOR}${getColumnName( + record + )}${ENTITY_LINK_SEPARATOR}description`, + Boolean(record), + ThreadType.Task + )} + + ) : null} + + + + {checkIfJoinsAvailable(record?.name) && ( +
+ + Frequently joined columns: + + + {getFrequentlyJoinedWithColumns(record?.name) + .slice(0, 3) + .map((columnJoin, index) => ( + + {index > 0 && ,} + + {getPartialNameFromTableFQN( + columnJoin.fullyQualifiedName, + [FqnPart.Database, FqnPart.Table, FqnPart.Column], + FQN_SEPARATOR_CHAR + )} + + + ))} + + {getFrequentlyJoinedWithColumns(record?.name).length > 3 && ( + + {getFrequentlyJoinedWithColumns(record?.name) + ?.slice(3) + .map((columnJoin, index) => ( + + + {getPartialNameFromTableFQN( + columnJoin?.fullyQualifiedName, + [ + FqnPart.Database, + FqnPart.Table, + FqnPart.Column, + ] + )} + + + ))} +
+ } + position="bottom" + theme="light" + trigger="click"> + ... + + )} + + + )} + + ); + }; + + const getTagsCell = (index: number, record: ModifiedTableColumn | Column) => { + return ( +
+ {isReadOnly ? ( +
+ +
+ ) : ( +
{ + if (!editColumnTag) { + handleEditColumnTag(record, index); + // Fetch tags and terms only once + if (allTags.length === 0 || tagFetchFailed) { + fetchTagsAndGlossaryTerms(); + } + } + }}> + + { + handleTagSelection(); + }} + onSelectionChange={(tags) => { + handleTagSelection(tags, record?.name); + }} + /> + +
+ {getRequestTagsElement(record)} + {getFieldThreadElement( + getColumnName(record), + 'tags', + entityFieldThreads as EntityFieldThreads[], + onThreadLinkSelect, + EntityType.TABLE, + entityFqn, + `columns${ENTITY_LINK_SEPARATOR}${getColumnName( + record + )}${ENTITY_LINK_SEPARATOR}tags`, + Boolean(record?.name?.length) + )} + {getFieldThreadElement( + getColumnName(record), + EntityField.TAGS, + entityFieldTasks as EntityFieldThreads[], + onThreadLinkSelect, + EntityType.TABLE, + entityFqn, + `${EntityField.COLUMNS}${ENTITY_LINK_SEPARATOR}${getColumnName( + record + )}${ENTITY_LINK_SEPARATOR}${EntityField.TAGS}`, + Boolean(record?.name), + ThreadType.Task + )} +
+
+ )} +
+ ); + }; + + const renderCell = useCallback( + (key: string, record: ModifiedTableColumn | Column, index: number) => { + const columnTestLength = record?.columnTests?.length; + + const [failingTests, passingTests] = getTestStats( + record, + columnTestLength + ); + + switch (key) { + case TABLE_HEADERS_V1.columnTests: + return getColumnTestsCell( + columnTestLength, + failingTests, + passingTests + ); + + case TABLE_HEADERS_V1.dataTypeDisplay: + return getDataTypeDisplayCell(record); + + case TABLE_HEADERS_V1.description: + return getDescriptionCell(index, record); + + case TABLE_HEADERS_V1.tags: + return getTagsCell(index, record); + + default: + return ( + + {isReadOnly ? ( +
+ +
+ ) : ( + + {prepareConstraintIcon(record.name, record.constraint)} + {record.name} + + )} +
+ ); + } + }, + [editColumnTag, isTagLoading, handleUpdate, handleTagSelection] + ); + + const columns = useMemo( + () => [ + { + title: 'Name', + dataIndex: 'name', + key: 'name', + accessor: 'name', + ellipsis: true, + width: 180, + render: ( + _: Array, + record: ModifiedTableColumn, + index: number + ) => renderCell(TABLE_HEADERS_V1.name, record, index), + }, + { + title: 'Type', + dataIndex: 'dataTypeDisplay', + key: 'dataTypeDisplay', + accessor: 'dataTypeDisplay', + ellipsis: true, + width: 200, + render: ( + _: Array, + record: ModifiedTableColumn, + index: number + ) => { + return renderCell(TABLE_HEADERS_V1.dataTypeDisplay, record, index); + }, + }, + { + title: 'Data Quality', + dataIndex: 'columnTests', + key: 'columnTests', + accessor: 'columnTests', + width: 200, + render: ( + _: Array, + record: ModifiedTableColumn, + index: number + ) => { + return renderCell(TABLE_HEADERS_V1.columnTests, record, index); + }, + }, + { + title: 'Description', + dataIndex: 'description', + key: 'description', + accessor: 'description', + render: ( + _: Array, + record: ModifiedTableColumn, + index: number + ) => renderCell(TABLE_HEADERS_V1.description, record, index), + }, + { + title: 'Tags', + dataIndex: 'tags', + key: 'tags', + accessor: 'tags', + width: 272, + render: ( + _: Array, + record: ModifiedTableColumn | Column, + index: number + ) => renderCell(TABLE_HEADERS_V1.tags, record, index), + }, + ], + [editColumnTag, isTagLoading, renderCell] + ); + useEffect(() => { if (!searchText) { setSearchedColumns(tableColumns); @@ -483,435 +908,31 @@ const EntityTable = ({ } }, [searchText, tableColumns]); - useEffect(() => { - toggleAllRowsExpanded(isReadOnly); - }, []); - return ( -
- - - {/* eslint-disable-next-line */} - {headerGroups.map((headerGroup: any, index: number) => ( - - {/* eslint-disable-next-line */} - {headerGroup.headers.map((column: any, index: number) => ( - - ))} - - ))} - - - - {/* eslint-disable-next-line */} - {rows.map((row: any, index: number) => { - prepareRow(row); - - return ( - - {/* eslint-disable-next-line */} - {row.cells.map((cell: any, index: number) => { - const columnTests = - cell.column.id === 'columnTests' - ? ((cell.value ?? []) as ColumnTest[]) - : ([] as ColumnTest[]); - const columnTestLength = columnTests.length; - const failingTests = columnTests.filter((test) => - test.results?.some( - (t) => t.testCaseStatus === TestCaseStatus.Failed - ) - ); - const passingTests = columnTests.filter((test) => - test.results?.some( - (t) => t.testCaseStatus === TestCaseStatus.Success - ) - ); - - return ( - - ); - })} - - ); - })} - -
- {column.render('Header')} -
- {row.canExpand && cell.column.id === 'name' ? ( - - - - ) : null} - - {cell.column.id === 'columnTests' && ( - - {columnTestLength ? ( - - {failingTests.length ? ( -
-

- -

-

- {`${failingTests.length}/${columnTestLength} tests failing`} -

-
- ) : ( - - {passingTests.length ? ( -
-
- -
-

{`${passingTests.length} tests`}

-
- ) : ( -

{`${columnTestLength} tests`}

- )} -
- )} -
- ) : ( - '--' - )} -
- )} - - {cell.column.id === 'dataTypeDisplay' && ( - <> - {cell.value ? ( - <> - {isReadOnly ? ( -
- -
- ) : ( - <> - {cell.value.length > 25 ? ( - - - - {cell.value.toLowerCase()} - - - } - position="bottom" - theme="light" - trigger="click"> -
- -
-
-
- ) : ( - cell.value.toLowerCase() - )} - - )} - - ) : ( - '--' - )} - - )} - - {cell.column.id === 'tags' && ( - <> - {isReadOnly ? ( -
- -
- ) : ( -
handleTagContainerClick(row)}> - - { - handleTagSelection(); - }} - onSelectionChange={(tags) => { - handleTagSelection(tags, row.original.name); - }} - /> - -
- {getRequestTagsElement(cell)} - {getFieldThreadElement( - getColumnName(cell), - 'tags', - entityFieldThreads as EntityFieldThreads[], - onThreadLinkSelect, - EntityType.TABLE, - entityFqn, - `columns${ENTITY_LINK_SEPARATOR}${getColumnName( - cell - )}${ENTITY_LINK_SEPARATOR}tags`, - Boolean(cell.value.length) - )} - {getFieldThreadElement( - getColumnName(cell), - EntityField.TAGS, - entityFieldTasks as EntityFieldThreads[], - onThreadLinkSelect, - EntityType.TABLE, - entityFqn, - `${ - EntityField.COLUMNS - }${ENTITY_LINK_SEPARATOR}${getColumnName( - cell - )}${ENTITY_LINK_SEPARATOR}${ - EntityField.TAGS - }`, - Boolean(cell.value), - ThreadType.Task - )} -
-
- )} - - )} - {cell.column.id === 'description' && ( -
-
-
-
- {cell.value ? ( - - ) : ( - - No description{' '} - - )} -
-
- {!isReadOnly ? ( - - {checkPermission() && ( - - )} - {getRequestDescriptionElement(cell)} - {getFieldThreadElement( - getColumnName(cell), - EntityField.DESCRIPTION, - entityFieldThreads as EntityFieldThreads[], - onThreadLinkSelect, - EntityType.TABLE, - entityFqn, - `columns${ENTITY_LINK_SEPARATOR}${getColumnName( - cell - )}${ENTITY_LINK_SEPARATOR}description`, - Boolean(cell.value) - )} - {getFieldThreadElement( - getColumnName(cell), - EntityField.DESCRIPTION, - entityFieldTasks as EntityFieldThreads[], - onThreadLinkSelect, - EntityType.TABLE, - entityFqn, - `columns${ENTITY_LINK_SEPARATOR}${getColumnName( - cell - )}${ENTITY_LINK_SEPARATOR}description`, - Boolean(cell.value), - ThreadType.Task - )} - - ) : null} -
-
-
- {checkIfJoinsAvailable(row.original.name) && ( -
- - Frequently joined columns: - - - {getFrequentlyJoinedWithColumns( - row.original.name - ) - .slice(0, 3) - .map((columnJoin, index) => ( - - {index > 0 && ( - , - )} - - {getPartialNameFromTableFQN( - columnJoin?.fullyQualifiedName as string, - [ - FqnPart.Database, - FqnPart.Table, - FqnPart.Column, - ], - FQN_SEPARATOR_CHAR - )} - - - ))} - - {getFrequentlyJoinedWithColumns( - row.original.name - ).length > 3 && ( - - {getFrequentlyJoinedWithColumns( - row.original.name - ) - ?.slice(3) - .map((columnJoin, index) => ( - - - {getPartialNameFromTableFQN( - columnJoin?.fullyQualifiedName as string, - [ - FqnPart.Database, - FqnPart.Table, - FqnPart.Column, - ] - )} - - - ))} -
- } - position="bottom" - theme="light" - trigger="click"> - - ... - - - )} - -
- )} - - )} - {cell.column.id === 'name' && ( - - {isReadOnly ? ( -
- -
- ) : ( - - {prepareConstraintIcon( - cell.value, - row.original.constraint - )} - {cell.render('Cell')} - - )} -
- )} -
+ <> + + record.children ? ( + + onExpand( + record, + e as unknown as React.MouseEvent + ) + } + /> + ) : null, + }} + pagination={false} + size="small" + /> {editColumn && ( )} - + ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.constant.ts b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.constant.ts deleted file mode 100644 index 727171c51dd..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.constant.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2021 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. - */ - -export const TABLE_HEADERS = [ - { - Header: 'Name', - accessor: 'name', - }, - { - Header: 'Type', - accessor: 'dataTypeDisplay', - }, - { - Header: 'Data Quality', - accessor: 'columnTests', - }, - { - Header: 'Description', - accessor: 'description', - }, - { - Header: 'Tags', - accessor: 'tags', - }, -]; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.constants.ts b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.constants.ts new file mode 100644 index 00000000000..32d5554ec70 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.constants.ts @@ -0,0 +1,7 @@ +export const TABLE_HEADERS_V1 = { + name: 'name', + dataTypeDisplay: 'dataTypeDisplay', + columnTests: 'columnTests', + description: 'description', + tags: 'tags', +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.style.less b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.style.less new file mode 100644 index 00000000000..0652cfacf06 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.style.less @@ -0,0 +1,10 @@ +.hover-icon-group { + .hover-cell-icon { + opacity: 0; + } + &:hover { + .hover-cell-icon { + opacity: 100; + } + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.test.tsx index 4a8d9b29ec9..6b1bdc7b48a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.test.tsx @@ -11,55 +11,17 @@ * limitations under the License. */ -import { - findAllByTestId, - findByTestId, - fireEvent, - queryByTestId, - render, -} from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import { flatten } from 'lodash'; import { FormattedGlossaryTermData, TagOption } from 'Models'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; +import { Column } from '../../generated/api/data/createTable'; import { Table } from '../../generated/entity/data/table'; import { TagCategory, TagClass } from '../../generated/entity/tags/tagCategory'; import { ModifiedTableColumn } from '../../interface/dataQuality.interface'; -import { fetchGlossaryTerms } from '../../utils/GlossaryUtils'; -import { getTagCategories } from '../../utils/TagsUtils'; -import EntityTable from './EntityTable.component'; - -const mockTableheader = [ - { - Header: 'Name', - accessor: 'name', - }, - { - Header: 'Type', - accessor: 'dataTypeDisplay', - }, - { - Header: 'Data Quality', - accessor: 'columnTests', - }, - { - Header: 'Description', - accessor: 'description', - }, - { - Header: 'Tags', - accessor: 'tags', - }, -]; - -const mockEntityFieldThreads = [ - { - entityLink: - '<#E::table::bigquery_gcp.ecommerce.shopify.raw_product_catalog::columns::products::description>', - count: 1, - entityField: 'columns::products::description', - }, -]; +import EntityTableV1 from './EntityTable.component'; +import type { ColumnsType } from 'antd/es/table'; const onEntityFieldSelect = jest.fn(); const onThreadLinkSelect = jest.fn(); @@ -71,6 +33,15 @@ const mockTableConstraints = [ }, ] as Table['tableConstraints']; +type ColumnDataType = { + key: string; + name: string; + dataTypeDisplay: string; + columnTests: string; + description: string; + tags: string; +}; + const mockEntityTableProp = { tableColumns: [ { @@ -230,6 +201,11 @@ jest.mock('@fortawesome/react-fontawesome', () => ({ FontAwesomeIcon: jest.fn().mockReturnValue(Icon), })); +jest.mock('@fortawesome/free-solid-svg-icons', () => ({ + faCaretDown: jest.fn().mockReturnValue(faCaretDown), + faCaretRight: jest.fn().mockReturnValue(faCaretRight), +})); + jest.mock('../common/non-admin-action/NonAdminAction', () => { return jest .fn() @@ -241,9 +217,11 @@ jest.mock('../common/non-admin-action/NonAdminAction', () => { jest.mock('../common/rich-text-editor/RichTextEditorPreviewer', () => { return jest.fn().mockReturnValue(

RichTextEditorPreviewer

); }); + jest.mock('../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor', () => ({ ModalWithMarkdownEditor: jest.fn().mockReturnValue(

EditorModal

), })); + jest.mock('../tags-container/tags-container', () => { return jest.fn().mockImplementation(({ tagList }) => { return ( @@ -255,9 +233,11 @@ jest.mock('../tags-container/tags-container', () => { ); }); }); + jest.mock('../tags-viewer/tags-viewer', () => { return jest.fn().mockReturnValue(

TagViewer

); }); + jest.mock('../tags/tags', () => { return jest.fn().mockReturnValue(

Tag

); }); @@ -286,258 +266,82 @@ jest.mock('../../utils/TagsUtils', () => ({ }), })); -jest.mock('./EntityTable.constant', () => { - return { - TABLE_HEADERS: [ - { - Header: 'Name', - accessor: 'name', - }, - { - Header: 'Type', - accessor: 'dataTypeDisplay', - }, - { - Header: 'Data Quality', - accessor: 'columnTests', - }, - { - Header: 'Description', - accessor: 'description', - }, - { - Header: 'Tags', - accessor: 'tags', - }, - ], - }; -}); +jest.mock('antd', () => ({ + Popover: jest + .fn() + .mockImplementation(({ children }) =>
{children}
), + + Table: jest.fn().mockImplementation(({ columns, dataSource }) => ( +
+ + + {(columns as ColumnsType).map((col) => ( + + ))} + + + + {dataSource.map((row: ModifiedTableColumn | Column, i: number) => ( + + {(columns as ColumnsType).map((col, index) => ( + + ))} + + ))} + +
{col.title}
+ {col.render ? col.render(row, dataSource, index) : 'alt'} +
+ )), +})); describe('Test EntityTable Component', () => { - it('Check if it has all child elements', async () => { - const { container } = render(, { + it('Initially, Table should load', async () => { + render(, { wrapper: MemoryRouter, }); - const entityTable = await findByTestId(container, 'entity-table'); + const entityTable = await screen.findByTestId('entity-table'); expect(entityTable).toBeInTheDocument(); - - const tableHeader = await findByTestId(container, 'table-header'); - - expect(tableHeader).toBeInTheDocument(); - - for (let index = 0; index < mockTableheader.length; index++) { - const headerValue = mockTableheader[index]; - - const header = await findByTestId(tableHeader, `${headerValue.accessor}`); - - expect(header).toBeInTheDocument(); - } - - const tableBody = await findByTestId(container, 'table-body'); - - expect(tableBody).toBeInTheDocument(); - - const tableRows = await findAllByTestId(tableBody, 'row'); - - expect(tableRows).toHaveLength(mockEntityTableProp.tableColumns.length); }); it('should render request description button', async () => { - const { container } = render(, { + render(, { wrapper: MemoryRouter, }); - const entityTable = await findByTestId(container, 'entity-table'); + const entityTable = await screen.findByTestId('entity-table'); expect(entityTable).toBeInTheDocument(); - const tableBody = await findByTestId(container, 'table-body'); - - expect(tableBody).toBeInTheDocument(); - - const tableRows = await findAllByTestId(tableBody, 'row'); - - const requestDescriptionButton = await findByTestId( - tableRows[0], + const requestDescriptionButton = await screen.findAllByTestId( 'request-description' ); - expect(requestDescriptionButton).toBeInTheDocument(); - - const descriptionThread = queryByTestId(tableRows[0], 'field-thread'); - const startDescriptionThread = queryByTestId( - tableRows[0], - 'start-field-thread' - ); - - // should not be in the document, as request description button is present - expect(descriptionThread).not.toBeInTheDocument(); - expect(startDescriptionThread).not.toBeInTheDocument(); + expect(requestDescriptionButton[0]).toBeInTheDocument(); }); it('Should render start thread button', async () => { - const { container } = render(, { + render(, { wrapper: MemoryRouter, }); - const entityTable = await findByTestId(container, 'entity-table'); + const entityTable = await screen.findByTestId('entity-table'); expect(entityTable).toBeInTheDocument(); - const tableBody = await findByTestId(container, 'table-body'); - - expect(tableBody).toBeInTheDocument(); - - const tableRows = await findAllByTestId(tableBody, 'row'); - - const startThreadButton = await findByTestId( - tableRows[4], + const startThreadButton = await screen.findAllByTestId( 'start-field-thread' ); - expect(startThreadButton).toBeInTheDocument(); + expect(startThreadButton[0]).toBeInTheDocument(); fireEvent.click( - startThreadButton, + startThreadButton[0], new MouseEvent('click', { bubbles: true, cancelable: true }) ); expect(onThreadLinkSelect).toBeCalled(); }); - - it('Should render thread button with count', async () => { - const { container } = render( - , - { - wrapper: MemoryRouter, - } - ); - - const entityTable = await findByTestId(container, 'entity-table'); - - expect(entityTable).toBeInTheDocument(); - - const tableBody = await findByTestId(container, 'table-body'); - - expect(tableBody).toBeInTheDocument(); - - const tableRows = await findAllByTestId(tableBody, 'row'); - - const threadButton = await findByTestId(tableRows[1], 'field-thread'); - - expect(threadButton).toBeInTheDocument(); - - fireEvent.click( - threadButton, - new MouseEvent('click', { bubbles: true, cancelable: true }) - ); - - expect(onThreadLinkSelect).toBeCalled(); - - const threadCount = await findByTestId(threadButton, 'field-thread-count'); - - expect(threadCount).toBeInTheDocument(); - - expect(threadCount).toHaveTextContent( - String(mockEntityFieldThreads[0].count) - ); - }); - - it('Check if tags and glossary-terms are present', async () => { - const { getAllByTestId, findAllByText } = render( - , - { - wrapper: MemoryRouter, - } - ); - - const tagWrapper = getAllByTestId('tags-wrapper')[0]; - fireEvent.click(tagWrapper); - - const tag1 = await findAllByText('TagCat1.Tag1'); - const glossaryTerm1 = await findAllByText('Glossary.Tag1'); - - expect(tag1).toHaveLength(mockEntityTableProp.tableColumns.length); - expect(glossaryTerm1).toHaveLength(mockEntityTableProp.tableColumns.length); - }); - - it('Check if only tags are present', async () => { - (fetchGlossaryTerms as jest.Mock).mockImplementationOnce(() => - Promise.reject() - ); - const { getAllByTestId, findAllByText, queryAllByText } = render( - , - { - wrapper: MemoryRouter, - } - ); - - const tagWrapper = getAllByTestId('tags-wrapper')[0]; - fireEvent.click( - tagWrapper, - new MouseEvent('click', { bubbles: true, cancelable: true }) - ); - - const tag1 = await findAllByText('TagCat1.Tag1'); - const glossaryTerm1 = queryAllByText('Glossary.Tag1'); - - expect(tag1).toHaveLength(mockEntityTableProp.tableColumns.length); - expect(glossaryTerm1).toHaveLength(0); - }); - - it('Check if only glossary terms are present', async () => { - (getTagCategories as jest.Mock).mockImplementationOnce(() => - Promise.reject() - ); - const { getAllByTestId, findAllByText, queryAllByText } = render( - , - { - wrapper: MemoryRouter, - } - ); - - const tagWrapper = getAllByTestId('tags-wrapper')[0]; - fireEvent.click( - tagWrapper, - new MouseEvent('click', { bubbles: true, cancelable: true }) - ); - - const tag1 = queryAllByText('TagCat1.Tag1'); - const glossaryTerm1 = await findAllByText('Glossary.Tag1'); - - expect(tag1).toHaveLength(0); - expect(glossaryTerm1).toHaveLength(mockEntityTableProp.tableColumns.length); - }); - - it('Check that tags and glossary terms are not present', async () => { - (getTagCategories as jest.Mock).mockImplementationOnce(() => - Promise.reject() - ); - (fetchGlossaryTerms as jest.Mock).mockImplementationOnce(() => - Promise.reject() - ); - const { getAllByTestId, queryAllByText } = render( - , - { - wrapper: MemoryRouter, - } - ); - - const tagWrapper = getAllByTestId('tags-wrapper')[0]; - fireEvent.click( - tagWrapper, - new MouseEvent('click', { bubbles: true, cancelable: true }) - ); - - const tag1 = queryAllByText('TagCat1.Tag1'); - const glossaryTerm1 = queryAllByText('Glossary.Tag1'); - - expect(tag1).toHaveLength(0); - expect(glossaryTerm1).toHaveLength(0); - }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx index 6d25b214bb6..eff1cbeb142 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx @@ -21,7 +21,7 @@ import { } from '../../generated/entity/data/table'; import { ThreadType } from '../../generated/entity/feed/thread'; import Searchbar from '../common/searchbar/Searchbar'; -import EntityTable from '../EntityTable/EntityTable.component'; +import EntityTableV1 from '../EntityTable/EntityTable.component'; type Props = { owner?: Table['owner']; @@ -76,7 +76,7 @@ const SchemaTab: FunctionComponent = ({
{columns?.length > 0 ? (
- { }); jest.mock('../EntityTable/EntityTable.component', () => { - return jest.fn().mockReturnValue(

EntityTable

); + return jest.fn().mockReturnValue(

EntityTableV1

); }); const mockTableConstraints = [ diff --git a/openmetadata-ui/src/main/resources/ui/src/components/tags-container/tags-container.tsx b/openmetadata-ui/src/main/resources/ui/src/components/tags-container/tags-container.tsx index afc96afb7e2..e6269f9cc35 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/tags-container/tags-container.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/tags-container/tags-container.tsx @@ -140,7 +140,7 @@ const TagsContainer: FunctionComponent = ({
-
+
{showTags && !editable && ( {showAddTagButton && ( diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/FeedElementUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/FeedElementUtils.tsx index 711e50b16ee..07d70d37a7a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/FeedElementUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/FeedElementUtils.tsx @@ -45,7 +45,7 @@ export const getFieldThreadElement = ( return !isEmpty(threadValue) ? (