From bcc8cc1e393769ba1389b4f66f7a58e59117834e Mon Sep 17 00:00:00 2001 From: Dhruv Parmar <83108871+dhruvjsx@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:25:26 +0530 Subject: [PATCH] Fix(ui) : Updated tag and selection group style (#22708) * Updated tag and slection group style * added add testCase drawer in the contract form * added pagination * fixed unit tests * fix the pagination in table schema form --------- Co-authored-by: Ashish Gupta --- .../AddDataContract/add-data-contract.less | 3 + .../ContractDetailTab/ContractDetail.tsx | 10 +- .../ContractDetailTab/contract-detail.less | 9 + .../ContractQualityFormTab.tsx | 228 +++++++++++++----- .../contract-quality-form-tab.less | 28 +++ .../ContractScehmaFormTab.tsx | 149 ++++++++++-- .../components/TestCaseFormV1.test.tsx | 5 + .../BundleSuiteForm/BundleSuiteForm.test.tsx | 5 + .../TestCaseResultTab.test.tsx | 5 + .../TableProfilerProvider.test.tsx | 5 + .../NodeChildren.component.test.tsx | 5 + .../TableSummary/TableSummary.test.tsx | 5 + .../SelectionCardGroup/SelectionCardGroup.tsx | 2 +- .../IncidentManagerDetailPage.test.tsx | 5 + .../TestSuiteDetailsPage.test.tsx | 5 + .../TestSuiteIngestionPage.test.tsx | 5 + .../utils/DataQuality/DataQualityUtils.tsx | 23 ++ 17 files changed, 409 insertions(+), 88 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractQualityFormTab/contract-quality-form-tab.less diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/AddDataContract/add-data-contract.less b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/AddDataContract/add-data-contract.less index 8e63b5922fe..933b27d6b33 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/AddDataContract/add-data-contract.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/AddDataContract/add-data-contract.less @@ -126,6 +126,9 @@ border-radius: 8px; border: 1px solid @border-color-7; box-shadow: @button-box-shadow-default; + display: flex; + flex-direction: column; + gap: @size-lg; } // Table styling diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractDetailTab/ContractDetail.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractDetailTab/ContractDetail.tsx index 862b05b8aa4..02cc1dc9d08 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractDetailTab/ContractDetail.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractDetailTab/ContractDetail.tsx @@ -142,7 +142,11 @@ const ContractDetail: React.FC<{ title: t('label.type'), dataIndex: 'dataType', key: 'dataType', - render: (type: string) => {type}, + render: (type: string) => ( + + {type} + + ), }, { title: t('label.constraint-plural'), @@ -151,7 +155,9 @@ const ContractDetail: React.FC<{ render: (constraint: string) => (
{constraint ? ( - {constraint} + + {constraint} + ) : ( {NO_DATA_PLACEHOLDER} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractDetailTab/contract-detail.less b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractDetailTab/contract-detail.less index c4bddb01f7e..a07ecd26bcd 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractDetailTab/contract-detail.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractDetailTab/contract-detail.less @@ -243,3 +243,12 @@ } } } +.ant-tag.custom-tag { + border-radius: 6px; + padding: 2px @size-xs 2px 6px; + font-size: @size-sm; + font-weight: @font-medium; + max-width: 100%; + text-wrap: auto; + border: none; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractQualityFormTab/ContractQualityFormTab.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractQualityFormTab/ContractQualityFormTab.tsx index 7d28b2a474e..3d104fb7a05 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractQualityFormTab/ContractQualityFormTab.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractQualityFormTab/ContractQualityFormTab.tsx @@ -11,26 +11,39 @@ * limitations under the License. */ -import { ArrowLeftOutlined } from '@ant-design/icons'; -import { Button, Card, Radio, Typography } from 'antd'; -import { AxiosError } from 'axios'; -import { useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { EntityType } from '../../../enums/entity.enum'; -import { Table as TableType } from '../../../generated/entity/data/table'; -import { TestCase, TestCaseStatus } from '../../../generated/tests/testCase'; -import { EntityReference } from '../../../generated/type/entityReference'; -import { usePaging } from '../../../hooks/paging/usePaging'; -import { listTestCases, TestCaseType } from '../../../rest/testAPI'; -import { showErrorToast } from '../../../utils/ToastUtils'; -import Table from '../../common/Table/Table'; - +import Icon, { ArrowLeftOutlined } from '@ant-design/icons'; +import { Button, Card, Typography } from 'antd'; import { ColumnsType } from 'antd/lib/table'; +import { AxiosError } from 'axios'; import { toLower } from 'lodash'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactComponent as PlusIcon } from '../../../assets/svg/x-colored.svg'; +import { DEFAULT_SORT_ORDER } from '../../../constants/profiler.constant'; +import { EntityType, TabSpecificField } from '../../../enums/entity.enum'; import { DataContract } from '../../../generated/entity/data/dataContract'; +import { Table as TableType } from '../../../generated/entity/data/table'; +import { TestCase, TestCaseResult } from '../../../generated/tests/testCase'; +import { EntityReference } from '../../../generated/type/entityReference'; +import { Include } from '../../../generated/type/include'; +import { usePaging } from '../../../hooks/paging/usePaging'; +import { + getListTestCaseBySearch, + ListTestCaseParamsBySearch, + TestCaseType, +} from '../../../rest/testAPI'; +import { TEST_LEVEL_OPTIONS } from '../../../utils/DataQuality/DataQualityUtils'; +import { generateEntityLink } from '../../../utils/TableUtils'; +import { showErrorToast } from '../../../utils/ToastUtils'; +import { PagingHandlerParams } from '../../common/NextPrevious/NextPrevious.interface'; +import { SelectionCard } from '../../common/SelectionCardGroup/SelectionCardGroup'; import StatusBadge from '../../common/StatusBadge/StatusBadge.component'; import { StatusType } from '../../common/StatusBadge/StatusBadge.interface'; +import Table from '../../common/Table/Table'; import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider'; +import TestCaseFormV1 from '../../DataQuality/AddDataQualityTest/components/TestCaseFormV1'; +import { TestLevel } from '../../DataQuality/AddDataQualityTest/components/TestCaseFormV1.interface'; +import './contract-quality-form-tab.less'; export const ContractQualityFormTab: React.FC<{ selectedQuality: string[]; @@ -41,47 +54,38 @@ export const ContractQualityFormTab: React.FC<{ const [testType, setTestType] = useState(TestCaseType.table); const [allTestCases, setAllTestCases] = useState([]); const { data: table } = useGenericContext(); - const { pageSize, handlePagingChange } = usePaging(); const [isTestsLoading, setIsTestsLoading] = useState(false); const [selectedKeys, setSelectedKeys] = useState( selectedQuality ?? [] ); + const [isTestCaseDrawerOpen, setIsTestCaseDrawerOpen] = + useState(false); + const { + currentPage, + pageSize, + handlePageChange, + handlePageSizeChange, + showPagination, + paging, + handlePagingChange, + } = usePaging(); const { t } = useTranslation(); - const columns: ColumnsType = useMemo( - () => [ - { - title: t('label.name'), - dataIndex: 'name', - }, - { - title: t('label.status'), - dataIndex: 'testCaseStatus', - key: 'testCaseStatus', - render: (testCaseStatus: TestCaseStatus) => { - return ( - - ); - }, - }, - ], - [] - ); - - const fetchAllTests = async () => { + const fetchAllTests = async (params?: ListTestCaseParamsBySearch) => { if (!table?.fullyQualifiedName) { return; } setIsTestsLoading(true); try { - const { data, paging } = await listTestCases({ - entityFQN: table.fullyQualifiedName, + const { data, paging } = await getListTestCaseBySearch({ + ...DEFAULT_SORT_ORDER, + ...params, testCaseType: testType, + fields: [TabSpecificField.TEST_CASE_RESULT], + entityLink: generateEntityLink(table.fullyQualifiedName ?? ''), + includeAllTests: true, limit: pageSize, + include: Include.NonDeleted, }); setAllTestCases(data); @@ -93,9 +97,77 @@ export const ContractQualityFormTab: React.FC<{ } }; - useEffect(() => { + const handleTestPageChange = useCallback( + ({ currentPage }: PagingHandlerParams) => { + fetchAllTests({ + offset: (currentPage - 1) * pageSize, + }); + + handlePageChange(currentPage); + }, + [pageSize, fetchAllTests, handlePageChange] + ); + + const handleOpenTestCaseDrawer = useCallback(() => { + setIsTestCaseDrawerOpen(true); + }, []); + + const handleCloseTestCaseDrawer = useCallback(() => { + setIsTestCaseDrawerOpen(false); + }, []); + + const handleTestCaseSubmit = useCallback(() => { + handleCloseTestCaseDrawer(); fetchAllTests(); - }, [testType]); + }, [handleCloseTestCaseDrawer, fetchAllTests]); + + const columns: ColumnsType = useMemo( + () => [ + { + title: t('label.name'), + dataIndex: 'name', + }, + { + title: t('label.status'), + dataIndex: 'testCaseResult', + key: 'testCaseResult', + render: (result: TestCaseResult, record) => { + return result?.testCaseStatus ? ( + + ) : ( + '--' + ); + }, + }, + ], + [] + ); + + const paginationProps = useMemo( + () => ({ + currentPage, + showPagination, + isLoading: isTestsLoading, + isNumberBased: false, + pageSize, + paging, + pagingHandler: handleTestPageChange, + onShowSizeChange: handlePageSizeChange, + }), + [ + currentPage, + showPagination, + isTestsLoading, + pageSize, + paging, + handleTestPageChange, + handlePageSizeChange, + ] + ); const handleSelection = (selectedRowKeys: string[]) => { const qualityExpectations = selectedRowKeys.map((id) => { @@ -114,31 +186,47 @@ export const ContractQualityFormTab: React.FC<{ }); }; + useEffect(() => { + fetchAllTests(); + }, [testType]); + return ( - -
- - {t('label.quality')} - - - {t('message.quality-contract-description')} - + +
+
+ + {t('label.quality')} + + + {t('message.quality-contract-description')} + +
+ +
-
- setTestType(e.target.value)}> - - {t('label.table')} - - - {t('label.column')} - - +
+
+ {TEST_LEVEL_OPTIONS.map((option) => ( + setTestType(option.value as TestCaseType)} + /> + ))} +
+ + {isTestCaseDrawerOpen && ( + + )} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractQualityFormTab/contract-quality-form-tab.less b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractQualityFormTab/contract-quality-form-tab.less new file mode 100644 index 00000000000..231975733b0 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractQualityFormTab/contract-quality-form-tab.less @@ -0,0 +1,28 @@ +/* + * Copyright 2025 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * 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 (reference) url('../../../styles/variables.less'); + +.contract-quality-form-tab-container { + .add-test-case-button { + font-weight: 600; + color: @grey-700; + border: 1px solid @grey-300 !important; + box-shadow: 0px 1px 2px rgba(10, 13, 18, 0.05), + inset 0px -2px 0px rgba(10, 13, 18, 0.05); + + .anticon { + transform: rotate(45deg); + } + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSchemaFormTab/ContractScehmaFormTab.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSchemaFormTab/ContractScehmaFormTab.tsx index 508217ba35c..0bfbc2b8807 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSchemaFormTab/ContractScehmaFormTab.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSchemaFormTab/ContractScehmaFormTab.tsx @@ -11,25 +11,32 @@ * limitations under the License. */ import { ArrowLeftOutlined, ArrowRightOutlined } from '@ant-design/icons'; -import { Button, Card, Typography } from 'antd'; +import { Button, Card, Tag, Typography } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import { isEmpty } from 'lodash'; import { Key, useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { NO_DATA_PLACEHOLDER } from '../../../constants/constants'; +import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants'; +import { + NO_DATA_PLACEHOLDER, + PAGE_SIZE_MEDIUM, +} from '../../../constants/constants'; import { TABLE_COLUMNS_KEYS } from '../../../constants/TableKeys.constants'; -import { EntityType } from '../../../enums/entity.enum'; +import { EntityType, FqnPart } from '../../../enums/entity.enum'; import { DataContract } from '../../../generated/entity/data/dataContract'; import { Column } from '../../../generated/entity/data/table'; import { TagSource } from '../../../generated/tests/testCase'; import { TagLabel } from '../../../generated/type/tagLabel'; +import { usePaging } from '../../../hooks/paging/usePaging'; import { useFqn } from '../../../hooks/useFqn'; import { getTableColumnsByFQN } from '../../../rest/tableAPI'; +import { getPartialNameFromTableFQN } from '../../../utils/CommonUtils'; import { getEntityName, highlightSearchArrayElement, } from '../../../utils/EntityUtils'; import { pruneEmptyChildren } from '../../../utils/TableUtils'; +import { PagingHandlerParams } from '../../common/NextPrevious/NextPrevious.interface'; import Table from '../../common/Table/Table'; import { TableCellRendered } from '../../Database/SchemaTable/SchemaTable.interface'; import TableTags from '../../Database/TableTags/TableTags.component'; @@ -44,29 +51,103 @@ export const ContractSchemaFormTab: React.FC<{ }> = ({ selectedSchema, onNext, onChange, onPrev, nextLabel, prevLabel }) => { const { t } = useTranslation(); const { fqn } = useFqn(); - const [schema, setSchema] = useState([]); + const [allColumns, setAllColumns] = useState([]); const [selectedKeys, setSelectedKeys] = useState(selectedSchema); + const [isLoading, setIsLoading] = useState(false); + const tableFqn = useMemo( + () => + getPartialNameFromTableFQN( + fqn, + [FqnPart.Service, FqnPart.Database, FqnPart.Schema, FqnPart.Table], + FQN_SEPARATOR_CHAR + ), + [fqn] + ); + + const { + currentPage, + pageSize, + paging, + handlePageChange, + handlePageSizeChange, + handlePagingChange, + showPagination, + } = usePaging(PAGE_SIZE_MEDIUM); const handleChangeTable = useCallback( (selectedRowKeys: Key[]) => { setSelectedKeys(selectedRowKeys as string[]); onChange({ - schema: schema.filter((column) => + schema: allColumns.filter((column) => selectedRowKeys.includes(column.name) ), }); }, - [schema, onChange] + [allColumns, onChange] ); - const fetchTableColumns = useCallback(async () => { - const response = await getTableColumnsByFQN(fqn); - setSchema(pruneEmptyChildren(response.data)); - }, [fqn]); + const fetchTableColumns = useCallback( + async (page = 1) => { + if (!tableFqn) { + return; + } - useEffect(() => { - fetchTableColumns(); - }, [fqn]); + setIsLoading(true); + try { + const offset = (page - 1) * pageSize; + + const response = await getTableColumnsByFQN(tableFqn, { + limit: pageSize, + offset: offset, + fields: 'tags', + }); + + const prunedColumns = pruneEmptyChildren(response.data); + setAllColumns(prunedColumns); + handlePagingChange(response.paging); + } catch { + // Set empty state if API fails + setAllColumns([]); + handlePagingChange({ + offset: 1, + limit: pageSize, + total: 0, + }); + } + setIsLoading(false); + }, + [tableFqn, pageSize] + ); + + const handleColumnsPageChange = useCallback( + ({ currentPage }: PagingHandlerParams) => { + fetchTableColumns(currentPage); + handlePageChange(currentPage); + }, + [fetchTableColumns] + ); + + const paginationProps = useMemo( + () => ({ + currentPage, + showPagination, + isLoading: isLoading, + isNumberBased: false, + pageSize, + paging, + pagingHandler: handleColumnsPageChange, + onShowSizeChange: handlePageSizeChange, + }), + [ + currentPage, + showPagination, + isLoading, + pageSize, + paging, + handlePageSizeChange, + handleColumnsPageChange, + ] + ); const renderDataTypeDisplay: TableCellRendered = ( dataTypeDisplay, @@ -81,11 +162,29 @@ export const ContractSchemaFormTab: React.FC<{ } return ( - + {highlightSearchArrayElement(dataTypeDisplay, '')} - + + ); + }; + + const renderConstraint: TableCellRendered = ( + constraint + ) => { + if (isEmpty(constraint)) { + return NO_DATA_PLACEHOLDER; + } + + return ( + + {constraint} + ); }; @@ -114,7 +213,7 @@ export const ContractSchemaFormTab: React.FC<{ render: (tags: TagLabel[], record: Column, index: number) => ( isReadOnly - entityFqn={fqn} + entityFqn={tableFqn} entityType={EntityType.TABLE} handleTagSelection={() => Promise.resolve()} hasTagEditAccess={false} @@ -132,7 +231,7 @@ export const ContractSchemaFormTab: React.FC<{ render: (tags: TagLabel[], record: Column, index: number) => ( isReadOnly - entityFqn={fqn} + entityFqn={tableFqn} entityType={EntityType.TABLE} handleTagSelection={() => Promise.resolve()} hasTagEditAccess={false} @@ -146,11 +245,17 @@ export const ContractSchemaFormTab: React.FC<{ { title: t('label.constraint-plural'), dataIndex: 'constraint', + key: 'constraint', + render: renderConstraint, }, ], - [t] + [] ); + useEffect(() => { + fetchTableColumns(); + }, [fetchTableColumns]); + return ( <> @@ -164,7 +269,9 @@ export const ContractSchemaFormTab: React.FC<{
({ getListTestCase: jest.fn().mockResolvedValue({ data: [] }), createTestCase: jest.fn().mockResolvedValue(MOCK_TEST_CASE[0]), getTestCaseByFqn: jest.fn().mockResolvedValue(MOCK_TEST_CASE[0]), + TestCaseType: { + all: 'all', + table: 'table', + column: 'column', + }, })); jest.mock('../../../../rest/ingestionPipelineAPI', () => ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/BundleSuiteForm/BundleSuiteForm.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/BundleSuiteForm/BundleSuiteForm.test.tsx index 2c06238fbb5..38fd3fcb140 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/BundleSuiteForm/BundleSuiteForm.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/BundleSuiteForm/BundleSuiteForm.test.tsx @@ -121,6 +121,11 @@ jest.mock('../../../hooks/useApplicationStore', () => ({ jest.mock('../../../rest/testAPI', () => ({ createTestSuites: jest.fn().mockResolvedValue(mockTestSuite), addTestCaseToLogicalTestSuite: jest.fn().mockResolvedValue({}), + TestCaseType: { + all: 'all', + table: 'table', + column: 'column', + }, })); jest.mock('../../../rest/ingestionPipelineAPI', () => ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.test.tsx index 585a5a43206..98f1c7b664f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.test.tsx @@ -127,6 +127,11 @@ jest.mock('../../../../rest/testAPI', () => ({ updateTestCaseById: jest .fn() .mockImplementation(() => mockUpdateTestCaseById()), + TestCaseType: { + all: 'all', + table: 'table', + column: 'column', + }, })); // Mock TagsContainerV2 to capture props diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/TableProfilerProvider.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/TableProfilerProvider.test.tsx index 9314e8b1d3f..ad63d963621 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/TableProfilerProvider.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/TableProfilerProvider.test.tsx @@ -40,6 +40,11 @@ jest.mock('../../../../rest/testAPI', () => ({ getListTestCaseBySearch: jest .fn() .mockResolvedValue({ data: [], paging: {} }), + TestCaseType: { + all: 'all', + table: 'table', + column: 'column', + }, })); jest.mock('../../../../utils/ToastUtils', () => ({ showErrorToast: jest.fn(), diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/NodeChildren/NodeChildren.component.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/NodeChildren/NodeChildren.component.test.tsx index c3bac5aa879..de93a328780 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/NodeChildren/NodeChildren.component.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/NodeChildren/NodeChildren.component.test.tsx @@ -50,6 +50,11 @@ jest.mock('../../../../context/LineageProvider/LineageProvider', () => ({ jest.mock('../../../../rest/testAPI', () => ({ getTestCaseExecutionSummary: jest.fn(), + TestCaseType: { + all: 'all', + table: 'table', + column: 'column', + }, })); jest.mock('../../../../utils/EntityLink', () => ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.test.tsx index 1b549c737f3..f5a0a60875d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.test.tsx @@ -41,6 +41,11 @@ jest.mock('../../../../rest/tableAPI', () => ({ })); jest.mock('../../../../rest/testAPI', () => ({ getTestCaseExecutionSummary: jest.fn(), + TestCaseType: { + all: 'all', + table: 'table', + column: 'column', + }, })); jest.mock('../SummaryList/SummaryList.component', () => diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/SelectionCardGroup.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/SelectionCardGroup.tsx index f0ead26c9d3..d7f9e319d31 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/SelectionCardGroup.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/SelectionCardGroup.tsx @@ -21,7 +21,7 @@ import { SelectionCardProps, } from './SelectionCardGroup.interface'; -const SelectionCard: FC = ({ +export const SelectionCard: FC = ({ option, isSelected, onClick, diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx index f06291c7789..22063bbfcbb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx @@ -96,6 +96,11 @@ jest.mock('../../../rest/testAPI', () => ({ .fn() .mockImplementation(() => Promise.resolve({ data: mockTestCaseData })), updateTestCaseById: jest.fn(), + TestCaseType: { + all: 'all', + table: 'table', + column: 'column', + }, })); jest.mock('../../../hooks/useCustomLocation/useCustomLocation', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.test.tsx index 747a85b2fc6..e70ed0c921d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteDetailsPage/TestSuiteDetailsPage.test.tsx @@ -112,6 +112,11 @@ jest.mock('../../rest/testAPI', () => { ListTestCaseParamsBySearch: jest .fn() .mockImplementation(() => Promise.resolve({ data: [] })), + TestCaseType: { + all: 'all', + table: 'table', + column: 'column', + }, }; }); jest.mock('../../context/PermissionProvider/PermissionProvider', () => ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteIngestionPage/TestSuiteIngestionPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteIngestionPage/TestSuiteIngestionPage.test.tsx index cda6f580789..6452deeb835 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteIngestionPage/TestSuiteIngestionPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TestSuiteIngestionPage/TestSuiteIngestionPage.test.tsx @@ -51,6 +51,11 @@ jest.mock('../../rest/testAPI', () => { getTestSuiteByName: jest .fn() .mockImplementation(() => Promise.resolve(mockTestSuite)), + TestCaseType: { + all: 'all', + table: 'table', + column: 'column', + }, }; }); jest.mock('../../rest/ingestionPipelineAPI', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/DataQualityUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/DataQualityUtils.tsx index bb6caeebb88..beeb346933f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/DataQualityUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/DataQualityUtils.tsx @@ -10,15 +10,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { t } from 'i18next'; import { isArray, isNil, isUndefined, omit, omitBy } from 'lodash'; import { ReactComponent as AccuracyIcon } from '../../assets/svg/ic-accuracy.svg'; +import { ReactComponent as ColumnIcon } from '../../assets/svg/ic-column.svg'; import { ReactComponent as CompletenessIcon } from '../../assets/svg/ic-completeness.svg'; import { ReactComponent as ConsistencyIcon } from '../../assets/svg/ic-consistency.svg'; import { ReactComponent as IntegrityIcon } from '../../assets/svg/ic-integrity.svg'; import { ReactComponent as SqlIcon } from '../../assets/svg/ic-sql.svg'; +import { ReactComponent as TableIcon } from '../../assets/svg/ic-table-test.svg'; import { ReactComponent as UniquenessIcon } from '../../assets/svg/ic-uniqueness.svg'; import { ReactComponent as ValidityIcon } from '../../assets/svg/ic-validity.svg'; import { ReactComponent as NoDimensionIcon } from '../../assets/svg/no-dimension-icon.svg'; +import { SelectionOption } from '../../components/common/SelectionCardGroup/SelectionCardGroup.interface'; import { TestCaseSearchParams } from '../../components/DataQuality/DataQuality.interface'; import { TEST_CASE_FILTERS } from '../../constants/profiler.constant'; import { Table } from '../../generated/entity/data/table'; @@ -322,3 +326,22 @@ export const convertSearchSourceToTable = ( ...searchSource, columns: searchSource.columns || [], } as Table); + +export const TEST_LEVEL_OPTIONS: SelectionOption[] = [ + { + value: TestCaseType.table, + label: t('label.table-level'), + description: t('label.test-applied-on-entity', { + entity: t('label.table-lowercase'), + }), + icon: , + }, + { + value: TestCaseType.column, + label: t('label.column-level'), + description: t('label.test-applied-on-entity', { + entity: t('label.column-lowercase'), + }), + icon: , + }, +];