mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-11-03 20:19:31 +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
This commit is contained in:
		
							parent
							
								
									5771b6c014
								
							
						
					
					
						commit
						0081ece0ac
					
				@ -32,139 +32,189 @@ 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',
 | 
			
		||||
  'Knowledge Graph',
 | 
			
		||||
  'Queries',
 | 
			
		||||
  'Sample Data',
 | 
			
		||||
  'Schema',
 | 
			
		||||
  'View Definition',
 | 
			
		||||
  'dbt',
 | 
			
		||||
  EntityTabs.ACTIVITY_FEED,
 | 
			
		||||
  EntityTabs.CONTRACT,
 | 
			
		||||
  EntityTabs.CUSTOM_PROPERTIES,
 | 
			
		||||
  EntityTabs.PROFILER,
 | 
			
		||||
  EntityTabs.LINEAGE,
 | 
			
		||||
  EntityTabs.KNOWLEDGE_GRAPH,
 | 
			
		||||
  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,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@ -391,7 +391,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();
 | 
			
		||||
@ -475,9 +475,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();
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
@ -494,6 +494,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' })
 | 
			
		||||
@ -556,8 +559,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);
 | 
			
		||||
 | 
			
		||||
@ -602,4 +605,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
 | 
			
		||||
 | 
			
		||||
@ -35,6 +35,8 @@ import {
 | 
			
		||||
} 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';
 | 
			
		||||
 | 
			
		||||
@ -99,7 +101,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,
 | 
			
		||||
@ -107,7 +106,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