mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-13 01:13:39 +00:00
* Fix #6791 : Replaced react-table with antd table component * Updated unit tests * Fixed failing cypress tests * Refactored code, removed code smells. * Fixed code smells
This commit is contained in:
parent
3f491a8578
commit
1b084fd956
@ -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');
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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<ModifiedTableColumn[]>(
|
||||
[]
|
||||
@ -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<EntityTags> = 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 (
|
||||
<button
|
||||
className="tw-w-8 tw-h-8 tw-mr-1 tw-flex-none link-text focus:tw-outline-none tw-opacity-0 group-hover:tw-opacity-100"
|
||||
className="tw-w-8 tw-h-8 tw-mr-1 tw-flex-none link-text focus:tw-outline-none hover-cell-icon"
|
||||
data-testid="request-description"
|
||||
onClick={() =>
|
||||
hasDescription
|
||||
@ -446,7 +432,7 @@ const EntityTable = ({
|
||||
|
||||
return (
|
||||
<button
|
||||
className="tw-w-8 tw-h-8 tw-mr-1 tw-flex-none link-text focus:tw-outline-none tw-opacity-0 group-hover:tw-opacity-100 tw-align-top"
|
||||
className="tw-w-8 tw-h-8 tw-mr-1 tw-flex-none link-text focus:tw-outline-none tw-align-top hover-cell-icon"
|
||||
data-testid="request-tags"
|
||||
onClick={() =>
|
||||
hasTags ? onUpdateTagsHandler(cell) : onRequestTagsHandler(cell)
|
||||
@ -463,113 +449,34 @@ const EntityTable = ({
|
||||
);
|
||||
};
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
const handleTagContainerClick = (row: any) => {
|
||||
if (!editColumnTag) {
|
||||
handleEditColumnTag(row.original, row.id);
|
||||
// Fetch tags and terms only once
|
||||
if (allTags.length === 0 || tagFetchFailed) {
|
||||
fetchTagsAndGlossaryTerms();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!searchText) {
|
||||
setSearchedColumns(tableColumns);
|
||||
} else {
|
||||
const searchCols = searchInColumns(tableColumns, searchText);
|
||||
setSearchedColumns(searchCols);
|
||||
}
|
||||
}, [searchText, tableColumns]);
|
||||
|
||||
useEffect(() => {
|
||||
toggleAllRowsExpanded(isReadOnly);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="tw-table-responsive" id="schemaTable">
|
||||
<table
|
||||
className="tw-w-full"
|
||||
{...getTableProps()}
|
||||
data-testid="entity-table">
|
||||
<thead data-testid="table-header">
|
||||
{/* eslint-disable-next-line */}
|
||||
{headerGroups.map((headerGroup: any, index: number) => (
|
||||
<tr
|
||||
className="tableHead-row"
|
||||
key={index}
|
||||
{...headerGroup.getHeaderGroupProps()}>
|
||||
{/* eslint-disable-next-line */}
|
||||
{headerGroup.headers.map((column: any, index: number) => (
|
||||
<th
|
||||
className={classNames('tableHead-cell', {
|
||||
'tw-w-60':
|
||||
column.id === 'tags' || column.id === 'columnTests',
|
||||
})}
|
||||
data-testid={column.id}
|
||||
key={index}
|
||||
{...column.getHeaderProps()}>
|
||||
{column.render('Header')}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
|
||||
<tbody {...getTableBodyProps()} data-testid="table-body">
|
||||
{/* eslint-disable-next-line */}
|
||||
{rows.map((row: any, index: number) => {
|
||||
prepareRow(row);
|
||||
|
||||
return (
|
||||
<tr
|
||||
className={classNames('tableBody-row')}
|
||||
data-testid="row"
|
||||
key={index}
|
||||
{...row.getRowProps()}>
|
||||
{/* eslint-disable-next-line */}
|
||||
{row.cells.map((cell: any, index: number) => {
|
||||
const getTestStats = (
|
||||
record: ModifiedTableColumn | Column,
|
||||
columnTestLength: number | undefined
|
||||
) => {
|
||||
const columnTests =
|
||||
cell.column.id === 'columnTests'
|
||||
? ((cell.value ?? []) as ColumnTest[])
|
||||
columnTestLength && columnTestLength > 0
|
||||
? record.columnTests ?? []
|
||||
: ([] as ColumnTest[]);
|
||||
const columnTestLength = columnTests.length;
|
||||
|
||||
const failingTests = columnTests.filter((test) =>
|
||||
test.results?.some(
|
||||
(t) => t.testCaseStatus === TestCaseStatus.Failed
|
||||
)
|
||||
test.results?.some((t) => t.testCaseStatus === TestCaseStatus.Failed)
|
||||
);
|
||||
const passingTests = columnTests.filter((test) =>
|
||||
test.results?.some(
|
||||
(t) => t.testCaseStatus === TestCaseStatus.Success
|
||||
)
|
||||
test.results?.some((t) => t.testCaseStatus === TestCaseStatus.Success)
|
||||
);
|
||||
|
||||
return (
|
||||
<td
|
||||
className={classNames(
|
||||
'tableBody-cell tw-group tw-relative tw-align-baseline'
|
||||
)}
|
||||
key={index}
|
||||
{...cell.getCellProps()}>
|
||||
{row.canExpand && cell.column.id === 'name' ? (
|
||||
<span
|
||||
{...row.getToggleRowExpandedProps({})}
|
||||
className="tw-mr-2 tw-cursor-pointer"
|
||||
style={{
|
||||
marginLeft: `${row.depth * 35}px`,
|
||||
}}>
|
||||
<FontAwesomeIcon
|
||||
icon={row.isExpanded ? faCaretDown : faCaretRight}
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
return [failingTests, passingTests];
|
||||
};
|
||||
|
||||
{cell.column.id === 'columnTests' && (
|
||||
<Fragment>
|
||||
const getColumnTestsCell = (
|
||||
columnTestLength: number | undefined,
|
||||
failingTests: ColumnTest[],
|
||||
passingTests: ColumnTest[]
|
||||
) => {
|
||||
return (
|
||||
<>
|
||||
{columnTestLength ? (
|
||||
<Fragment>
|
||||
<>
|
||||
{failingTests.length ? (
|
||||
<div className="tw-flex">
|
||||
<p className="tw-mr-2">
|
||||
@ -583,7 +490,7 @@ const EntityTable = ({
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<Fragment>
|
||||
<>
|
||||
{passingTests.length ? (
|
||||
<div className="tw-flex">
|
||||
<div className="tw-mr-2">
|
||||
@ -597,43 +504,44 @@ const EntityTable = ({
|
||||
) : (
|
||||
<p>{`${columnTestLength} tests`}</p>
|
||||
)}
|
||||
</Fragment>
|
||||
</>
|
||||
)}
|
||||
</Fragment>
|
||||
</>
|
||||
) : (
|
||||
'--'
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
{cell.column.id === 'dataTypeDisplay' && (
|
||||
const getDataTypeDisplayCell = (record: ModifiedTableColumn | Column) => {
|
||||
return (
|
||||
<>
|
||||
{cell.value ? (
|
||||
{record.dataTypeDisplay ? (
|
||||
<>
|
||||
{isReadOnly ? (
|
||||
<div className="tw-flex tw-flex-wrap tw-w-60 tw-overflow-x-auto">
|
||||
<RichTextEditorPreviewer
|
||||
markdown={cell.value.toLowerCase()}
|
||||
markdown={record.dataTypeDisplay.toLowerCase()}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{cell.value.length > 25 ? (
|
||||
{record.dataTypeDisplay.length > 25 ? (
|
||||
<span>
|
||||
<PopOver
|
||||
html={
|
||||
<div className="tw-break-words">
|
||||
<span>
|
||||
{cell.value.toLowerCase()}
|
||||
</span>
|
||||
<span>{record.dataTypeDisplay.toLowerCase()}</span>
|
||||
</div>
|
||||
}
|
||||
key="pop-over"
|
||||
position="bottom"
|
||||
theme="light"
|
||||
trigger="click">
|
||||
<div className="tw-cursor-pointer tw-underline tw-inline-block">
|
||||
<RichTextEditorPreviewer
|
||||
markdown={`${cell.value
|
||||
markdown={`${record.dataTypeDisplay
|
||||
.slice(0, 20)
|
||||
.toLowerCase()}...`}
|
||||
/>
|
||||
@ -641,7 +549,7 @@ const EntityTable = ({
|
||||
</PopOver>
|
||||
</span>
|
||||
) : (
|
||||
cell.value.toLowerCase()
|
||||
record.dataTypeDisplay.toLowerCase()
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
@ -650,109 +558,35 @@ const EntityTable = ({
|
||||
'--'
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
);
|
||||
};
|
||||
|
||||
{cell.column.id === 'tags' && (
|
||||
<>
|
||||
{isReadOnly ? (
|
||||
<div className="tw-flex tw-flex-wrap">
|
||||
<TagsViewer
|
||||
sizeCap={-1}
|
||||
tags={cell.value || []}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
data-testid="tags-wrapper"
|
||||
onClick={() => handleTagContainerClick(row)}>
|
||||
<NonAdminAction
|
||||
html={getHtmlForNonAdminAction(Boolean(owner))}
|
||||
isOwner={hasEditAccess}
|
||||
permission={Operation.EditTags}
|
||||
position="left"
|
||||
trigger="click">
|
||||
<TagsContainer
|
||||
showAddTagButton
|
||||
editable={editColumnTag?.index === row.id}
|
||||
isLoading={
|
||||
isTagLoading &&
|
||||
editColumnTag?.index === row.id
|
||||
}
|
||||
selectedTags={cell.value || []}
|
||||
size="small"
|
||||
tagList={allTags}
|
||||
type="label"
|
||||
onCancel={() => {
|
||||
handleTagSelection();
|
||||
}}
|
||||
onSelectionChange={(tags) => {
|
||||
handleTagSelection(tags, row.original.name);
|
||||
}}
|
||||
/>
|
||||
</NonAdminAction>
|
||||
<div className="tw-mt-1">
|
||||
{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
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{cell.column.id === 'description' && (
|
||||
<div>
|
||||
const getDescriptionCell = (
|
||||
index: number,
|
||||
record: ModifiedTableColumn | Column
|
||||
) => {
|
||||
return (
|
||||
<div className="hover-icon-group">
|
||||
<div className="tw-inline-block">
|
||||
<div
|
||||
className="tw-flex"
|
||||
data-testid="description"
|
||||
id={`column-description-${index}`}>
|
||||
<div>
|
||||
{cell.value ? (
|
||||
<RichTextEditorPreviewer
|
||||
markdown={cell.value}
|
||||
/>
|
||||
{record?.description ? (
|
||||
<RichTextEditorPreviewer markdown={record?.description} />
|
||||
) : (
|
||||
<span className="tw-no-description">
|
||||
No description{' '}
|
||||
</span>
|
||||
<span className="tw-no-description">No description</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="tw-flex tw--mt-2">
|
||||
{!isReadOnly ? (
|
||||
<Fragment>
|
||||
{checkPermission() && (
|
||||
<>
|
||||
<button
|
||||
className="tw-self-start tw-w-8 tw-h-8 tw-opacity-0 tw-ml-1 group-hover:tw-opacity-100 focus:tw-outline-none tw-flex-none"
|
||||
onClick={() =>
|
||||
handleUpdate(row.original, row.id)
|
||||
}>
|
||||
className="tw-self-start tw-w-8 tw-h-8 tw-ml-1 focus:tw-outline-none tw-flex-none hover-cell-icon"
|
||||
onClick={() => handleUpdate(record, index)}>
|
||||
<SVGIcons
|
||||
alt="edit"
|
||||
icon="icon-edit"
|
||||
@ -760,31 +594,32 @@ const EntityTable = ({
|
||||
width="14px"
|
||||
/>
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{getRequestDescriptionElement(cell)}
|
||||
{getRequestDescriptionElement(record)}
|
||||
{getFieldThreadElement(
|
||||
getColumnName(cell),
|
||||
getColumnName(record),
|
||||
EntityField.DESCRIPTION,
|
||||
entityFieldThreads as EntityFieldThreads[],
|
||||
onThreadLinkSelect,
|
||||
EntityType.TABLE,
|
||||
entityFqn,
|
||||
`columns${ENTITY_LINK_SEPARATOR}${getColumnName(
|
||||
cell
|
||||
record
|
||||
)}${ENTITY_LINK_SEPARATOR}description`,
|
||||
Boolean(cell.value)
|
||||
Boolean(record)
|
||||
)}
|
||||
{getFieldThreadElement(
|
||||
getColumnName(cell),
|
||||
getColumnName(record),
|
||||
EntityField.DESCRIPTION,
|
||||
entityFieldTasks as EntityFieldThreads[],
|
||||
onThreadLinkSelect,
|
||||
EntityType.TABLE,
|
||||
entityFqn,
|
||||
`columns${ENTITY_LINK_SEPARATOR}${getColumnName(
|
||||
cell
|
||||
record
|
||||
)}${ENTITY_LINK_SEPARATOR}description`,
|
||||
Boolean(cell.value),
|
||||
Boolean(record),
|
||||
ThreadType.Task
|
||||
)}
|
||||
</Fragment>
|
||||
@ -792,56 +627,40 @@ const EntityTable = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{checkIfJoinsAvailable(row.original.name) && (
|
||||
<div
|
||||
className="tw-mt-3"
|
||||
data-testid="frequently-joined-columns">
|
||||
{checkIfJoinsAvailable(record?.name) && (
|
||||
<div className="tw-mt-3" data-testid="frequently-joined-columns">
|
||||
<span className="tw-text-grey-muted tw-mr-1">
|
||||
Frequently joined columns:
|
||||
</span>
|
||||
<span>
|
||||
{getFrequentlyJoinedWithColumns(
|
||||
row.original.name
|
||||
)
|
||||
{getFrequentlyJoinedWithColumns(record?.name)
|
||||
.slice(0, 3)
|
||||
.map((columnJoin, index) => (
|
||||
<Fragment key={index}>
|
||||
{index > 0 && (
|
||||
<span className="tw-mr-1">,</span>
|
||||
)}
|
||||
{index > 0 && <span className="tw-mr-1">,</span>}
|
||||
<Link
|
||||
className="link-text"
|
||||
to={getTableDetailsPath(
|
||||
getTableFQNFromColumnFQN(
|
||||
columnJoin?.fullyQualifiedName as string
|
||||
),
|
||||
getTableFQNFromColumnFQN(columnJoin.fullyQualifiedName),
|
||||
getPartialNameFromTableFQN(
|
||||
columnJoin?.fullyQualifiedName as string,
|
||||
columnJoin.fullyQualifiedName,
|
||||
[FqnPart.Column]
|
||||
)
|
||||
)}>
|
||||
{getPartialNameFromTableFQN(
|
||||
columnJoin?.fullyQualifiedName as string,
|
||||
[
|
||||
FqnPart.Database,
|
||||
FqnPart.Table,
|
||||
FqnPart.Column,
|
||||
],
|
||||
columnJoin.fullyQualifiedName,
|
||||
[FqnPart.Database, FqnPart.Table, FqnPart.Column],
|
||||
FQN_SEPARATOR_CHAR
|
||||
)}
|
||||
</Link>
|
||||
</Fragment>
|
||||
))}
|
||||
|
||||
{getFrequentlyJoinedWithColumns(
|
||||
row.original.name
|
||||
).length > 3 && (
|
||||
{getFrequentlyJoinedWithColumns(record?.name).length > 3 && (
|
||||
<PopOver
|
||||
html={
|
||||
<div className="tw-text-left">
|
||||
{getFrequentlyJoinedWithColumns(
|
||||
row.original.name
|
||||
)
|
||||
{getFrequentlyJoinedWithColumns(record?.name)
|
||||
?.slice(3)
|
||||
.map((columnJoin, index) => (
|
||||
<Fragment key={index}>
|
||||
@ -849,15 +668,15 @@ const EntityTable = ({
|
||||
className="link-text tw-block tw-py-1"
|
||||
href={getTableDetailsPath(
|
||||
getTableFQNFromColumnFQN(
|
||||
columnJoin?.fullyQualifiedName as string
|
||||
columnJoin?.fullyQualifiedName
|
||||
),
|
||||
getPartialNameFromTableFQN(
|
||||
columnJoin?.fullyQualifiedName as string,
|
||||
columnJoin?.fullyQualifiedName,
|
||||
[FqnPart.Column]
|
||||
)
|
||||
)}>
|
||||
{getPartialNameFromTableFQN(
|
||||
columnJoin?.fullyQualifiedName as string,
|
||||
columnJoin?.fullyQualifiedName,
|
||||
[
|
||||
FqnPart.Database,
|
||||
FqnPart.Table,
|
||||
@ -872,46 +691,248 @@ const EntityTable = ({
|
||||
position="bottom"
|
||||
theme="light"
|
||||
trigger="click">
|
||||
<span className="show-more tw-ml-1 tw-underline">
|
||||
...
|
||||
</span>
|
||||
<span className="show-more tw-ml-1 tw-underline">...</span>
|
||||
</PopOver>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getTagsCell = (index: number, record: ModifiedTableColumn | Column) => {
|
||||
return (
|
||||
<div className="hover-icon-group">
|
||||
{isReadOnly ? (
|
||||
<div className="tw-flex tw-flex-wrap">
|
||||
<TagsViewer sizeCap={-1} tags={record?.tags || []} />
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={classNames(
|
||||
`tw-flex tw-justify-content`,
|
||||
editColumnTag?.index === index || !isEmpty(record.tags)
|
||||
? 'tw-flex-col tw-items-start'
|
||||
: 'tw-items-center'
|
||||
)}
|
||||
{cell.column.id === 'name' && (
|
||||
data-testid="tags-wrapper"
|
||||
onClick={() => {
|
||||
if (!editColumnTag) {
|
||||
handleEditColumnTag(record, index);
|
||||
// Fetch tags and terms only once
|
||||
if (allTags.length === 0 || tagFetchFailed) {
|
||||
fetchTagsAndGlossaryTerms();
|
||||
}
|
||||
}
|
||||
}}>
|
||||
<NonAdminAction
|
||||
html={getHtmlForNonAdminAction(Boolean(owner))}
|
||||
isOwner={hasEditAccess}
|
||||
permission={Operation.EditTags}
|
||||
position="left"
|
||||
trigger="click">
|
||||
<TagsContainer
|
||||
showAddTagButton
|
||||
editable={editColumnTag?.index === index}
|
||||
isLoading={isTagLoading && editColumnTag?.index === index}
|
||||
selectedTags={record?.tags || []}
|
||||
size="small"
|
||||
tagList={allTags}
|
||||
type="label"
|
||||
onCancel={() => {
|
||||
handleTagSelection();
|
||||
}}
|
||||
onSelectionChange={(tags) => {
|
||||
handleTagSelection(tags, record?.name);
|
||||
}}
|
||||
/>
|
||||
</NonAdminAction>
|
||||
<div className="tw-mt-1 tw-flex">
|
||||
{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
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<Fragment>
|
||||
{isReadOnly ? (
|
||||
<div className="tw-inline-block">
|
||||
<RichTextEditorPreviewer markdown={cell.value} />
|
||||
<RichTextEditorPreviewer markdown={record.name} />
|
||||
</div>
|
||||
) : (
|
||||
<span
|
||||
style={{
|
||||
paddingLeft: `${
|
||||
row.canExpand ? '0px' : `${row.depth * 35}px`
|
||||
}`,
|
||||
}}>
|
||||
{prepareConstraintIcon(
|
||||
cell.value,
|
||||
row.original.constraint
|
||||
)}
|
||||
{cell.render('Cell')}
|
||||
<span>
|
||||
{prepareConstraintIcon(record.name, record.constraint)}
|
||||
<span className="tw-ml-4">{record.name}</span>
|
||||
</span>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
}
|
||||
},
|
||||
[editColumnTag, isTagLoading, handleUpdate, handleTagSelection]
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
accessor: 'name',
|
||||
ellipsis: true,
|
||||
width: 180,
|
||||
render: (
|
||||
_: Array<unknown>,
|
||||
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<unknown>,
|
||||
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<unknown>,
|
||||
record: ModifiedTableColumn,
|
||||
index: number
|
||||
) => {
|
||||
return renderCell(TABLE_HEADERS_V1.columnTests, record, index);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Description',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
accessor: 'description',
|
||||
render: (
|
||||
_: Array<unknown>,
|
||||
record: ModifiedTableColumn,
|
||||
index: number
|
||||
) => renderCell(TABLE_HEADERS_V1.description, record, index),
|
||||
},
|
||||
{
|
||||
title: 'Tags',
|
||||
dataIndex: 'tags',
|
||||
key: 'tags',
|
||||
accessor: 'tags',
|
||||
width: 272,
|
||||
render: (
|
||||
_: Array<unknown>,
|
||||
record: ModifiedTableColumn | Column,
|
||||
index: number
|
||||
) => renderCell(TABLE_HEADERS_V1.tags, record, index),
|
||||
},
|
||||
],
|
||||
[editColumnTag, isTagLoading, renderCell]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!searchText) {
|
||||
setSearchedColumns(tableColumns);
|
||||
} else {
|
||||
const searchCols = searchInColumns(tableColumns, searchText);
|
||||
setSearchedColumns(searchCols);
|
||||
}
|
||||
}, [searchText, tableColumns]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table
|
||||
columns={columns}
|
||||
data-testid="entity-table"
|
||||
dataSource={data}
|
||||
expandable={{
|
||||
defaultExpandedRowKeys: [],
|
||||
expandIcon: ({ expanded, onExpand, record }) =>
|
||||
record.children ? (
|
||||
<FontAwesomeIcon
|
||||
className="tw-mr-2 tw-cursor-pointer"
|
||||
icon={expanded ? faCaretDown : faCaretRight}
|
||||
onClick={(e) =>
|
||||
onExpand(
|
||||
record,
|
||||
e as unknown as React.MouseEvent<HTMLElement, MouseEvent>
|
||||
)
|
||||
}
|
||||
/>
|
||||
) : null,
|
||||
}}
|
||||
pagination={false}
|
||||
size="small"
|
||||
/>
|
||||
{editColumn && (
|
||||
<ModalWithMarkdownEditor
|
||||
header={`Edit column: "${editColumn.column.name}"`}
|
||||
@ -919,9 +940,10 @@ const EntityTable = ({
|
||||
value={editColumn.column.description as string}
|
||||
onCancel={closeEditColumnModal}
|
||||
onSave={handleEditColumnChange}
|
||||
// expandable={}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -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',
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,7 @@
|
||||
export const TABLE_HEADERS_V1 = {
|
||||
name: 'name',
|
||||
dataTypeDisplay: 'dataTypeDisplay',
|
||||
columnTests: 'columnTests',
|
||||
description: 'description',
|
||||
tags: 'tags',
|
||||
};
|
||||
@ -0,0 +1,10 @@
|
||||
.hover-icon-group {
|
||||
.hover-cell-icon {
|
||||
opacity: 0;
|
||||
}
|
||||
&:hover {
|
||||
.hover-cell-icon {
|
||||
opacity: 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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(<i>Icon</i>),
|
||||
}));
|
||||
|
||||
jest.mock('@fortawesome/free-solid-svg-icons', () => ({
|
||||
faCaretDown: jest.fn().mockReturnValue(<i>faCaretDown</i>),
|
||||
faCaretRight: jest.fn().mockReturnValue(<i>faCaretRight</i>),
|
||||
}));
|
||||
|
||||
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(<p>RichTextEditorPreviewer</p>);
|
||||
});
|
||||
|
||||
jest.mock('../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor', () => ({
|
||||
ModalWithMarkdownEditor: jest.fn().mockReturnValue(<p>EditorModal</p>),
|
||||
}));
|
||||
|
||||
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(<p>TagViewer</p>);
|
||||
});
|
||||
|
||||
jest.mock('../tags/tags', () => {
|
||||
return jest.fn().mockReturnValue(<p>Tag</p>);
|
||||
});
|
||||
@ -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 }) => <div>{children}</div>),
|
||||
|
||||
Table: jest.fn().mockImplementation(({ columns, dataSource }) => (
|
||||
<table data-testid="entity-table">
|
||||
<thead>
|
||||
<tr>
|
||||
{(columns as ColumnsType<ColumnDataType>).map((col) => (
|
||||
<th key={col.key}>{col.title}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody key="tbody">
|
||||
{dataSource.map((row: ModifiedTableColumn | Column, i: number) => (
|
||||
<tr key={i}>
|
||||
{(columns as ColumnsType<ColumnDataType>).map((col, index) => (
|
||||
<td key={col.key}>
|
||||
{col.render ? col.render(row, dataSource, index) : 'alt'}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)),
|
||||
}));
|
||||
|
||||
describe('Test EntityTable Component', () => {
|
||||
it('Check if it has all child elements', async () => {
|
||||
const { container } = render(<EntityTable {...mockEntityTableProp} />, {
|
||||
it('Initially, Table should load', async () => {
|
||||
render(<EntityTableV1 {...mockEntityTableProp} />, {
|
||||
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(<EntityTable {...mockEntityTableProp} />, {
|
||||
render(<EntityTableV1 {...mockEntityTableProp} />, {
|
||||
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(<EntityTable {...mockEntityTableProp} />, {
|
||||
render(<EntityTableV1 {...mockEntityTableProp} />, {
|
||||
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(
|
||||
<EntityTable
|
||||
{...mockEntityTableProp}
|
||||
entityFieldThreads={mockEntityFieldThreads}
|
||||
/>,
|
||||
{
|
||||
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(
|
||||
<EntityTable {...mockEntityTableProp} />,
|
||||
{
|
||||
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(
|
||||
<EntityTable {...mockEntityTableProp} />,
|
||||
{
|
||||
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(
|
||||
<EntityTable {...mockEntityTableProp} />,
|
||||
{
|
||||
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(
|
||||
<EntityTable {...mockEntityTableProp} />,
|
||||
{
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
@ -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<Props> = ({
|
||||
<div className="row">
|
||||
{columns?.length > 0 ? (
|
||||
<div className="col-sm-12">
|
||||
<EntityTable
|
||||
<EntityTableV1
|
||||
columnName={columnName}
|
||||
entityFieldTasks={entityFieldTasks}
|
||||
entityFieldThreads={entityFieldThreads}
|
||||
|
||||
@ -76,7 +76,7 @@ jest.mock('../SampleDataTable/SampleDataTable.component', () => {
|
||||
});
|
||||
|
||||
jest.mock('../EntityTable/EntityTable.component', () => {
|
||||
return jest.fn().mockReturnValue(<p>EntityTable</p>);
|
||||
return jest.fn().mockReturnValue(<p>EntityTableV1</p>);
|
||||
});
|
||||
|
||||
const mockTableConstraints = [
|
||||
|
||||
@ -140,7 +140,7 @@ const TagsContainer: FunctionComponent<TagsContainerProps> = ({
|
||||
<div
|
||||
className={classNames('tw-cursor-pointer', containerClass)}
|
||||
data-testid="tag-container">
|
||||
<div>
|
||||
<div className="tw-flex tw-flex-wrap">
|
||||
{showTags && !editable && (
|
||||
<Fragment>
|
||||
{showAddTagButton && (
|
||||
|
||||
@ -45,7 +45,7 @@ export const getFieldThreadElement = (
|
||||
|
||||
return !isEmpty(threadValue) ? (
|
||||
<button
|
||||
className="link-text tw-self-start tw-w-8 tw-h-8 tw-flex-none tw-mx-1 tw-opacity-0 group-hover:tw-opacity-100"
|
||||
className="link-text tw-self-start tw-w-8 tw-h-8 tw-flex-none tw-mx-1 hover-cell-icon"
|
||||
data-testid="field-thread"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@ -70,7 +70,7 @@ export const getFieldThreadElement = (
|
||||
<Fragment>
|
||||
{entityType && entityFqn && entityField && flag && !isTaskType ? (
|
||||
<button
|
||||
className="link-text tw-self-start tw-w-8 tw-h-8 tw-flex-none tw-mx-1 tw-opacity-0 group-hover:tw-opacity-100"
|
||||
className="link-text tw-self-start tw-w-8 tw-h-8 tw-flex-none tw-mx-1 hover-cell-icon"
|
||||
data-testid="start-field-thread"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user