From 54dd39d5e6c714c58adbde72a380e318b2e9b0fa Mon Sep 17 00:00:00 2001 From: Amrit Borah Date: Mon, 5 Jan 2026 18:33:38 +0530 Subject: [PATCH 01/12] feat: add expandable rows support in table --- src/Shared/Components/Table/TableContent.tsx | 116 ++++++++++++++++--- src/Shared/Components/Table/styles.scss | 9 ++ src/Shared/Components/Table/types.ts | 12 +- 3 files changed, 119 insertions(+), 18 deletions(-) diff --git a/src/Shared/Components/Table/TableContent.tsx b/src/Shared/Components/Table/TableContent.tsx index f7239c885..9177e076c 100644 --- a/src/Shared/Components/Table/TableContent.tsx +++ b/src/Shared/Components/Table/TableContent.tsx @@ -22,11 +22,14 @@ import { useEffectAfterMount } from '@Common/Helper' import { Pagination } from '@Common/Pagination' import { SortableTableHeaderCell } from '@Common/SortableTableHeaderCell' import { CHECKBOX_VALUE } from '@Common/Types' +import { Button, ButtonStyleType, ButtonVariantType } from '@Shared/Components/Button' +import { Icon } from '@Shared/Components/Icon' +import { ComponentSizeType } from '@Shared/constants' import { BulkSelection } from '../BulkSelection' import BulkSelectionActionWidget from './BulkSelectionActionWidget' import { BULK_ACTION_GUTTER_LABEL, EVENT_TARGET, SHIMMER_DUMMY_ARRAY } from './constants' -import { BulkActionStateType, FiltersTypeEnum, PaginationEnum, SignalsType, TableContentProps } from './types' +import { BulkActionStateType, FiltersTypeEnum, PaginationEnum, RowType, SignalsType, TableContentProps } from './types' import useTableWithKeyboardShortcuts from './useTableWithKeyboardShortcuts' import { getStickyColumnConfig, scrollToShowActiveElementIfNeeded } from './utils' @@ -61,6 +64,7 @@ const TableContent = < const [bulkActionState, setBulkActionState] = useState(null) const [showBorderRightOnStickyElements, setShowBorderRightOnStickyElements] = useState(false) + const [expandState, setExpandState] = useState>({}) const { width: rowOnHoverComponentWidth, Component: RowOnHoverComponent } = rowActionOnHoverConfig || {} @@ -92,10 +96,50 @@ const TableContent = < .join(' '), } = resizableConfig ?? {} - const gridTemplateColumns = rowOnHoverComponentWidth + const { visibleRows, areAllRowsExpanded, isAnyRowExpandable } = useMemo(() => { + const normalizedFilteredRows = filteredRows ?? [] + + const paginatedRows = + paginationVariant !== PaginationEnum.PAGINATED || + (paginationVariant === PaginationEnum.PAGINATED && getRows) + ? normalizedFilteredRows + : normalizedFilteredRows.slice(offset, offset + pageSize) + + const _isAnyRowExpandable = paginatedRows.some((row) => !!row.expandableRows) + + const _areAllRowsExpanded = + _isAnyRowExpandable && + paginatedRows.reduce((acc, row) => { + if (row.expandableRows) { + return acc && !!expandState[row.id] + } + + return acc + }, true) + + const paginatedRowsWithExpandedRows = paginatedRows.flatMap((row) => { + if (row.expandableRows && expandState[row.id]) { + return [row, ...row.expandableRows] + } + + return [row] + }) + + return { + visibleRows: paginatedRowsWithExpandedRows, + areAllRowsExpanded: _areAllRowsExpanded, + isAnyRowExpandable: _isAnyRowExpandable, + } + }, [paginationVariant, offset, pageSize, filteredRows, expandState]) + + const gridTemplateColumnsWithoutExpandButton = rowOnHoverComponentWidth ? `${initialGridTemplateColumns} ${typeof rowOnHoverComponentWidth === 'number' ? `minmax(${rowOnHoverComponentWidth}px, 1fr)` : rowOnHoverComponentWidth}` : initialGridTemplateColumns + const gridTemplateColumns = isAnyRowExpandable + ? `16px ${gridTemplateColumnsWithoutExpandButton}` + : gridTemplateColumnsWithoutExpandButton + useEffect(() => { const scrollEventHandler = () => { setShowBorderRightOnStickyElements(rowsContainerRef.current?.scrollLeft > 0) @@ -113,18 +157,6 @@ const TableContent = < const bulkSelectionCount = isBulkSelectionApplied ? totalRows : (getSelectedIdentifiersCount?.() ?? 0) - const visibleRows = useMemo(() => { - const normalizedFilteredRows = filteredRows ?? [] - - const paginatedRows = - paginationVariant !== PaginationEnum.PAGINATED || - (paginationVariant === PaginationEnum.PAGINATED && getRows) - ? normalizedFilteredRows - : normalizedFilteredRows.slice(offset, offset + pageSize) - - return paginatedRows - }, [paginationVariant, offset, pageSize, filteredRows]) - const isBEPagination = !!getRows const showPagination = @@ -155,6 +187,18 @@ const TableContent = < handleSorting(newSortBy) } + const toggleExpandAll = () => { + setExpandState( + visibleRows.reduce((acc, row) => { + if ((row as RowType).expandableRows) { + acc[row.id] = !areAllRowsExpanded + } + + return acc + }, {}), + ) + } + const focusActiveRow = (node: HTMLDivElement) => { if ( node && @@ -216,6 +260,7 @@ const TableContent = < return visibleRows.map((row, visibleRowIndex) => { const isRowActive = activeRowIndex === visibleRowIndex const isRowBulkSelected = !!bulkSelectionState[row.id] || isBulkSelectionApplied + const isExpandedRow = row.id.startsWith('expanded-row') const handleChangeActiveRowIndex = () => { setActiveRowIndex(visibleRowIndex) @@ -225,6 +270,13 @@ const TableContent = < handleToggleBulkSelectionOnRow(row) } + const toggleExpandRow = () => { + setExpandState({ + ...expandState, + [row.id]: !expandState[row.id], + }) + } + return (
+ {!isExpandedRow && !!(row as RowType).expandableRows ? ( + diff --git a/src/Common/SortableTableHeaderCell/types.ts b/src/Common/SortableTableHeaderCell/types.ts index 15f3f058f..346c80a3f 100644 --- a/src/Common/SortableTableHeaderCell/types.ts +++ b/src/Common/SortableTableHeaderCell/types.ts @@ -26,6 +26,10 @@ export type SortableTableHeaderCellProps = { * @default false */ showTippyOnTruncate?: boolean + /** + * If provided, shown in a tooltip on info-icon-outline beside the label + */ + infoTooltipText?: string } & ( | { /** diff --git a/src/Shared/Components/Table/InternalTable.tsx b/src/Shared/Components/Table/InternalTable.tsx index d179c91a4..f6796ee3f 100644 --- a/src/Shared/Components/Table/InternalTable.tsx +++ b/src/Shared/Components/Table/InternalTable.tsx @@ -56,6 +56,7 @@ const InternalTable = < rowActionOnHoverConfig, pageSizeOptions, clearFilters: userGivenUrlClearFilters, + rowStartIconConfig, }: InternalTableProps) => { const { sortBy, @@ -219,6 +220,7 @@ const InternalTable = < stylesConfig={stylesConfig} getRows={getRows} totalRows={totalRows} + rowStartIconConfig={rowStartIconConfig} /> ) diff --git a/src/Shared/Components/Table/TableContent.tsx b/src/Shared/Components/Table/TableContent.tsx index 9177e076c..871ec952e 100644 --- a/src/Shared/Components/Table/TableContent.tsx +++ b/src/Shared/Components/Table/TableContent.tsx @@ -28,7 +28,7 @@ import { ComponentSizeType } from '@Shared/constants' import { BulkSelection } from '../BulkSelection' import BulkSelectionActionWidget from './BulkSelectionActionWidget' -import { BULK_ACTION_GUTTER_LABEL, EVENT_TARGET, SHIMMER_DUMMY_ARRAY } from './constants' +import { ACTION_GUTTER_SIZE, BULK_ACTION_GUTTER_LABEL, EVENT_TARGET, SHIMMER_DUMMY_ARRAY } from './constants' import { BulkActionStateType, FiltersTypeEnum, PaginationEnum, RowType, SignalsType, TableContentProps } from './types' import useTableWithKeyboardShortcuts from './useTableWithKeyboardShortcuts' import { getStickyColumnConfig, scrollToShowActiveElementIfNeeded } from './utils' @@ -56,15 +56,22 @@ const TableContent = < areFilteredRowsLoading, getRows, totalRows, + rowStartIconConfig, }: TableContentProps) => { const rowsContainerRef = useRef(null) const parentRef = useRef(null) const bulkSelectionButtonRef = useRef(null) const headerRef = useRef(null) + const skipFocusActiveRowRef = useRef(false) const [bulkActionState, setBulkActionState] = useState(null) const [showBorderRightOnStickyElements, setShowBorderRightOnStickyElements] = useState(false) - const [expandState, setExpandState] = useState>({}) + const [expandState, _setExpandState] = useState>({}) + + const setExpandState: typeof _setExpandState = (value) => { + skipFocusActiveRowRef.current = true + _setExpandState(value) + } const { width: rowOnHoverComponentWidth, Component: RowOnHoverComponent } = rowActionOnHoverConfig || {} @@ -137,7 +144,7 @@ const TableContent = < : initialGridTemplateColumns const gridTemplateColumns = isAnyRowExpandable - ? `16px ${gridTemplateColumnsWithoutExpandButton}` + ? `${ACTION_GUTTER_SIZE}px ${gridTemplateColumnsWithoutExpandButton}` : gridTemplateColumnsWithoutExpandButton useEffect(() => { @@ -200,6 +207,11 @@ const TableContent = < } const focusActiveRow = (node: HTMLDivElement) => { + if (skipFocusActiveRowRef.current) { + skipFocusActiveRowRef.current = false + return + } + if ( node && !['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName.toUpperCase()) && @@ -277,6 +289,10 @@ const TableContent = < }) } + const hasBulkOrExpandAction = + (isAnyRowExpandable && !isExpandedRow && !!(row as RowType).expandableRows) || + !!bulkSelectionReturnValue + return (
+ {rowStartIconConfig && !isExpandedRow && ( +
+ +
+ )} + {!isExpandedRow && !!(row as RowType).expandableRows ? ( -
) : null} - {isAnyRowExpandable && (isExpandedRow || !(row as RowType).expandableRows) &&
} + {/* empty div needed for alignment; therefore hide if rowStartIconConfig (only applies to parent rows) is present */} + {isAnyRowExpandable && + (isExpandedRow || (!(row as RowType).expandableRows && !rowStartIconConfig)) && ( +
+ )} {visibleColumns.map(({ field, horizontallySticky: isStickyColumn, CellComponent }, index) => { const isBulkActionGutter = field === BULK_ACTION_GUTTER_LABEL @@ -321,7 +358,7 @@ const TableContent = < if (isBulkActionGutter && !isExpandedRow) { return (
@@ -409,7 +446,13 @@ const TableContent = < {isAnyRowExpandable ? (
) : null} {visibleColumns.map( @@ -501,7 +522,12 @@ const TableContent = < const isBulkActionGutter = field === BULK_ACTION_GUTTER_LABEL const horizontallySticky = isStickyColumn || isBulkActionGutter const { className: stickyClassName = '', left: stickyLeftValue = '' } = - horizontallySticky ? getStickyColumnConfig(gridTemplateColumns, index) : {} + horizontallySticky + ? getStickyColumnConfig( + gridTemplateColumns, + index + (isAnyRowExpandable ? 1 : 0), + ) + : {} if (field === BULK_ACTION_GUTTER_LABEL) { return ( diff --git a/src/Shared/Components/Table/styles.scss b/src/Shared/Components/Table/styles.scss index 1fea37dc4..ecc29cb3f 100644 --- a/src/Shared/Components/Table/styles.scss +++ b/src/Shared/Components/Table/styles.scss @@ -40,6 +40,13 @@ left: -20px; width: 20px; } + + &.expand-row-btn::before, + &.row-start-icon::before, + &.expanded-tree-libe::before { + left: -24px; + width: 24px; + } } &--scrolled { @@ -137,7 +144,7 @@ content: ''; width: 1px; height: 100%; - background: var(--N100); + background: var(--N200); left: calc(50% - 1px); // offset to left by width for perfect centering top: 0; position: absolute; diff --git a/src/Shared/Components/Table/types.ts b/src/Shared/Components/Table/types.ts index 914723d86..ddf7f27ea 100644 --- a/src/Shared/Components/Table/types.ts +++ b/src/Shared/Components/Table/types.ts @@ -329,7 +329,7 @@ export type InternalTableProps< */ rowStartIconConfig?: Omit - onRowClick?: (row: RowType) => void + onRowClick?: (row: RowType, isExpandedRow: boolean) => void } & ( | { /** From 351a35a57e1286bd9d19eaaffaae9f8629108133 Mon Sep 17 00:00:00 2001 From: Amrit Borah Date: Wed, 14 Jan 2026 16:00:30 +0530 Subject: [PATCH 08/12] fix: better table states --- src/Shared/Components/Table/InternalTable.tsx | 14 +++++--- src/Shared/Components/Table/TableContent.tsx | 34 +++++++++++++------ src/Shared/Components/Table/types.ts | 12 +++++-- src/Shared/Components/Table/utils.ts | 3 +- 4 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/Shared/Components/Table/InternalTable.tsx b/src/Shared/Components/Table/InternalTable.tsx index 99a7045d3..007ab9084 100644 --- a/src/Shared/Components/Table/InternalTable.tsx +++ b/src/Shared/Components/Table/InternalTable.tsx @@ -58,6 +58,7 @@ const InternalTable = < clearFilters: userGivenUrlClearFilters, rowStartIconConfig, onRowClick, + areFiltersApplied: userProvidedAreFiltersApplied, }: InternalTableProps) => { const { sortBy, @@ -126,6 +127,7 @@ const InternalTable = < rows, filter, filterData, + additionalProps, visibleColumns.find(({ field }) => field === sortBy)?.comparator, ) @@ -140,10 +142,14 @@ const InternalTable = < // useAsync hook for 'rows' scenario const [_areRowsLoading, rowsResult, rowsError, reloadRows] = useAsync( handleFiltering, - [searchKey, sortBy, sortOrder, rows, JSON.stringify(otherFilters), visibleColumns], + [searchKey, filter, sortBy, sortOrder, rows, JSON.stringify(otherFilters), visibleColumns], !!rows, ) + // NOTE: passing getRows to queryKey won't trigger a refetch + // since it is a function + const lastUpdatedGetRowsInstance = useMemo(() => new Date().toISOString(), [getRows]) + // useAsync hook for 'getRows' scenario const { isFetching: _areGetRowsLoading, @@ -160,8 +166,7 @@ const InternalTable = < searchKey, sortBy, sortOrder, - // !TODO: functions in queryKey cannot trigger refetch - // getRows, + lastUpdatedGetRowsInstance, offset, pageSize, JSON.stringify(otherFilters), @@ -191,7 +196,8 @@ const InternalTable = < } if (!areFilteredRowsLoading && !filteredRows?.length && !loading) { - return filtersVariant !== FiltersTypeEnum.NONE && areFiltersApplied ? ( + return filtersVariant !== FiltersTypeEnum.NONE && + (userProvidedAreFiltersApplied !== undefined ? userProvidedAreFiltersApplied : areFiltersApplied) ? ( { const scrollEventHandler = () => { @@ -248,13 +249,15 @@ const TableContent = < } const renderRows = () => { - if (loading) { + if (loading && !visibleColumns.length) { return SHIMMER_DUMMY_ARRAY.map((shimmerRowLabel) => (
- {isBulkSelectionConfigured ?
: null} + {isBulkSelectionConfigured || rowStartIconConfig || isAnyRowExpandable ? ( +
+ ) : null} {SHIMMER_DUMMY_ARRAY.map((shimmerCellLabel) => (
))} @@ -262,7 +265,7 @@ const TableContent = < )) } - if (areFilteredRowsLoading) { + if (areFilteredRowsLoading || (loading && visibleColumns.length)) { return SHIMMER_DUMMY_ARRAY.map((shimmerRowLabel) => (
+ {isBulkSelectionConfigured || rowStartIconConfig || isAnyRowExpandable ? ( +
+
+
+ ) : null} {visibleColumns.map(({ label }) => (
@@ -364,7 +372,7 @@ const TableContent = < ariaLabel="Expand/Collapse row" showAriaLabelInTippy={false} variant={ButtonVariantType.borderLess} - size={ComponentSizeType.xs} + size={ComponentSizeType.xxs} style={ButtonStyleType.neutral} onClick={toggleExpandRow} /> @@ -467,9 +475,11 @@ const TableContent = < ref={headerRef} className="bg__primary dc__min-width-fit-content px-20 border__secondary--bottom dc__position-sticky dc__zi-2 dc__top-0 generic-table__header" > - {loading ? ( + {loading && !visibleColumns.length ? (
- {isBulkSelectionConfigured ?
: null} + {isBulkSelectionConfigured || rowStartIconConfig || isAnyRowExpandable ? ( +
+ ) : null} {SHIMMER_DUMMY_ARRAY.map((label) => (
))} @@ -498,13 +508,15 @@ const TableContent = < ariaLabel="Expand/Collapse all rows" showAriaLabelInTippy={false} variant={ButtonVariantType.borderLess} - size={ComponentSizeType.xs} + size={ComponentSizeType.xxs} style={ButtonStyleType.neutral} onClick={toggleExpandAll} />
) : null} + {!isAnyRowExpandable && rowStartIconConfig &&
} + {visibleColumns.map( ( { diff --git a/src/Shared/Components/Table/types.ts b/src/Shared/Components/Table/types.ts index ddf7f27ea..cdf996711 100644 --- a/src/Shared/Components/Table/types.ts +++ b/src/Shared/Components/Table/types.ts @@ -237,7 +237,11 @@ export type ViewWrapperProps< : {}) > -type FilterConfig = { +type FilterConfig< + FilterVariant extends FiltersTypeEnum, + RowData extends unknown, + AdditionalProps extends Record, +> = { filtersVariant: FilterVariant /** * Props for useUrlFilters/useStateFilters hooks @@ -251,12 +255,14 @@ type FilterConfig, filterData: UseFiltersReturnType) => boolean + : (row: RowType, filterData: UseFiltersReturnType, additionalProps: AdditionalProps) => boolean clearFilters?: FilterVariant extends FiltersTypeEnum.URL ? () => void : FilterVariant extends FiltersTypeEnum.STATE ? never : never + + areFiltersApplied?: FilterVariant extends FiltersTypeEnum.NONE ? never : boolean } export type InternalTableProps< @@ -360,7 +366,7 @@ export type InternalTableProps< pageSizeOptions?: never } ) & - FilterConfig + FilterConfig export type UseResizableTableConfigWrapperProps< RowData extends unknown, diff --git a/src/Shared/Components/Table/utils.ts b/src/Shared/Components/Table/utils.ts index 8e9ff9580..5d0f4b46a 100644 --- a/src/Shared/Components/Table/utils.ts +++ b/src/Shared/Components/Table/utils.ts @@ -42,11 +42,12 @@ export const searchAndSortRows = < rows: TableProps['rows'], filter: TableProps['filter'], filterData: UseFiltersReturnType, + additionalProps: AdditionalProps, comparator?: Column['comparator'], ): Awaited['getRows']>> => { const { sortBy, sortOrder } = filterData ?? {} - const filteredRows = filter ? rows.filter((row) => filter(row, filterData)) : rows + const filteredRows = filter ? rows.filter((row) => filter(row, filterData, additionalProps)) : rows return { rows: From 460b94183fb8ea15016f7d19704f8c1de52e2dfc Mon Sep 17 00:00:00 2001 From: Amrit Borah Date: Wed, 14 Jan 2026 17:28:26 +0530 Subject: [PATCH 09/12] feat: expand/collapse using right/left arrow --- .../CICDHistory/ConflictedResourcesTable.tsx | 3 +- src/Shared/Components/Table/TableContent.tsx | 33 ++++++++++++++++++- src/Shared/Components/Table/styles.scss | 4 +++ src/Shared/Components/Table/types.ts | 2 ++ .../Table/useTableWithKeyboardShortcuts.ts | 16 +++++++++ 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/Shared/Components/CICDHistory/ConflictedResourcesTable.tsx b/src/Shared/Components/CICDHistory/ConflictedResourcesTable.tsx index 238293eae..9e24fc76f 100644 --- a/src/Shared/Components/CICDHistory/ConflictedResourcesTable.tsx +++ b/src/Shared/Components/CICDHistory/ConflictedResourcesTable.tsx @@ -10,7 +10,6 @@ import './ConflictedResourcesTable.scss' const Wrapper = ({ children }: TableViewWrapperProps) => (
{children}
) -const filter = () => true const ConflictedResourcesTable = ({ resourceConflictDetails }: ConflictedResourcesTableProps) => { const rows: RowsType = useMemo( @@ -42,7 +41,7 @@ const ConflictedResourcesTable = ({ resourceConflictDetails }: ConflictedResourc }} filtersVariant={FiltersTypeEnum.STATE} ViewWrapper={Wrapper} - filter={filter} + filter={null} /> ) } diff --git a/src/Shared/Components/Table/TableContent.tsx b/src/Shared/Components/Table/TableContent.tsx index e21fe83d1..031ae920b 100644 --- a/src/Shared/Components/Table/TableContent.tsx +++ b/src/Shared/Components/Table/TableContent.tsx @@ -35,6 +35,7 @@ import { FiltersTypeEnum, PaginationEnum, RowType, + SignalEnum, SignalsType, TableContentProps, } from './types' @@ -189,7 +190,7 @@ const TableContent = < useEffectAfterMount(() => { setActiveRowIndex(0) - }, [offset, visibleRows]) + }, [offset]) useEffect(() => { setIdentifiers?.( @@ -204,6 +205,36 @@ const TableContent = < handleSorting(newSortBy) } + useEffect(() => { + if (!isAnyRowExpandable) { + return () => {} + } + + const getExpandCollapseRowHandler = + (state: boolean) => + ({ detail: { activeRowData } }) => { + if ((activeRowData as RowType).expandableRows) { + setExpandState({ + ...expandState, + [activeRowData.id]: state, + }) + } + } + + const handleExpandRow = getExpandCollapseRowHandler(true) + const handleCollapseRow = getExpandCollapseRowHandler(false) + + const signals = EVENT_TARGET as SignalsType + + signals.addEventListener(SignalEnum.EXPAND_ROW, handleExpandRow) + signals.addEventListener(SignalEnum.COLLAPSE_ROW, handleCollapseRow) + + return () => { + signals.removeEventListener(SignalEnum.EXPAND_ROW, handleExpandRow) + signals.removeEventListener(SignalEnum.COLLAPSE_ROW, handleCollapseRow) + } + }, [isAnyRowExpandable]) + const toggleExpandAll = (e: MouseEvent) => { e.stopPropagation() diff --git a/src/Shared/Components/Table/styles.scss b/src/Shared/Components/Table/styles.scss index ecc29cb3f..a790c9b56 100644 --- a/src/Shared/Components/Table/styles.scss +++ b/src/Shared/Components/Table/styles.scss @@ -76,6 +76,10 @@ outline: none; } + &--expanded-row:has(+ .generic-table__row--expanded-row) { + border-bottom: 0px; + } + &:hover, &:hover > *, &--active, diff --git a/src/Shared/Components/Table/types.ts b/src/Shared/Components/Table/types.ts index cdf996711..baff6ce0c 100644 --- a/src/Shared/Components/Table/types.ts +++ b/src/Shared/Components/Table/types.ts @@ -33,6 +33,8 @@ import { useBulkSelection, UseBulkSelectionProps } from '../BulkSelection' export interface UseFiltersReturnType extends UseStateFiltersReturnType {} export enum SignalEnum { + COLLAPSE_ROW = 'collapse-row', + EXPAND_ROW = 'expand-row', ENTER_PRESSED = 'enter-pressed', DELETE_PRESSED = 'delete-pressed', ESCAPE_PRESSED = 'escape-pressed', diff --git a/src/Shared/Components/Table/useTableWithKeyboardShortcuts.ts b/src/Shared/Components/Table/useTableWithKeyboardShortcuts.ts index a7a67a14b..cb51044c9 100644 --- a/src/Shared/Components/Table/useTableWithKeyboardShortcuts.ts +++ b/src/Shared/Components/Table/useTableWithKeyboardShortcuts.ts @@ -101,6 +101,20 @@ const useTableWithKeyboardShortcuts = < ) useEffect(() => { + registerShortcut({ + keys: ['ArrowLeft'], + callback: () => { + dispatchEvent(SignalEnum.COLLAPSE_ROW) + }, + }) + + registerShortcut({ + keys: ['ArrowRight'], + callback: () => { + dispatchEvent(SignalEnum.EXPAND_ROW) + }, + }) + registerShortcut({ keys: ['ArrowDown'], callback: () => { @@ -142,6 +156,8 @@ const useTableWithKeyboardShortcuts = < unregisterShortcut(['Enter']) unregisterShortcut(['Backspace']) unregisterShortcut(['.']) + unregisterShortcut(['ArrowLeft']) + unregisterShortcut(['ArrowRight']) } }, [getMoveFocusToNextRowHandler, getMoveFocusToPreviousRowHandler, dispatchEvent]) From 7ab7b142069dec6e7df770b9d188d3f6cf5c80a2 Mon Sep 17 00:00:00 2001 From: Amrit Borah Date: Thu, 15 Jan 2026 15:26:24 +0530 Subject: [PATCH 10/12] feat: if onRowCilck provided handle enter press internally --- src/Shared/Components/Table/TableContent.tsx | 18 ++++++++++++++++++ src/Shared/Components/Table/types.ts | 1 + 2 files changed, 19 insertions(+) diff --git a/src/Shared/Components/Table/TableContent.tsx b/src/Shared/Components/Table/TableContent.tsx index 031ae920b..1c1185f18 100644 --- a/src/Shared/Components/Table/TableContent.tsx +++ b/src/Shared/Components/Table/TableContent.tsx @@ -235,6 +235,24 @@ const TableContent = < } }, [isAnyRowExpandable]) + useEffect(() => { + if (!onRowClick) { + return () => {} + } + + const handleEnterPress = ({ detail: { activeRowData } }) => { + onRowClick(activeRowData, activeRowData.id.startsWith('expanded-row-' satisfies ExpandedRowPrefixType)) + } + + const signals = EVENT_TARGET as SignalsType + + signals.addEventListener(SignalEnum.ENTER_PRESSED, handleEnterPress) + + return () => { + signals.removeEventListener(SignalEnum.ENTER_PRESSED, handleEnterPress) + } + }, [onRowClick]) + const toggleExpandAll = (e: MouseEvent) => { e.stopPropagation() diff --git a/src/Shared/Components/Table/types.ts b/src/Shared/Components/Table/types.ts index baff6ce0c..a45d2b7f8 100644 --- a/src/Shared/Components/Table/types.ts +++ b/src/Shared/Components/Table/types.ts @@ -423,6 +423,7 @@ export type TableProps< | 'clearFilters' | 'rowStartIconConfig' | 'onRowClick' + | 'areFiltersApplied' > export type BulkActionStateType = string | null From b4d2bcbfc9da596f8d81fc173a9ea8b106def131 Mon Sep 17 00:00:00 2001 From: Amrit Borah Date: Mon, 19 Jan 2026 13:41:34 +0530 Subject: [PATCH 11/12] fix: review 3Bcomments --- src/Shared/Components/Table/TableContent.tsx | 18 ++++++++---------- src/Shared/Components/Table/styles.scss | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Shared/Components/Table/TableContent.tsx b/src/Shared/Components/Table/TableContent.tsx index 1c1185f18..59c755fe6 100644 --- a/src/Shared/Components/Table/TableContent.tsx +++ b/src/Shared/Components/Table/TableContent.tsx @@ -214,10 +214,10 @@ const TableContent = < (state: boolean) => ({ detail: { activeRowData } }) => { if ((activeRowData as RowType).expandableRows) { - setExpandState({ - ...expandState, + setExpandState((prev) => ({ + ...prev, [activeRowData.id]: state, - }) + })) } } @@ -297,6 +297,8 @@ const TableContent = < return Object.values(bulkSelectionState) } + const showIconOrExpandActionGutter = isBulkSelectionConfigured || !!rowStartIconConfig || isAnyRowExpandable + const renderRows = () => { if (loading && !visibleColumns.length) { return SHIMMER_DUMMY_ARRAY.map((shimmerRowLabel) => ( @@ -304,9 +306,7 @@ const TableContent = < key={shimmerRowLabel} className={`px-20 flex left py-12 dc__gap-16 ${showSeparatorBetweenRows ? 'border__secondary--bottom' : ''}`} > - {isBulkSelectionConfigured || rowStartIconConfig || isAnyRowExpandable ? ( -
- ) : null} + {showIconOrExpandActionGutter ?
: null} {SHIMMER_DUMMY_ARRAY.map((shimmerCellLabel) => (
))} @@ -323,7 +323,7 @@ const TableContent = < gridTemplateColumns, }} > - {isBulkSelectionConfigured || rowStartIconConfig || isAnyRowExpandable ? ( + {showIconOrExpandActionGutter ? (
@@ -526,9 +526,7 @@ const TableContent = < > {loading && !visibleColumns.length ? (
- {isBulkSelectionConfigured || rowStartIconConfig || isAnyRowExpandable ? ( -
- ) : null} + {showIconOrExpandActionGutter ?
: null} {SHIMMER_DUMMY_ARRAY.map((label) => (
))} diff --git a/src/Shared/Components/Table/styles.scss b/src/Shared/Components/Table/styles.scss index a790c9b56..16bfa0e00 100644 --- a/src/Shared/Components/Table/styles.scss +++ b/src/Shared/Components/Table/styles.scss @@ -43,7 +43,7 @@ &.expand-row-btn::before, &.row-start-icon::before, - &.expanded-tree-libe::before { + &.expanded-tree-line::before { left: -24px; width: 24px; } From ae57aab9f0e969aedc1c383914c89e452e5f7819 Mon Sep 17 00:00:00 2001 From: Amrit Borah Date: Tue, 20 Jan 2026 11:05:42 +0530 Subject: [PATCH 12/12] chore: update common-lib --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 194e77c32..bcc9c0f6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.22.0-alpha-11", + "version": "1.22.0-alpha-12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.22.0-alpha-11", + "version": "1.22.0-alpha-12", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index b36090676..09462f1d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtron-labs/devtron-fe-common-lib", - "version": "1.22.0-alpha-11", + "version": "1.22.0-alpha-12", "description": "Supporting common component library", "type": "module", "main": "dist/index.js",