From e2d3e7f08c1f86eaf57c3e72a536d3dc822c2cc1 Mon Sep 17 00:00:00 2001 From: VahantSharma Date: Sat, 20 Dec 2025 20:51:56 +0000 Subject: [PATCH 1/2] fix(ui): prevent accidental navigation in AppTable and VariantsTable - Add shouldIgnoreRowClick guard to AppTable row click handler - Add shouldIgnoreRowClick guard to VariantsTable row click handler - Fixes issue where clicking checkboxes or dropdown menus triggers row navigation - Affects app management page and all variant table contexts (overview, deployment, evaluation) Builds on shared utility from PR #1 --- web/oss/src/components/VariantsComponents/Table/index.tsx | 4 +++- .../components/pages/app-management/components/AppTable.tsx | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/web/oss/src/components/VariantsComponents/Table/index.tsx b/web/oss/src/components/VariantsComponents/Table/index.tsx index 715b72d6b..7848aa8f8 100644 --- a/web/oss/src/components/VariantsComponents/Table/index.tsx +++ b/web/oss/src/components/VariantsComponents/Table/index.tsx @@ -6,6 +6,7 @@ import {atom, useAtom} from "jotai" import useURL from "@/oss/hooks/useURL" import {EnhancedVariant} from "@/oss/lib/shared/variant/transformer/types" +import {shouldIgnoreRowClick} from "@/oss/lib/tableRowClick" import {variantTableSelectionAtomFamily} from "@/oss/state/variant/atoms/selection" import ResizableTitle from "../../ResizableTitle" @@ -143,7 +144,8 @@ const VariantsTable = ({ onRow={(record: any) => ({ className: "variant-table-row", style: {cursor: "pointer"}, - onClick: () => { + onClick: (event) => { + if (shouldIgnoreRowClick(event)) return onRowClick(record) }, })} diff --git a/web/oss/src/components/pages/app-management/components/AppTable.tsx b/web/oss/src/components/pages/app-management/components/AppTable.tsx index b7f7569d1..d88a588bc 100644 --- a/web/oss/src/components/pages/app-management/components/AppTable.tsx +++ b/web/oss/src/components/pages/app-management/components/AppTable.tsx @@ -7,6 +7,7 @@ import {useRouter} from "next/router" import NoResultsFound from "@/oss/components/NoResultsFound/NoResultsFound" import useURL from "@/oss/hooks/useURL" import {formatDay} from "@/oss/lib/helpers/dateTimeHelper" +import {shouldIgnoreRowClick} from "@/oss/lib/tableRowClick" import {ListAppsItem} from "@/oss/lib/Types" import {getAppTypeIcon} from "../../prompts/assets/iconHelpers" @@ -126,7 +127,10 @@ const AppTable = ({filteredApps, openDeleteAppModal, openEditAppModal}: AppTable bordered onRow={(record) => ({ style: {cursor: "pointer"}, - onClick: () => router.push(`${baseAppURL}/${record.app_id}/overview`), + onClick: (event) => { + if (shouldIgnoreRowClick(event)) return + router.push(`${baseAppURL}/${record.app_id}/overview`) + }, })} locale={{emptyText: }} /> From 13985db11e5619f00be22e16bf4b984f7a3918bb Mon Sep 17 00:00:00 2001 From: VahantSharma Date: Sat, 20 Dec 2025 19:03:45 +0000 Subject: [PATCH 2/2] ui: add shared table row click guard and reuse in InfiniteVirtualTable --- .../hooks/useTableManager.tsx | 29 ++---------- web/oss/src/lib/tableRowClick.ts | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 web/oss/src/lib/tableRowClick.ts diff --git a/web/oss/src/components/InfiniteVirtualTable/hooks/useTableManager.tsx b/web/oss/src/components/InfiniteVirtualTable/hooks/useTableManager.tsx index a10623755..609dea4c2 100644 --- a/web/oss/src/components/InfiniteVirtualTable/hooks/useTableManager.tsx +++ b/web/oss/src/components/InfiniteVirtualTable/hooks/useTableManager.tsx @@ -6,11 +6,13 @@ import {Button, Grid, Tooltip} from "antd" import type {ColumnsType} from "antd/es/table" import clsx from "clsx" +import {shouldIgnoreRowClick} from "@/oss/lib/tableRowClick" + import type {InfiniteDatasetStore} from "../createInfiniteDatasetStore" import type { - TableScopeConfig, - TableFeaturePagination, InfiniteVirtualTableFeatureProps, + TableFeaturePagination, + TableScopeConfig, } from "../features/InfiniteVirtualTableFeatureShell" import type { InfiniteTableRowBase, @@ -20,29 +22,6 @@ import type { import useTableExport from "./useTableExport" -/** - * Helper to detect if a click event should be ignored for row navigation - * Returns true if the click was on an interactive element (button, link, dropdown, etc.) - */ -export const shouldIgnoreRowClick = (event: MouseEvent): boolean => { - const target = event.target as HTMLElement - - // Check if clicking on interactive elements - if ( - target.closest("button") || - target.closest("a") || - target.closest(".ant-dropdown-trigger") || - target.closest(".ant-checkbox-wrapper") || - target.closest(".ant-select") || - target.closest("input") || - target.closest("textarea") - ) { - return true - } - - return false -} - export interface UseTableManagerConfig { /** The dataset store for this table */ datasetStore: InfiniteDatasetStore diff --git a/web/oss/src/lib/tableRowClick.ts b/web/oss/src/lib/tableRowClick.ts new file mode 100644 index 000000000..e9be7ea05 --- /dev/null +++ b/web/oss/src/lib/tableRowClick.ts @@ -0,0 +1,46 @@ +import type {MouseEvent} from "react" + +/** + * Determines if a row click event should be ignored because it originated from + * an interactive element within the row. + * + * This helper prevents accidental row navigation when users interact with: + * - Buttons (including dropdown triggers) + * - Links + * - Checkboxes and radio buttons + * - Select dropdowns + * - Input fields and textareas + * + * @param event - The mouse click event from the table row + * @returns `true` if the click should be ignored (don't trigger row action), `false` otherwise + * + * @example + * ```tsx + * ({ + * onClick: (event) => { + * if (shouldIgnoreRowClick(event)) return + * navigateToRecord(record) + * } + * })} + * /> + * ``` + */ +export const shouldIgnoreRowClick = (event: MouseEvent): boolean => { + const target = event.target as HTMLElement + + // Check if clicking on interactive elements + if ( + target.closest("button") || + target.closest("a") || + target.closest(".ant-dropdown-trigger") || + target.closest(".ant-checkbox-wrapper") || + target.closest(".ant-select") || + target.closest("input") || + target.closest("textarea") + ) { + return true + } + + return false +}