From 0d27b71f88cdc9b8ec7d9598d2448228d31adaf1 Mon Sep 17 00:00:00 2001 From: anuj-kumary Date: Fri, 21 Aug 2026 14:46:05 +0530 Subject: [PATCH 1/3] fix(ui): make whole Domain and Data Product rows clickable --- .../src/components/DataProduct/DataProductListPage.tsx | 7 +++++-- .../ui/src/components/DomainListing/DomainListPage.tsx | 4 +++- .../common/atoms/domain/ui/domainFieldRenderers.tsx | 6 ++++-- .../common/atoms/domain/ui/useDomainTableColumns.tsx | 9 +++++++-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx index 87f53bac40dd..6188820bc334 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx @@ -173,7 +173,10 @@ const DataProductListPage = ({ align="center" className={NAME_CELL_CLIP_CLASS} direction="row" - gap={3}> + gap={3} + onClick={() => + dataProductListing.actionHandlers.onEntityClick?.(entity) + }> { const { renderDomainCard } = useDomainCardTemplates(); const { columns: domainColumns, renderCell: renderDomainCell } = - useDomainTableColumns(); + useDomainTableColumns({ + onEntityClick: domainListing.actionHandlers.onEntityClick, + }); const selectedDomainEntities = useMemo( () => diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/domainFieldRenderers.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/domainFieldRenderers.tsx index f8f779d318f7..bdbe304d431d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/domainFieldRenderers.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/domainFieldRenderers.tsx @@ -69,7 +69,8 @@ export const LIST_EMPTY_STATE_CLASS = 'tw:flex tw:flex-1 tw:min-h-60 tw:items-center tw:justify-center'; export const renderDomainNameCell = ( - entity: Domain | DataProduct + entity: Domain | DataProduct, + onClick?: () => void ): ReactNode => { const entityName = getEntityName(entity); @@ -78,7 +79,8 @@ export const renderDomainNameCell = ( align="center" className={NAME_CELL_CLIP_CLASS} direction="row" - gap={3}> + gap={3} + onClick={onClick}> void; } export const useDomainTableColumns = ({ nameLabelKey = 'label.domain', tagSize = 'sm', + onEntityClick, }: UseDomainTableColumnsOptions = {}) => { const { t } = useTranslation(); @@ -49,7 +51,10 @@ export const useDomainTableColumns = ({ (entity: Domain, columnId: string): ReactNode => { switch (columnId) { case 'name': - return renderDomainNameCell(entity); + return renderDomainNameCell( + entity, + onEntityClick ? () => onEntityClick(entity) : undefined + ); case 'domainType': return renderDomainTypeCell(entity); case 'owners': @@ -62,7 +67,7 @@ export const useDomainTableColumns = ({ return null; } }, - [tagSize] + [tagSize, onEntityClick] ); return { columns, renderCell }; From 3ed927433b85352e25f735bed8cfc1e6a4457f46 Mon Sep 17 00:00:00 2001 From: anuj-kumary Date: Fri, 21 Aug 2026 15:28:05 +0530 Subject: [PATCH 2/3] addressed comments --- .../DataProduct/DataProductListPage.tsx | 18 ++++++++++++++---- .../atoms/domain/ui/domainFieldRenderers.tsx | 9 +++++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx index 6188820bc334..198121bdbf6c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx @@ -23,7 +23,14 @@ import { import { Globe01, Package, Plus } from '@untitledui/icons'; import classNames from 'classnames'; import { isEmpty } from 'lodash'; -import { FC, ReactNode, useCallback, useMemo, useState } from 'react'; +import { + FC, + MouseEvent, + ReactNode, + useCallback, + useMemo, + useState, +} from 'react'; import { useTranslation } from 'react-i18next'; import { NO_DATA, ROUTES } from '../../constants/constants'; import { LEARNING_PAGE_IDS } from '../../constants/Learning.constants'; @@ -168,15 +175,18 @@ const DataProductListPage = ({ entity.name && entity.displayName !== entity.name; + const handleNameClick = (event: MouseEvent) => { + event.stopPropagation(); + dataProductListing.actionHandlers.onEntityClick?.(entity); + }; + return ( - dataProductListing.actionHandlers.onEntityClick?.(entity) - }> + onClick={handleNameClick}> { const entityName = getEntityName(entity); + const handleNameClick = (event: MouseEvent) => { + event.stopPropagation(); + onClick?.(); + }; + return ( + onClick={onClick ? handleNameClick : undefined}> Date: Fri, 21 Aug 2026 17:22:15 +0530 Subject: [PATCH 3/3] Added unit test for the fix --- .../domain/ui/domainFieldRenderers.test.tsx | 79 +++++++++++++++++++ .../domain/ui/useDomainTableColumns.test.tsx | 78 ++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/domainFieldRenderers.test.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDomainTableColumns.test.tsx diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/domainFieldRenderers.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/domainFieldRenderers.test.tsx new file mode 100644 index 000000000000..cd0ee0a389d7 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/domainFieldRenderers.test.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2024 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { fireEvent, render, screen } from '@testing-library/react'; +import { ReactNode } from 'react'; +import { Domain } from '../../../../../generated/entity/domains/domain'; +import { renderDomainNameCell } from './domainFieldRenderers'; + +jest.mock('@openmetadata/ui-core-components', () => ({ + Avatar: () => , + Box: ({ + children, + onClick, + }: { + children: ReactNode; + onClick?: () => void; + }) => ( +
+ {children} +
+ ), + Typography: ({ children }: { children: ReactNode }) => ( + {children} + ), +})); + +jest.mock('../../../../../utils/TooltipUtils', () => ({ + renderBreakableTooltip: (value: string) => value, +})); + +const DOMAIN = { + id: 'domain-id', + name: 'engineering', + displayName: 'Engineering', + fullyQualifiedName: 'engineering', +} as Domain; + +describe('renderDomainNameCell', () => { + it('navigates once when the name cell is clicked', () => { + const onClick = jest.fn(); + + render(<>{renderDomainNameCell(DOMAIN, onClick)}); + fireEvent.click(screen.getByText('Engineering')); + + expect(onClick).toHaveBeenCalledTimes(1); + }); + + it('stops the click from bubbling to the row so navigation is not duplicated', () => { + const onClick = jest.fn(); + const rowClick = jest.fn(); + + render( +
{renderDomainNameCell(DOMAIN, onClick)}
+ ); + fireEvent.click(screen.getByText('Engineering')); + + expect(onClick).toHaveBeenCalledTimes(1); + expect(rowClick).not.toHaveBeenCalled(); + }); + + it('does not attach a click handler when no onClick is provided', () => { + const rowClick = jest.fn(); + + render(
{renderDomainNameCell(DOMAIN)}
); + fireEvent.click(screen.getByText('Engineering')); + + // With no cell handler the click falls through to the row unchanged. + expect(rowClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDomainTableColumns.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDomainTableColumns.test.tsx new file mode 100644 index 000000000000..3bf806d24d66 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDomainTableColumns.test.tsx @@ -0,0 +1,78 @@ +/* + * Copyright 2024 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { fireEvent, render, renderHook, screen } from '@testing-library/react'; +import { ReactNode } from 'react'; +import { Domain } from '../../../../../generated/entity/domains/domain'; +import { useDomainTableColumns } from './useDomainTableColumns'; + +jest.mock('react-i18next', () => ({ + useTranslation: () => ({ t: (key: string) => key }), +})); + +jest.mock('@openmetadata/ui-core-components', () => ({ + Avatar: () => , + Box: ({ + children, + onClick, + }: { + children: ReactNode; + onClick?: () => void; + }) => ( +
+ {children} +
+ ), + Typography: ({ children }: { children: ReactNode }) => ( + {children} + ), +})); + +jest.mock('../../../../../utils/TooltipUtils', () => ({ + renderBreakableTooltip: (value: string) => value, +})); + +const DOMAIN = { + id: 'domain-id', + name: 'engineering', + displayName: 'Engineering', + fullyQualifiedName: 'engineering', +} as Domain; + +describe('useDomainTableColumns', () => { + it('routes a name-cell click to onEntityClick with the row entity', () => { + const onEntityClick = jest.fn(); + + const { result } = renderHook(() => + useDomainTableColumns({ onEntityClick }) + ); + + render(<>{result.current.renderCell(DOMAIN, 'name')}); + fireEvent.click(screen.getByText('Engineering')); + + expect(onEntityClick).toHaveBeenCalledTimes(1); + expect(onEntityClick).toHaveBeenCalledWith(DOMAIN); + }); + + it('renders the name cell without a click handler when onEntityClick is omitted', () => { + const rowClick = jest.fn(); + + const { result } = renderHook(() => useDomainTableColumns()); + + render( +
{result.current.renderCell(DOMAIN, 'name')}
+ ); + fireEvent.click(screen.getByText('Engineering')); + + expect(rowClick).toHaveBeenCalledTimes(1); + }); +});