mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-05 07:03:07 +00:00
Enhance Data Quality Test Case Form UI
- Improved layout and styling for the Test Case Form, including margin adjustments and label styling. - Added functionality for selecting tables and columns with dynamic fetching and caching. - Implemented test type selection with corresponding parameter forms based on selected definitions. - Updated AsyncSelect component to handle disabled state correctly. - Adjusted SelectionCardGroup styles for better visual consistency.
This commit is contained in:
parent
4cb5987438
commit
2aa5431488
@ -13,6 +13,50 @@
|
|||||||
@import (reference) '../../../../styles/variables.less';
|
@import (reference) '../../../../styles/variables.less';
|
||||||
|
|
||||||
.test-case-form-v1 {
|
.test-case-form-v1 {
|
||||||
|
.ant-form-item {
|
||||||
|
margin-bottom: @size-lg;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-form-item-label > label {
|
||||||
|
color: @grey-800;
|
||||||
|
font-weight: @font-medium;
|
||||||
|
font-size: @font-size-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-table-card,
|
||||||
|
.test-type-card {
|
||||||
|
background-color: @grey-1;
|
||||||
|
border: 1px solid @grey-200;
|
||||||
|
border-radius: @border-radius-sm;
|
||||||
|
margin-bottom: @size-lg;
|
||||||
|
|
||||||
|
.ant-card-body {
|
||||||
|
padding: @size-sm @size-mlg @size-mlg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-form-item {
|
||||||
|
margin-bottom: @size-lg;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-form-item-label > label {
|
||||||
|
color: @grey-800;
|
||||||
|
font-weight: @font-medium;
|
||||||
|
font-size: @font-size-base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-type-card {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.test-level-section {
|
.test-level-section {
|
||||||
margin-bottom: @size-lg;
|
margin-bottom: @size-lg;
|
||||||
|
|
||||||
|
@ -10,64 +10,321 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { Drawer, DrawerProps, Form } from 'antd';
|
import { Card, Drawer, DrawerProps, Form, Select, Typography } from 'antd';
|
||||||
import { useForm } from 'antd/lib/form/Form';
|
import { useForm } from 'antd/lib/form/Form';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FC, useEffect } from 'react';
|
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { ReactComponent as ColumnIcon } from '../../../../assets/svg/ic-column.svg';
|
import { ReactComponent as ColumnIcon } from '../../../../assets/svg/ic-column.svg';
|
||||||
import { ReactComponent as TableIcon } from '../../../../assets/svg/ic-format-table.svg';
|
import { ReactComponent as TableIcon } from '../../../../assets/svg/ic-format-table.svg';
|
||||||
|
import {
|
||||||
|
PAGE_SIZE_LARGE,
|
||||||
|
PAGE_SIZE_MEDIUM,
|
||||||
|
} from '../../../../constants/constants';
|
||||||
|
import { SearchIndex } from '../../../../enums/search.enum';
|
||||||
|
import { Table } from '../../../../generated/entity/data/table';
|
||||||
|
import {
|
||||||
|
EntityType,
|
||||||
|
TestDefinition,
|
||||||
|
TestPlatform,
|
||||||
|
} from '../../../../generated/tests/testDefinition';
|
||||||
|
import { TableSearchSource } from '../../../../interface/search.interface';
|
||||||
|
import { searchQuery } from '../../../../rest/searchAPI';
|
||||||
|
import { getTableDetailsByFQN } from '../../../../rest/tableAPI';
|
||||||
|
import { getListTestDefinitions } from '../../../../rest/testAPI';
|
||||||
|
import { filterSelectOptions } from '../../../../utils/CommonUtils';
|
||||||
|
import { getEntityName } from '../../../../utils/EntityUtils';
|
||||||
|
import { AsyncSelect } from '../../../common/AsyncSelect/AsyncSelect';
|
||||||
import SelectionCardGroup from '../../../common/SelectionCardGroup/SelectionCardGroup';
|
import SelectionCardGroup from '../../../common/SelectionCardGroup/SelectionCardGroup';
|
||||||
import { SelectionOption } from '../../../common/SelectionCardGroup/SelectionCardGroup.interface';
|
import { SelectionOption } from '../../../common/SelectionCardGroup/SelectionCardGroup.interface';
|
||||||
|
import ParameterForm from './ParameterForm';
|
||||||
import './TestCaseFormV1.less';
|
import './TestCaseFormV1.less';
|
||||||
|
|
||||||
export interface TestCaseFormV1Props {
|
export interface TestCaseFormV1Props {
|
||||||
isDrawer?: boolean;
|
isDrawer?: boolean;
|
||||||
drawerProps?: DrawerProps;
|
drawerProps?: DrawerProps;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
table?: Table;
|
||||||
onFormSubmit?: (values: FormValues) => void;
|
onFormSubmit?: (values: FormValues) => void;
|
||||||
initialValues?: Partial<FormValues>;
|
initialValues?: Partial<FormValues>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FormValues {
|
interface FormValues {
|
||||||
testLevel: TestLevel;
|
testLevel: TestLevel;
|
||||||
|
selectedTable?: string;
|
||||||
|
selectedColumn?: string;
|
||||||
|
testTypeId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TablesCache = Map<string, TableSearchSource>;
|
||||||
|
|
||||||
export enum TestLevel {
|
export enum TestLevel {
|
||||||
TABLE = 'table',
|
TABLE = 'table',
|
||||||
COLUMN = 'column',
|
COLUMN = 'column',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Constants
|
||||||
const TEST_LEVEL_OPTIONS: SelectionOption[] = [
|
const TEST_LEVEL_OPTIONS: SelectionOption[] = [
|
||||||
{
|
{
|
||||||
value: 'table',
|
value: TestLevel.TABLE,
|
||||||
label: 'Table Level',
|
label: 'Table Level',
|
||||||
description: 'Test applied on table',
|
description: 'Test applied on table',
|
||||||
icon: <TableIcon />,
|
icon: <TableIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'column',
|
value: TestLevel.COLUMN,
|
||||||
label: 'Column Level',
|
label: 'Column Level',
|
||||||
description: 'Test applied on column',
|
description: 'Test applied on column',
|
||||||
icon: <ColumnIcon />,
|
icon: <ColumnIcon />,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const TABLE_SEARCH_FIELDS: (keyof TableSearchSource)[] = [
|
||||||
|
'name',
|
||||||
|
'fullyQualifiedName',
|
||||||
|
'displayName',
|
||||||
|
'columns',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Helper function to convert TableSearchSource to Table
|
||||||
|
const convertSearchSourceToTable = (searchSource: TableSearchSource): Table =>
|
||||||
|
({
|
||||||
|
...searchSource,
|
||||||
|
columns: searchSource.columns || [],
|
||||||
|
} as Table);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TestCaseFormV1 - An improved form component for creating test cases
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Progressive test level selection (Table/Column)
|
||||||
|
* - Smart table caching with column data
|
||||||
|
* - Dynamic test type filtering based on column data types
|
||||||
|
* - Efficient column selection without additional API calls
|
||||||
|
* - Dynamic parameter form rendering based on selected test definition
|
||||||
|
*/
|
||||||
const TestCaseFormV1: FC<TestCaseFormV1Props> = ({
|
const TestCaseFormV1: FC<TestCaseFormV1Props> = ({
|
||||||
className,
|
className,
|
||||||
drawerProps,
|
drawerProps,
|
||||||
initialValues,
|
initialValues,
|
||||||
isDrawer = false,
|
isDrawer = false,
|
||||||
|
table,
|
||||||
onFormSubmit,
|
onFormSubmit,
|
||||||
}) => {
|
}) => {
|
||||||
const [form] = useForm<FormValues>();
|
const [form] = useForm<FormValues>();
|
||||||
|
const [testDefinitions, setTestDefinitions] = useState<TestDefinition[]>([]);
|
||||||
|
const [selectedTestDefinition, setSelectedTestDefinition] =
|
||||||
|
useState<TestDefinition>();
|
||||||
|
const [currentColumnType, setCurrentColumnType] = useState<string>();
|
||||||
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
|
const [selectedTableData, setSelectedTableData] = useState<Table | undefined>(
|
||||||
|
table
|
||||||
|
);
|
||||||
|
const [tablesCache, setTablesCache] = useState<TablesCache>(new Map());
|
||||||
|
const selectedTestLevel = Form.useWatch('testLevel', form);
|
||||||
|
const selectedTable = Form.useWatch('selectedTable', form);
|
||||||
|
const selectedColumn = Form.useWatch('selectedColumn', form);
|
||||||
|
const selectedTestType = Form.useWatch('testTypeId', form);
|
||||||
|
|
||||||
const handleSubmit = (values: FormValues) => {
|
const handleSubmit = (values: FormValues) => {
|
||||||
onFormSubmit?.(values);
|
onFormSubmit?.(values);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchTables = useCallback(async (searchValue = '', page = 1) => {
|
||||||
|
try {
|
||||||
|
const response = await searchQuery({
|
||||||
|
query: searchValue ? `*${searchValue}*` : '*',
|
||||||
|
pageNumber: page,
|
||||||
|
pageSize: PAGE_SIZE_MEDIUM,
|
||||||
|
searchIndex: SearchIndex.TABLE,
|
||||||
|
includeFields: TABLE_SEARCH_FIELDS,
|
||||||
|
trackTotalHits: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = response.hits.hits.map((hit) => {
|
||||||
|
// Cache the table data for later use
|
||||||
|
setTablesCache((prev) => {
|
||||||
|
const newCache = new Map(prev);
|
||||||
|
newCache.set(
|
||||||
|
hit._source.fullyQualifiedName ?? hit._source.name,
|
||||||
|
hit._source
|
||||||
|
);
|
||||||
|
|
||||||
|
return newCache;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: hit._source.fullyQualifiedName,
|
||||||
|
value: hit._source.fullyQualifiedName,
|
||||||
|
data: hit._source,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return data in the format expected by AsyncSelect for infinite scroll
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
paging: {
|
||||||
|
total: response.hits.total.value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
data: [],
|
||||||
|
paging: { total: 0 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const columnOptions = useMemo(() => {
|
||||||
|
if (!selectedTableData?.columns) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedTableData.columns.map((column) => ({
|
||||||
|
label: column.name,
|
||||||
|
value: column.name,
|
||||||
|
data: column,
|
||||||
|
}));
|
||||||
|
}, [selectedTableData]);
|
||||||
|
|
||||||
|
const fetchTestDefinitions = useCallback(
|
||||||
|
async (columnType?: string) => {
|
||||||
|
try {
|
||||||
|
const { data } = await getListTestDefinitions({
|
||||||
|
limit: PAGE_SIZE_LARGE,
|
||||||
|
entityType:
|
||||||
|
selectedTestLevel === TestLevel.COLUMN
|
||||||
|
? EntityType.Column
|
||||||
|
: EntityType.Table,
|
||||||
|
testPlatform: TestPlatform.OpenMetadata,
|
||||||
|
supportedDataType: columnType,
|
||||||
|
});
|
||||||
|
|
||||||
|
setTestDefinitions(data);
|
||||||
|
} catch (error) {
|
||||||
|
setTestDefinitions([]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[selectedTestLevel]
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchSelectedTableData = useCallback(async (tableFqn: string) => {
|
||||||
|
try {
|
||||||
|
const tableData = await getTableDetailsByFQN(tableFqn, {
|
||||||
|
fields: 'columns',
|
||||||
|
});
|
||||||
|
setSelectedTableData(tableData);
|
||||||
|
} catch (error) {
|
||||||
|
setSelectedTableData(undefined);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Initialize form with default values
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
form.setFieldsValue({ testLevel: TestLevel.TABLE });
|
if (!isInitialized) {
|
||||||
}, [form]);
|
form.setFieldsValue({ testLevel: TestLevel.TABLE });
|
||||||
|
setIsInitialized(true);
|
||||||
|
}
|
||||||
|
}, [form, isInitialized]);
|
||||||
|
|
||||||
|
// Handle test level changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedTestLevel) {
|
||||||
|
// Fetch appropriate test definitions
|
||||||
|
fetchTestDefinitions();
|
||||||
|
|
||||||
|
// Reset dependent fields
|
||||||
|
form.setFieldsValue({
|
||||||
|
testTypeId: undefined,
|
||||||
|
selectedColumn:
|
||||||
|
selectedTestLevel === TestLevel.TABLE
|
||||||
|
? undefined
|
||||||
|
: form.getFieldValue('selectedColumn'),
|
||||||
|
});
|
||||||
|
setSelectedTestDefinition(undefined);
|
||||||
|
}
|
||||||
|
}, [selectedTestLevel, fetchTestDefinitions, form]);
|
||||||
|
|
||||||
|
// Handle table selection: use cached data or fetch if needed
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedTable) {
|
||||||
|
const cachedTableData = tablesCache.get(selectedTable);
|
||||||
|
if (cachedTableData) {
|
||||||
|
setSelectedTableData(convertSearchSourceToTable(cachedTableData));
|
||||||
|
} else {
|
||||||
|
fetchSelectedTableData(selectedTable);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setSelectedTableData(table);
|
||||||
|
}
|
||||||
|
form.setFieldsValue({ selectedColumn: undefined });
|
||||||
|
}, [selectedTable, table, tablesCache, fetchSelectedTableData, form]);
|
||||||
|
|
||||||
|
// Handle column selection and fetch test definitions with column type
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
selectedColumn &&
|
||||||
|
selectedTableData &&
|
||||||
|
selectedTestLevel === TestLevel.COLUMN
|
||||||
|
) {
|
||||||
|
const selectedColumnData = selectedTableData.columns?.find(
|
||||||
|
(column) => column.name === selectedColumn
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selectedColumnData?.dataType !== currentColumnType) {
|
||||||
|
fetchTestDefinitions(selectedColumnData?.dataType);
|
||||||
|
setCurrentColumnType(selectedColumnData?.dataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
selectedColumn,
|
||||||
|
selectedTableData,
|
||||||
|
currentColumnType,
|
||||||
|
selectedTestLevel,
|
||||||
|
fetchTestDefinitions,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Handle test type selection
|
||||||
|
const handleTestDefinitionChange = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
const testDefinition = testDefinitions.find(
|
||||||
|
(definition) => definition.fullyQualifiedName === value
|
||||||
|
);
|
||||||
|
setSelectedTestDefinition(testDefinition);
|
||||||
|
},
|
||||||
|
[testDefinitions]
|
||||||
|
);
|
||||||
|
|
||||||
|
const testTypeOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
testDefinitions.map((testDef) => ({
|
||||||
|
label: (
|
||||||
|
<div data-testid={testDef.fullyQualifiedName}>
|
||||||
|
<Typography.Paragraph className="m-b-0">
|
||||||
|
{getEntityName(testDef)}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
<Typography.Paragraph className="m-b-0 text-grey-muted text-xs">
|
||||||
|
{testDef.description}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
value: testDef.fullyQualifiedName ?? '',
|
||||||
|
labelValue: getEntityName(testDef),
|
||||||
|
})),
|
||||||
|
[testDefinitions]
|
||||||
|
);
|
||||||
|
|
||||||
|
const generateParamsField = useMemo(() => {
|
||||||
|
if (selectedTestDefinition?.parameterDefinition) {
|
||||||
|
return (
|
||||||
|
<ParameterForm
|
||||||
|
definition={selectedTestDefinition}
|
||||||
|
table={selectedTableData}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}, [selectedTestDefinition, selectedTableData]);
|
||||||
|
|
||||||
const formContent = (
|
const formContent = (
|
||||||
<div
|
<div
|
||||||
@ -90,6 +347,63 @@ const TestCaseFormV1: FC<TestCaseFormV1Props> = ({
|
|||||||
rules={[{ required: true, message: 'Please select test level' }]}>
|
rules={[{ required: true, message: 'Please select test level' }]}>
|
||||||
<SelectionCardGroup options={TEST_LEVEL_OPTIONS} />
|
<SelectionCardGroup options={TEST_LEVEL_OPTIONS} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
<Card className="select-table-card">
|
||||||
|
<Form.Item
|
||||||
|
label="Select Table"
|
||||||
|
name="selectedTable"
|
||||||
|
rules={[{ required: true, message: 'Please select a table' }]}>
|
||||||
|
<AsyncSelect
|
||||||
|
allowClear
|
||||||
|
enableInfiniteScroll
|
||||||
|
showSearch
|
||||||
|
api={fetchTables}
|
||||||
|
placeholder="Select one or more table at a time"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{selectedTestLevel === TestLevel.COLUMN && selectedTable && (
|
||||||
|
<Form.Item
|
||||||
|
label="Select Column"
|
||||||
|
name="selectedColumn"
|
||||||
|
rules={[{ required: true, message: 'Please select a column' }]}>
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
showSearch
|
||||||
|
filterOption={(input, option) =>
|
||||||
|
(option?.label ?? '')
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(input.toLowerCase())
|
||||||
|
}
|
||||||
|
loading={!selectedTableData}
|
||||||
|
options={columnOptions}
|
||||||
|
placeholder="Select a column"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="test-type-card">
|
||||||
|
<Form.Item
|
||||||
|
label="Test Type"
|
||||||
|
name="testTypeId"
|
||||||
|
rules={[{ required: true, message: 'Please select a test type' }]}
|
||||||
|
tooltip={selectedTestDefinition?.description}>
|
||||||
|
<Select
|
||||||
|
showSearch
|
||||||
|
filterOption={filterSelectOptions}
|
||||||
|
options={testTypeOptions}
|
||||||
|
placeholder="Select a test type"
|
||||||
|
popupClassName="no-wrap-option"
|
||||||
|
size="large"
|
||||||
|
onChange={handleTestDefinitionChange}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{selectedTestDefinition && generateParamsField}
|
||||||
|
</Card>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -147,8 +147,10 @@ export const AsyncSelect = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchOptions(searchText, 1);
|
if (!restProps.disabled) {
|
||||||
}, [searchText]);
|
fetchOptions(searchText, 1);
|
||||||
|
}
|
||||||
|
}, [searchText, restProps.disabled]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
.selection-card {
|
.selection-card {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border: 1px solid @grey-200;
|
border: 1px solid @grey-200;
|
||||||
border-radius: @border-radius-xs;
|
border-radius: @border-radius-sm;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
background: @grey-50;
|
background: @grey-50;
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
import { Button, Card, Col, Row, Tabs } from 'antd';
|
import { Button, Card, Col, Row, Tabs } from 'antd';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { useMemo } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import ManageButton from '../../components/common/EntityPageInfos/ManageButton/ManageButton';
|
import ManageButton from '../../components/common/EntityPageInfos/ManageButton/ManageButton';
|
||||||
@ -38,6 +38,19 @@ const DataQualityPage = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { permissions } = usePermissionProvider();
|
const { permissions } = usePermissionProvider();
|
||||||
const { testSuite: testSuitePermission } = permissions;
|
const { testSuite: testSuitePermission } = permissions;
|
||||||
|
|
||||||
|
// Add state for modal open/close
|
||||||
|
const [isTestCaseModalOpen, setIsTestCaseModalOpen] = useState(false);
|
||||||
|
|
||||||
|
// Add handlers for modal
|
||||||
|
const handleOpenTestCaseModal = () => {
|
||||||
|
setIsTestCaseModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseTestCaseModal = () => {
|
||||||
|
setIsTestCaseModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
const menuItems = useMemo(() => {
|
const menuItems = useMemo(() => {
|
||||||
const data = DataQualityClassBase.getDataQualityTab();
|
const data = DataQualityClassBase.getDataQualityTab();
|
||||||
|
|
||||||
@ -93,7 +106,10 @@ const DataQualityPage = () => {
|
|||||||
)}
|
)}
|
||||||
{activeTab === DataQualityPageTabs.TEST_CASES &&
|
{activeTab === DataQualityPageTabs.TEST_CASES &&
|
||||||
testSuitePermission?.Create && (
|
testSuitePermission?.Create && (
|
||||||
<Button data-testid="add-test-case-btn" type="primary">
|
<Button
|
||||||
|
data-testid="add-test-case-btn"
|
||||||
|
type="primary"
|
||||||
|
onClick={handleOpenTestCaseModal}>
|
||||||
{t('label.add-entity', {
|
{t('label.add-entity', {
|
||||||
entity: t('label.test-case'),
|
entity: t('label.test-case'),
|
||||||
})}
|
})}
|
||||||
@ -126,7 +142,8 @@ const DataQualityPage = () => {
|
|||||||
title: t('label.add-entity', {
|
title: t('label.add-entity', {
|
||||||
entity: t('label.test-case'),
|
entity: t('label.test-case'),
|
||||||
}),
|
}),
|
||||||
open: false,
|
open: isTestCaseModalOpen,
|
||||||
|
onClose: handleCloseTestCaseModal,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DataQualityProvider>
|
</DataQualityProvider>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user