mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-28 01:04:46 +00:00
fix(ui): tab label with persona assigned (#22927)
* fix(ui): tab label with persona assigned * fix tests * fix tests * add playwright to validate the same * remove dndProvider from glossary term table * fix unit tests * fix tests * fix data contract failure (cherry picked from commit 0081ece0ac8344edb699e7a3541fd086dde591a3)
This commit is contained in:
parent
3512549097
commit
eb57f6d5cd
@ -32,138 +32,188 @@ export enum ECustomizedGovernance {
|
||||
GLOSSARY_TERM = 'Glossary Term',
|
||||
}
|
||||
|
||||
export enum EntityTabs {
|
||||
SCHEMA = 'schema',
|
||||
SCHEMAS = 'schemas',
|
||||
ACTIVITY_FEED = 'activity_feed',
|
||||
SAMPLE_DATA = 'sample_data',
|
||||
TABLE_QUERIES = 'table_queries',
|
||||
PROFILER = 'profiler',
|
||||
LINEAGE = 'lineage',
|
||||
KNOWLEDGE_GRAPH = 'knowledge_graph',
|
||||
DBT = 'dbt',
|
||||
VIEW_DEFINITION = 'view_definition',
|
||||
SCHEMA_DEFINITION = 'schema_definition',
|
||||
CUSTOM_PROPERTIES = 'custom_properties',
|
||||
MODEL = 'model',
|
||||
FEATURES = 'features',
|
||||
TASKS = 'tasks',
|
||||
CONFIG = 'config',
|
||||
DETAILS = 'details',
|
||||
CHILDREN = 'children',
|
||||
EXECUTIONS = 'executions',
|
||||
TABLE = 'table',
|
||||
TEST_CASES = 'test-cases',
|
||||
PIPELINE = 'pipeline',
|
||||
DATA_Model = 'data-model',
|
||||
AGENTS = 'agents',
|
||||
CONNECTION = 'connection',
|
||||
SQL = 'sql',
|
||||
FIELDS = 'fields',
|
||||
SEARCH_INDEX_SETTINGS = 'search-index-settings',
|
||||
STORED_PROCEDURE = 'stored_procedure',
|
||||
CODE = 'code',
|
||||
API_COLLECTION = 'apiCollection',
|
||||
API_ENDPOINT = 'apiEndpoint',
|
||||
OVERVIEW = 'overview',
|
||||
INCIDENTS = 'incidents',
|
||||
TERMS = 'terms',
|
||||
GLOSSARY_TERMS = 'glossary_terms',
|
||||
ASSETS = 'assets',
|
||||
EXPRESSION = 'expression',
|
||||
INSIGHTS = 'insights',
|
||||
DASHBOARD = 'dashboard',
|
||||
DOCUMENTATION = 'documentation',
|
||||
DATA_PRODUCTS = 'data_products',
|
||||
SUBDOMAINS = 'subdomains',
|
||||
CONTRACT = 'contract',
|
||||
}
|
||||
|
||||
export const TABLE_DEFAULT_TABS = [
|
||||
'Activity Feeds & Tasks',
|
||||
'Contract',
|
||||
'Custom Properties',
|
||||
'Data Observability',
|
||||
'Lineage',
|
||||
'Queries',
|
||||
'Sample Data',
|
||||
'Schema',
|
||||
'View Definition',
|
||||
'dbt',
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.CONTRACT,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
EntityTabs.PROFILER,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.TABLE_QUERIES,
|
||||
EntityTabs.SAMPLE_DATA,
|
||||
EntityTabs.SCHEMA,
|
||||
EntityTabs.VIEW_DEFINITION,
|
||||
EntityTabs.DBT,
|
||||
];
|
||||
|
||||
export const TOPIC_DEFAULT_TABS = [
|
||||
'Schema',
|
||||
'Activity Feeds & Tasks',
|
||||
'Sample Data',
|
||||
'Config',
|
||||
'Lineage',
|
||||
'Custom Properties',
|
||||
EntityTabs.SCHEMA,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.SAMPLE_DATA,
|
||||
EntityTabs.CONFIG,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const DASHBOARD_DEFAULT_TABS = [
|
||||
'Details',
|
||||
'Activity Feeds & Tasks',
|
||||
'Lineage',
|
||||
'Custom Properties',
|
||||
EntityTabs.DETAILS,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const MLMODEL_DEFAULT_TABS = [
|
||||
'Features',
|
||||
'Activity Feeds & Tasks',
|
||||
'Details',
|
||||
'Lineage',
|
||||
'Custom Properties',
|
||||
EntityTabs.FEATURES,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.DETAILS,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const PIPELINE_DEFAULT_TABS = [
|
||||
'Tasks',
|
||||
'Activity Feeds & Tasks',
|
||||
'Executions',
|
||||
'Lineage',
|
||||
'Custom Properties',
|
||||
EntityTabs.TASKS,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.EXECUTIONS,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const DASHBOARD_DATAMODEL_DEFAULT_TABS = [
|
||||
'Model',
|
||||
'Activity Feeds & Tasks',
|
||||
'SQL',
|
||||
'Lineage',
|
||||
'Custom Properties',
|
||||
EntityTabs.MODEL,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.SQL,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const API_COLLECTION_DEFAULT_TABS = [
|
||||
'Endpoints',
|
||||
'Activity Feeds & Tasks',
|
||||
'Custom Properties',
|
||||
EntityTabs.API_ENDPOINT,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const SEARCH_INDEX_DEFAULT_TABS = [
|
||||
'Fields',
|
||||
'Activity Feeds & Tasks',
|
||||
'Sample Data',
|
||||
'Lineage',
|
||||
'Search Index Settings',
|
||||
'Custom Properties',
|
||||
EntityTabs.FIELDS,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.SAMPLE_DATA,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.SEARCH_INDEX_SETTINGS,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const CONTAINER_DEFAULT_TABS = [
|
||||
'Schema',
|
||||
'Children',
|
||||
'Activity Feeds & Tasks',
|
||||
'Lineage',
|
||||
'Custom Properties',
|
||||
EntityTabs.SCHEMA,
|
||||
EntityTabs.CHILDREN,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const DATABASE_DEFAULT_TABS = [
|
||||
'Schemas',
|
||||
'Activity Feeds & Tasks',
|
||||
'Custom Properties',
|
||||
EntityTabs.SCHEMAS,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const DATABASE_SCHEMA_DEFAULT_TABS = [
|
||||
'Tables',
|
||||
'Stored Procedures',
|
||||
'Activity Feeds & Tasks',
|
||||
'Custom Properties',
|
||||
EntityTabs.TABLE,
|
||||
EntityTabs.STORED_PROCEDURE,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const STORED_PROCEDURE_DEFAULT_TABS = [
|
||||
'Code',
|
||||
'Activity Feeds & Tasks',
|
||||
'Lineage',
|
||||
'Custom Properties',
|
||||
EntityTabs.CODE,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const API_ENDPOINT_DEFAULT_TABS = [
|
||||
'Schema',
|
||||
'Activity Feeds & Tasks',
|
||||
'Lineage',
|
||||
'Custom Properties',
|
||||
EntityTabs.SCHEMA,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const DASHBOARD_DATA_MODEL_DEFAULT_TABS = [
|
||||
'Model',
|
||||
'Activity Feeds & Tasks',
|
||||
'Lineage',
|
||||
'Custom Properties',
|
||||
EntityTabs.MODEL,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const ML_MODEL_DEFAULT_TABS = [
|
||||
'Features',
|
||||
'Activity Feeds & Tasks',
|
||||
'Details',
|
||||
'Lineage',
|
||||
'Custom Properties',
|
||||
EntityTabs.FEATURES,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.DETAILS,
|
||||
EntityTabs.LINEAGE,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const DOMAIN_DEFAULT_TABS = [
|
||||
'Documentation',
|
||||
'Sub Domains',
|
||||
'Data Products',
|
||||
'Assets',
|
||||
'Custom Properties',
|
||||
EntityTabs.DOCUMENTATION,
|
||||
EntityTabs.SUBDOMAINS,
|
||||
EntityTabs.DATA_PRODUCTS,
|
||||
EntityTabs.ASSETS,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
export const GLOSSARY_DEFAULT_TABS = ['Terms', 'Activity Feeds & Tasks'];
|
||||
export const GLOSSARY_DEFAULT_TABS = [
|
||||
EntityTabs.TERMS,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
];
|
||||
|
||||
export const GLOSSARY_TERM_DEFAULT_TABS = [
|
||||
'Overview',
|
||||
'Glossary Terms',
|
||||
'Assets',
|
||||
'Activity Feeds & Tasks',
|
||||
'Custom Properties',
|
||||
EntityTabs.OVERVIEW,
|
||||
EntityTabs.GLOSSARY_TERMS,
|
||||
EntityTabs.ASSETS,
|
||||
EntityTabs.ACTIVITY_FEED,
|
||||
EntityTabs.CUSTOM_PROPERTIES,
|
||||
];
|
||||
|
||||
@ -388,7 +388,7 @@ test.describe('Persona customization', () => {
|
||||
.getByTestId('remove-widget-button')
|
||||
.click();
|
||||
|
||||
await adminPage.getByTestId('tab-Custom Properties').click();
|
||||
await adminPage.getByTestId('tab-custom_properties').click();
|
||||
await adminPage.getByText('Hide', { exact: true }).click();
|
||||
|
||||
await adminPage.getByRole('button', { name: 'Add tab' }).click();
|
||||
@ -470,9 +470,9 @@ test.describe('Persona customization', () => {
|
||||
|
||||
for (const tabName of expectedTabs) {
|
||||
await expect(
|
||||
adminPage.getByTestId('customize-tab-card').getByRole('button', {
|
||||
name: tabName,
|
||||
})
|
||||
adminPage
|
||||
.getByTestId('customize-tab-card')
|
||||
.getByTestId(`tab-${tabName}`)
|
||||
).toBeVisible();
|
||||
}
|
||||
}
|
||||
@ -489,6 +489,9 @@ test.describe('Persona customization', () => {
|
||||
.click();
|
||||
|
||||
await adminPage.getByRole('button', { name: 'Add tab' }).click();
|
||||
|
||||
await expect(adminPage.getByRole('dialog')).toBeVisible();
|
||||
|
||||
await adminPage
|
||||
.getByRole('dialog')
|
||||
.getByRole('button', { name: 'Add' })
|
||||
@ -551,8 +554,8 @@ test.describe('Persona customization', () => {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
const dragElement = adminPage.getByTestId('tab-Overview');
|
||||
const dropTarget = adminPage.getByTestId('tab-Custom Properties');
|
||||
const dragElement = adminPage.getByTestId('tab-overview');
|
||||
const dropTarget = adminPage.getByTestId('tab-custom_properties');
|
||||
|
||||
await dragElement.dragTo(dropTarget);
|
||||
|
||||
@ -597,4 +600,98 @@ test.describe('Persona customization', () => {
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test("customize tab label should only render if it's customize by user", async ({
|
||||
adminPage,
|
||||
userPage,
|
||||
}) => {
|
||||
await test.step('apply tab label customization for Table', async () => {
|
||||
await settingClick(adminPage, GlobalSettingOptions.PERSONA);
|
||||
await adminPage.waitForLoadState('networkidle');
|
||||
await adminPage
|
||||
.getByTestId(`persona-details-card-${persona.data.name}`)
|
||||
.click();
|
||||
await adminPage.getByRole('tab', { name: 'Customize UI' }).click();
|
||||
await adminPage.waitForLoadState('networkidle');
|
||||
await adminPage.getByText('Data Assets').click();
|
||||
await adminPage.getByText('Table', { exact: true }).click();
|
||||
|
||||
await adminPage.waitForSelector('[data-testid="loader"]', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
await expect(
|
||||
adminPage
|
||||
.getByTestId('customize-tab-card')
|
||||
.getByTestId(`tab-sample_data`)
|
||||
).toBeVisible();
|
||||
|
||||
await adminPage
|
||||
.getByTestId('customize-tab-card')
|
||||
.getByTestId(`tab-sample_data`)
|
||||
.click();
|
||||
|
||||
await adminPage.getByRole('menuitem', { name: 'Rename' }).click();
|
||||
|
||||
await expect(adminPage.getByRole('dialog')).toBeVisible();
|
||||
|
||||
await adminPage.getByRole('dialog').getByRole('textbox').clear();
|
||||
await adminPage
|
||||
.getByRole('dialog')
|
||||
.getByRole('textbox')
|
||||
.fill('Sample Data Updated');
|
||||
|
||||
await adminPage
|
||||
.getByRole('dialog')
|
||||
.getByRole('button', { name: 'Ok' })
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
adminPage
|
||||
.getByTestId('customize-tab-card')
|
||||
.getByTestId(`tab-sample_data`)
|
||||
).toHaveText('Sample Data Updated');
|
||||
|
||||
await adminPage.getByTestId('save-button').click();
|
||||
|
||||
await toastNotification(
|
||||
adminPage,
|
||||
/^Page layout (created|updated) successfully\.$/
|
||||
);
|
||||
});
|
||||
|
||||
await test.step(
|
||||
'validate applied label change and language support for page',
|
||||
async () => {
|
||||
await redirectToHomePage(userPage);
|
||||
|
||||
const entity = getCustomizeDetailsEntity(ECustomizedDataAssets.TABLE);
|
||||
await entity.visitEntityPage(userPage);
|
||||
await userPage.waitForLoadState('networkidle');
|
||||
await userPage.waitForSelector('[data-testid="loader"]', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
// Change language to French
|
||||
await userPage.getByRole('button', { name: 'EN' }).click();
|
||||
await userPage.getByRole('menuitem', { name: 'Français - FR' }).click();
|
||||
await userPage.waitForLoadState('networkidle');
|
||||
await userPage.waitForSelector('[data-testid="loader"]', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
await expect(
|
||||
userPage.getByRole('tab', { name: 'Sample Data Updated' })
|
||||
).toBeVisible();
|
||||
// Overview tab in French, only customized tab should be non-localized rest should be localized
|
||||
await expect(
|
||||
userPage.getByRole('tab', { name: 'Colonnes' })
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
userPage.getByRole('tab', { name: "Flux d'Activité & Tâches" })
|
||||
).toBeVisible();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -696,7 +696,7 @@ test.describe('Data Contracts', () => {
|
||||
});
|
||||
|
||||
// Hide the Contract tab
|
||||
await page.getByTestId('tab-Contract').click();
|
||||
await page.getByTestId('tab-contract').click();
|
||||
await page.getByText('Hide', { exact: true }).click();
|
||||
|
||||
// Save the customization
|
||||
|
||||
@ -32,6 +32,8 @@ import { useApplicationStore } from './hooks/useApplicationStore';
|
||||
import { getCustomUiThemePreference } from './rest/settingConfigAPI';
|
||||
import { getBasePath } from './utils/HistoryUtils';
|
||||
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import i18n from './utils/i18next/LocalUtil';
|
||||
import { getThemeConfig } from './utils/ThemeUtils';
|
||||
|
||||
@ -88,7 +90,9 @@ const App: FC = () => {
|
||||
<AsyncDeleteProvider>
|
||||
<EntityExportModalProvider>
|
||||
<AirflowStatusProvider>
|
||||
<AppRouter />
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<AppRouter />
|
||||
</DndProvider>
|
||||
</AirflowStatusProvider>
|
||||
</EntityExportModalProvider>
|
||||
</AsyncDeleteProvider>
|
||||
|
||||
@ -13,17 +13,8 @@
|
||||
|
||||
import { EyeFilled, MoreOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Col, Dropdown, Input, Modal, Space } from 'antd';
|
||||
import {
|
||||
cloneDeep,
|
||||
isEmpty,
|
||||
isNil,
|
||||
isUndefined,
|
||||
toString,
|
||||
uniqueId,
|
||||
} from 'lodash';
|
||||
import { cloneDeep, isEmpty, isNil, isUndefined, uniqueId } from 'lodash';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import RGL, { Layout, WidthProvider } from 'react-grid-layout';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@ -50,6 +41,7 @@ import {
|
||||
getCustomizableWidgetByPage,
|
||||
getDefaultTabs,
|
||||
getDefaultWidgetForTab,
|
||||
getTabDisplayName,
|
||||
} from '../../../utils/CustomizePage/CustomizePageUtils';
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import { TabItem } from '../../common/DraggableTabs/DraggableTabs';
|
||||
@ -388,46 +380,44 @@ export const CustomizeTabWidget = () => {
|
||||
</Button>
|
||||
}
|
||||
title={t('label.customize-tab-plural')}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<div className="d-flex flex-wrap gap-4">
|
||||
{items.map((item, index) => (
|
||||
<TabItem
|
||||
index={index}
|
||||
item={item}
|
||||
key={item.id}
|
||||
moveTab={moveTab}
|
||||
shouldHide={systemTabIds.includes(item.id)}
|
||||
onEdit={onChange}
|
||||
onRemove={remove}
|
||||
onRename={handleTabEditClick}
|
||||
/>
|
||||
))}
|
||||
{hiddenTabs.map((item) => (
|
||||
<Dropdown
|
||||
key={item.id}
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
label: t('label.show'),
|
||||
key: 'show',
|
||||
icon: <EyeFilled />,
|
||||
},
|
||||
],
|
||||
onClick: () => add(item),
|
||||
}}
|
||||
trigger={['click']}>
|
||||
<Button
|
||||
className="draggable-hidden-tab-item bg-grey"
|
||||
data-testid={`tab-${item.displayName}`}>
|
||||
<Space>
|
||||
{getEntityName(item)}
|
||||
<MoreOutlined />
|
||||
</Space>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
))}
|
||||
</div>
|
||||
</DndProvider>
|
||||
<div className="d-flex flex-wrap gap-4">
|
||||
{items.map((item, index) => (
|
||||
<TabItem
|
||||
index={index}
|
||||
item={item}
|
||||
key={item.id}
|
||||
moveTab={moveTab}
|
||||
shouldHide={systemTabIds.includes(item.id)}
|
||||
onEdit={onChange}
|
||||
onRemove={remove}
|
||||
onRename={handleTabEditClick}
|
||||
/>
|
||||
))}
|
||||
{hiddenTabs.map((item) => (
|
||||
<Dropdown
|
||||
key={item.id}
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
label: t('label.show'),
|
||||
key: 'show',
|
||||
icon: <EyeFilled />,
|
||||
},
|
||||
],
|
||||
onClick: () => add(item),
|
||||
}}
|
||||
trigger={['click']}>
|
||||
<Button
|
||||
className="draggable-hidden-tab-item bg-grey"
|
||||
data-testid={`tab-${item.name}`}>
|
||||
<Space>
|
||||
{getTabDisplayName(item)}
|
||||
<MoreOutlined />
|
||||
</Space>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
@ -508,7 +498,7 @@ export const CustomizeTabWidget = () => {
|
||||
onOk={handleRenameSave}>
|
||||
<Input
|
||||
autoFocus
|
||||
value={toString(getEntityName(editableItem))}
|
||||
value={getTabDisplayName(editableItem)}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
@ -32,8 +32,6 @@ import classNames from 'classnames';
|
||||
import { compare } from 'fast-json-patch';
|
||||
import { cloneDeep, isEmpty, isUndefined } from 'lodash';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { ReactComponent as IconDrag } from '../../../assets/svg/drag.svg';
|
||||
@ -882,28 +880,26 @@ const GlossaryTermTab = ({ isGlossary, className }: GlossaryTermTabProps) => {
|
||||
{/* Have use the col to set the width of the table, to only use the viewport width for the table columns */}
|
||||
<Col className="w-full" ref={tableContainerRef} span={24}>
|
||||
{glossaryTerms.length > 0 ? (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Table
|
||||
resizableColumns
|
||||
className={classNames('drop-over-background', {
|
||||
'drop-over-table': isTableHovered,
|
||||
})}
|
||||
columns={columns}
|
||||
components={TABLE_CONSTANTS}
|
||||
data-testid="glossary-terms-table"
|
||||
dataSource={filteredGlossaryTerms}
|
||||
defaultVisibleColumns={DEFAULT_VISIBLE_COLUMNS}
|
||||
expandable={expandableConfig}
|
||||
extraTableFilters={extraTableFilters}
|
||||
loading={isTableLoading || termsLoading}
|
||||
pagination={false}
|
||||
rowKey="fullyQualifiedName"
|
||||
size="small"
|
||||
staticVisibleColumns={STATIC_VISIBLE_COLUMNS}
|
||||
onHeaderRow={onTableHeader}
|
||||
onRow={onTableRow}
|
||||
/>
|
||||
</DndProvider>
|
||||
<Table
|
||||
resizableColumns
|
||||
className={classNames('drop-over-background', {
|
||||
'drop-over-table': isTableHovered,
|
||||
})}
|
||||
columns={columns}
|
||||
components={TABLE_CONSTANTS}
|
||||
data-testid="glossary-terms-table"
|
||||
dataSource={filteredGlossaryTerms}
|
||||
defaultVisibleColumns={DEFAULT_VISIBLE_COLUMNS}
|
||||
expandable={expandableConfig}
|
||||
extraTableFilters={extraTableFilters}
|
||||
loading={isTableLoading || termsLoading}
|
||||
pagination={false}
|
||||
rowKey="fullyQualifiedName"
|
||||
size="small"
|
||||
staticVisibleColumns={STATIC_VISIBLE_COLUMNS}
|
||||
onHeaderRow={onTableHeader}
|
||||
onRow={onTableRow}
|
||||
/>
|
||||
) : (
|
||||
<ErrorPlaceHolder />
|
||||
)}
|
||||
|
||||
@ -19,6 +19,8 @@ import {
|
||||
getByText,
|
||||
render,
|
||||
} from '@testing-library/react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import {
|
||||
mockedGlossaryTerms,
|
||||
@ -104,6 +106,15 @@ jest.mock('../../Customization/GenericProvider/GenericProvider', () => ({
|
||||
})),
|
||||
}));
|
||||
|
||||
const renderWithDndProvider = (props = {}) => {
|
||||
return render(
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<GlossaryTermTab {...mockProps} {...props} />
|
||||
</DndProvider>,
|
||||
{ wrapper: MemoryRouter }
|
||||
);
|
||||
};
|
||||
|
||||
describe('Test GlossaryTermTab component', () => {
|
||||
it('should show the ErrorPlaceHolder component, if no glossary is present', () => {
|
||||
const { container } = render(<GlossaryTermTab {...mockProps} />, {
|
||||
@ -132,9 +143,7 @@ describe('Test GlossaryTermTab component', () => {
|
||||
glossaryChildTerms: mockedGlossaryTerms,
|
||||
updateActiveGlossary: jest.fn(),
|
||||
}));
|
||||
const { container } = render(<GlossaryTermTab {...mockProps} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
const { container } = renderWithDndProvider();
|
||||
|
||||
expect(getByTestId(container, 'Clothing')).toBeInTheDocument();
|
||||
expect(
|
||||
|
||||
@ -11,7 +11,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { act, fireEvent, render, screen } from '@testing-library/react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import {
|
||||
MOCK_CURRENT_TEAM,
|
||||
@ -80,13 +82,18 @@ jest.mock('../../../common/SearchBarComponent/SearchBar.component', () =>
|
||||
jest.fn().mockImplementation(() => <div>SearchBar</div>)
|
||||
);
|
||||
|
||||
const renderComponent = (props = {}) => {
|
||||
return render(
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<TeamHierarchy {...teamHierarchyPropsData} {...props} />
|
||||
</DndProvider>,
|
||||
{ wrapper: MemoryRouter }
|
||||
);
|
||||
};
|
||||
|
||||
describe('Team Hierarchy page', () => {
|
||||
it('Initially, Table should load', async () => {
|
||||
await act(async () => {
|
||||
render(<TeamHierarchy {...teamHierarchyPropsData} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
});
|
||||
renderComponent();
|
||||
|
||||
const table = await screen.findByTestId('team-hierarchy-table');
|
||||
|
||||
@ -94,11 +101,7 @@ describe('Team Hierarchy page', () => {
|
||||
});
|
||||
|
||||
it('Should render all table columns', async () => {
|
||||
await act(async () => {
|
||||
render(<TeamHierarchy {...teamHierarchyPropsData} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
});
|
||||
renderComponent();
|
||||
|
||||
const table = await screen.findByTestId('team-hierarchy-table');
|
||||
const teamsColumn = await screen.findByText('label.team-plural');
|
||||
@ -121,11 +124,7 @@ describe('Team Hierarchy page', () => {
|
||||
});
|
||||
|
||||
it('Should render child row in table', async () => {
|
||||
await act(async () => {
|
||||
render(<TeamHierarchy {...teamHierarchyPropsData} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
});
|
||||
renderComponent();
|
||||
|
||||
const table = await screen.findByTestId('team-hierarchy-table');
|
||||
|
||||
|
||||
@ -19,8 +19,6 @@ import classNames from 'classnames';
|
||||
import { compare } from 'fast-json-patch';
|
||||
import { isEmpty, isUndefined } from 'lodash';
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
@ -271,51 +269,49 @@ const TeamHierarchy: FC<TeamHierarchyProps> = ({
|
||||
|
||||
return (
|
||||
<div className="team-list-container">
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Table
|
||||
className={classNames('teams-list-table drop-over-background', {
|
||||
'drop-over-table': isTableHovered,
|
||||
})}
|
||||
columns={columns}
|
||||
components={TABLE_CONSTANTS}
|
||||
data-testid="team-hierarchy-table"
|
||||
dataSource={data}
|
||||
expandable={expandableConfig}
|
||||
extraTableFilters={
|
||||
<Space align="center">
|
||||
<span>
|
||||
<Switch
|
||||
checked={showDeletedTeam}
|
||||
data-testid="show-deleted"
|
||||
onClick={onShowDeletedTeamChange}
|
||||
/>
|
||||
<Typography.Text className="m-l-xs">
|
||||
{t('label.deleted')}
|
||||
</Typography.Text>
|
||||
</span>
|
||||
<Table
|
||||
className={classNames('teams-list-table drop-over-background', {
|
||||
'drop-over-table': isTableHovered,
|
||||
})}
|
||||
columns={columns}
|
||||
components={TABLE_CONSTANTS}
|
||||
data-testid="team-hierarchy-table"
|
||||
dataSource={data}
|
||||
expandable={expandableConfig}
|
||||
extraTableFilters={
|
||||
<Space align="center">
|
||||
<span>
|
||||
<Switch
|
||||
checked={showDeletedTeam}
|
||||
data-testid="show-deleted"
|
||||
onClick={onShowDeletedTeamChange}
|
||||
/>
|
||||
<Typography.Text className="m-l-xs">
|
||||
{t('label.deleted')}
|
||||
</Typography.Text>
|
||||
</span>
|
||||
|
||||
{createTeamPermission && !isTeamDeleted && (
|
||||
<Button
|
||||
data-testid="add-team"
|
||||
type="primary"
|
||||
onClick={handleAddTeamButtonClick}>
|
||||
{t('label.add-entity', { entity: t('label.team') })}
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
loading={isTableLoading}
|
||||
locale={{
|
||||
emptyText: <FilterTablePlaceHolder />,
|
||||
}}
|
||||
pagination={false}
|
||||
rowKey="name"
|
||||
searchProps={searchProps}
|
||||
size="small"
|
||||
onHeaderRow={onTableHeader}
|
||||
onRow={onTableRow}
|
||||
/>
|
||||
</DndProvider>
|
||||
{createTeamPermission && !isTeamDeleted && (
|
||||
<Button
|
||||
data-testid="add-team"
|
||||
type="primary"
|
||||
onClick={handleAddTeamButtonClick}>
|
||||
{t('label.add-entity', { entity: t('label.team') })}
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
loading={isTableLoading}
|
||||
locale={{
|
||||
emptyText: <FilterTablePlaceHolder />,
|
||||
}}
|
||||
pagination={false}
|
||||
rowKey="name"
|
||||
searchProps={searchProps}
|
||||
size="small"
|
||||
onHeaderRow={onTableHeader}
|
||||
onRow={onTableRow}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
centered
|
||||
|
||||
@ -15,9 +15,8 @@ import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { TabItem } from './DraggableTabs';
|
||||
|
||||
// Mock the getEntityName utility function
|
||||
jest.mock('../../../utils/EntityUtils', () => ({
|
||||
getEntityName: jest.fn().mockReturnValue('Test Tab'),
|
||||
jest.mock('../../../utils/CustomizePage/CustomizePageUtils', () => ({
|
||||
getTabDisplayName: jest.fn().mockReturnValue('Test Tab'),
|
||||
}));
|
||||
|
||||
describe('TabItem', () => {
|
||||
@ -53,12 +52,12 @@ describe('TabItem', () => {
|
||||
it('renders tab item with correct name', () => {
|
||||
renderComponent();
|
||||
|
||||
expect(screen.getByText('Test Tab')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('tab-Test Tab')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onItemClick when tab is clicked', () => {
|
||||
renderComponent();
|
||||
fireEvent.click(screen.getByText('Test Tab'));
|
||||
fireEvent.click(screen.getByTestId('tab-Test Tab'));
|
||||
|
||||
expect(defaultProps.onItemClick).toHaveBeenCalledWith(mockTab.id);
|
||||
});
|
||||
|
||||
@ -22,7 +22,7 @@ import React from 'react';
|
||||
import { useDrag, useDrop } from 'react-dnd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Tab } from '../../../generated/system/ui/tab';
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import { getTabDisplayName } from '../../../utils/CustomizePage/CustomizePageUtils';
|
||||
import './draggable-tabs.less';
|
||||
|
||||
type TargetKey = React.MouseEvent | React.KeyboardEvent | string;
|
||||
@ -118,10 +118,10 @@ export const TabItem = ({
|
||||
trigger={['click']}>
|
||||
<Button
|
||||
className="draggable-tab-item"
|
||||
data-testid={`tab-${item.displayName}`}
|
||||
data-testid={`tab-${item.name}`}
|
||||
onClick={() => onItemClick?.(item.id)}>
|
||||
<Space>
|
||||
{getEntityName(item)}
|
||||
{getTabDisplayName(item)}
|
||||
<MoreOutlined />
|
||||
</Space>
|
||||
</Button>
|
||||
|
||||
@ -33,8 +33,6 @@ import {
|
||||
} from 'react';
|
||||
import { useAntdColumnResize } from 'react-antd-column-resize';
|
||||
import { Column } from 'react-antd-column-resize/dist/useAntdColumnResize/types';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as ColumnIcon } from '../../../assets/svg/ic-column.svg';
|
||||
import { useCurrentUserPreferences } from '../../../hooks/currentUserStore/useCurrentUserStore';
|
||||
@ -291,24 +289,22 @@ const Table = <T extends Record<string, unknown>>(
|
||||
span={searchProps ? 12 : 24}>
|
||||
{rest.extraTableFilters}
|
||||
{isCustomizeColumnEnable && (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Dropdown
|
||||
className="custom-column-dropdown-menu text-primary"
|
||||
menu={menu}
|
||||
open={isDropdownVisible}
|
||||
placement="bottomRight"
|
||||
trigger={['click']}
|
||||
onOpenChange={setIsDropdownVisible}>
|
||||
<Button
|
||||
className="remove-button-background-hover"
|
||||
data-testid="column-dropdown"
|
||||
icon={<Icon component={ColumnIcon} />}
|
||||
size="small"
|
||||
type="text">
|
||||
{t('label.column-plural')}
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</DndProvider>
|
||||
<Dropdown
|
||||
className="custom-column-dropdown-menu text-primary"
|
||||
menu={menu}
|
||||
open={isDropdownVisible}
|
||||
placement="bottomRight"
|
||||
trigger={['click']}
|
||||
onOpenChange={setIsDropdownVisible}>
|
||||
<Button
|
||||
className="remove-button-background-hover"
|
||||
data-testid="column-dropdown"
|
||||
icon={<Icon component={ColumnIcon} />}
|
||||
size="small"
|
||||
type="text">
|
||||
{t('label.column-plural')}
|
||||
</Button>
|
||||
</Dropdown>
|
||||
)}
|
||||
</Col>
|
||||
)}
|
||||
|
||||
@ -17,6 +17,7 @@ import { WidgetConfig } from '../../pages/CustomizablePage/CustomizablePage.inte
|
||||
import {
|
||||
checkIfExpandViewSupported,
|
||||
getDefaultTabs,
|
||||
getTabDisplayName,
|
||||
getTabLabelFromId,
|
||||
getTabLabelMapFromTabs,
|
||||
sortTabs,
|
||||
@ -24,6 +25,43 @@ import {
|
||||
} from './CustomizePageUtils';
|
||||
|
||||
describe('CustomizePageUtils', () => {
|
||||
describe('getTabDisplayName', () => {
|
||||
it('should return displayName if present', () => {
|
||||
const tab: Tab = {
|
||||
id: EntityTabs.OVERVIEW,
|
||||
name: EntityTabs.OVERVIEW,
|
||||
displayName: 'Custom Overview',
|
||||
layout: [],
|
||||
};
|
||||
|
||||
expect(getTabDisplayName(tab)).toBe('Custom Overview');
|
||||
});
|
||||
|
||||
it('should fallback to getTabLabelFromId if displayName is missing', () => {
|
||||
const tab: Tab = {
|
||||
id: EntityTabs.OVERVIEW,
|
||||
name: EntityTabs.OVERVIEW,
|
||||
layout: [],
|
||||
};
|
||||
|
||||
const result = getTabDisplayName(tab);
|
||||
|
||||
expect(typeof result).toBe('string');
|
||||
});
|
||||
|
||||
it('should return empty string if displayName and name are missing', () => {
|
||||
const tab: Tab = {
|
||||
id: EntityTabs.OVERVIEW,
|
||||
name: '' as EntityTabs,
|
||||
layout: [],
|
||||
};
|
||||
|
||||
const result = getTabDisplayName(tab);
|
||||
|
||||
expect(typeof result).toBe('string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('sortTabs', () => {
|
||||
it('should sort tabs according to given order', () => {
|
||||
const tabs: TabsProps['items'] = [
|
||||
|
||||
@ -691,3 +691,7 @@ export const updateWidgetHeightRecursively = (
|
||||
|
||||
return acc;
|
||||
}, [] as WidgetConfig[]);
|
||||
|
||||
export const getTabDisplayName = (item: Tab) => {
|
||||
return item.displayName ?? getTabLabelFromId(item.name as EntityTabs);
|
||||
};
|
||||
|
||||
@ -28,7 +28,6 @@ import { Tab } from '../generated/system/ui/uiCustomization';
|
||||
import { TestSummary } from '../generated/tests/testCase';
|
||||
import { FeedCounts } from '../interface/feed.interface';
|
||||
import { WidgetConfig } from '../pages/CustomizablePage/CustomizablePage.interface';
|
||||
import { getTabLabelFromId } from './CustomizePage/CustomizePageUtils';
|
||||
import i18n from './i18next/LocalUtil';
|
||||
import {
|
||||
getTableDetailPageBaseTabs,
|
||||
@ -106,7 +105,6 @@ class TableClassBase {
|
||||
].map((tab: EntityTabs) => ({
|
||||
id: tab,
|
||||
name: tab,
|
||||
displayName: getTabLabelFromId(tab),
|
||||
layout: this.getDefaultLayout(tab),
|
||||
editable: tab === EntityTabs.SCHEMA,
|
||||
}));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user