diff --git a/openmetadata-ui/src/main/resources/ui/playwright/constant/customizeDetail.ts b/openmetadata-ui/src/main/resources/ui/playwright/constant/customizeDetail.ts
index 924e7009907..90e23265c2c 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/constant/customizeDetail.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/customizeDetail.ts
@@ -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,
];
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/CustomizeDetailPage.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/CustomizeDetailPage.spec.ts
index 5fa1c2f8d35..dcdf56a3f4d 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/CustomizeDetailPage.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/CustomizeDetailPage.spec.ts
@@ -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();
+ }
+ );
+ });
});
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts
index 8b4dd3ca3e7..8f848458652 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts
@@ -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
diff --git a/openmetadata-ui/src/main/resources/ui/src/App.tsx b/openmetadata-ui/src/main/resources/ui/src/App.tsx
index fcfd17f345b..150103bb9e3 100644
--- a/openmetadata-ui/src/main/resources/ui/src/App.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/App.tsx
@@ -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 = () => {