1- import { useCallback , useMemo , useRef , useState } from 'react' ;
1+ import { useCallback , useMemo , useState } from 'react' ;
22import Link from 'next/link' ;
33import cx from 'classnames' ;
44import {
@@ -28,6 +28,9 @@ import { ColumnDef } from '@tanstack/react-table';
2828import { useVirtualizer } from '@tanstack/react-virtual' ;
2929
3030import { CsvExportButton } from './components/CsvExportButton' ;
31+ import DBRowTableFieldWithPopover , {
32+ type CellFilterHandler ,
33+ } from './components/DBTable/DBRowTableFieldWithPopover' ;
3134import TableHeader from './components/DBTable/TableHeader' ;
3235import { useCsvExport } from './hooks/useCsvExport' ;
3336import type { RowAction } from './hooks/useOnClickLinkBuilder' ;
@@ -61,6 +64,7 @@ export const Table = ({
6164 variant = 'default' ,
6265 alternateRowBackground = false ,
6366 className,
67+ onCellFilter,
6468} : {
6569 data : any [ ] ;
6670 columns : {
@@ -78,6 +82,9 @@ export const Table = ({
7882 // Ordered conditional color rules evaluated against each cell's value
7983 // (last match wins). Resolves to `color` when no rule matches.
8084 colorRules ?: ColorCondition [ ] ;
85+ // Expression (or PromQL label) the column's values filter on. Cells of a
86+ // column with a key offer filter / exclude actions via `onCellFilter`.
87+ filterKey ?: string ;
8188 } [ ] ;
8289 groupColumnName ?: string ;
8390 // Returns the row click destination + a hover-hint description. When
@@ -105,11 +112,14 @@ export const Table = ({
105112 // Extra classes for the scroll container, e.g. `effect-pulse` while the
106113 // tile is refreshing.
107114 className ?: string ;
115+ onCellFilter ?: CellFilterHandler ;
108116} ) => {
109117 const brandName = useBrandDisplayName ( ) ;
110118 const MIN_COLUMN_WIDTH_PX = 100 ;
111119 //we need a reference to the scrolling element for logic down below
112- const tableContainerRef = useRef < HTMLDivElement > ( null ) ;
120+ const [ tableContainer , setTableContainer ] = useState < HTMLDivElement | null > (
121+ null ,
122+ ) ;
113123
114124 const truncatedData = useMemo ( ( ) => {
115125 if ( data . length > MAX_TABLE_ROWS ) {
@@ -119,7 +129,7 @@ export const Table = ({
119129 } , [ data ] ) ;
120130 const isTruncated = truncatedData . length === MAX_TABLE_ROWS ;
121131
122- const tableWidth = tableContainerRef . current ?. clientWidth ;
132+ const tableWidth = tableContainer ?. clientWidth ;
123133 const numColumns = columns . filter ( c => c . visible !== false ) . length + 1 ;
124134 const dataColumnsWidthPerc = columns
125135 . filter ( c => c . visible !== false )
@@ -163,6 +173,7 @@ export const Table = ({
163173 sortingFn,
164174 color,
165175 colorRules,
176+ filterKey,
166177 } ,
167178 i ,
168179 ) =>
@@ -189,6 +200,29 @@ export const Table = ({
189200 formattedValue = formatNumber ( value , numberFormat ) ;
190201 }
191202
203+ // Map / Array values render as JSON, which their `toString()`
204+ // in a filter condition wouldn't match.
205+ const isAdhocFilterable =
206+ filterKey &&
207+ onCellFilter &&
208+ value != null &&
209+ typeof value !== 'object' ;
210+
211+ const content : React . ReactNode = isAdhocFilterable ? (
212+ < DBRowTableFieldWithPopover
213+ cellValue = { value }
214+ columnName = { filterKey }
215+ onPropertyAddClick = { onCellFilter }
216+ wrapLinesEnabled = { wrapLinesEnabled }
217+ tableContainerRef = { tableContainer }
218+ inline
219+ >
220+ { formattedValue }
221+ </ DBRowTableFieldWithPopover >
222+ ) : (
223+ formattedValue
224+ ) ;
225+
192226 // Resolve this cell's color from the column config: ordered
193227 // rules first (last match wins), then the column's static
194228 // color, else no override. ClickHouse serializes numeric
@@ -257,7 +291,7 @@ export const Table = ({
257291 data-testid = "dashboard-table-row-action"
258292 data-shape = "external-link"
259293 >
260- { formattedValue }
294+ { content }
261295 </ a >
262296 ) ;
263297 }
@@ -273,7 +307,7 @@ export const Table = ({
273307 data-testid = "dashboard-table-row-action"
274308 data-shape = "link"
275309 >
276- { formattedValue }
310+ { content }
277311 </ Link >
278312 ) ;
279313 }
@@ -295,7 +329,7 @@ export const Table = ({
295329 data-testid = "dashboard-table-row-action"
296330 data-shape = "button"
297331 >
298- { formattedValue }
332+ { content }
299333 </ button >
300334 ) ;
301335 }
@@ -312,15 +346,15 @@ export const Table = ({
312346 data-testid = "dashboard-table-row-action"
313347 data-shape = "link"
314348 >
315- { formattedValue }
349+ { content }
316350 </ Link >
317351 ) ;
318352 }
319353 }
320354
321355 return (
322356 < div className = { className } style = { colorStyle } >
323- { formattedValue }
357+ { content }
324358 </ div >
325359 ) ;
326360 } ,
@@ -376,7 +410,7 @@ export const Table = ({
376410
377411 const rowVirtualizer = useVirtualizer ( {
378412 count : rows . length ,
379- getScrollElement : ( ) => tableContainerRef . current ,
413+ getScrollElement : ( ) => tableContainer ,
380414 estimateSize : useCallback ( ( ) => 58 , [ ] ) ,
381415 overscan : 10 ,
382416 paddingEnd : 20 ,
@@ -409,7 +443,7 @@ export const Table = ({
409443 return (
410444 < div
411445 className = { cx ( 'overflow-auto h-100' , styles . tableWrapper , className ) }
412- ref = { tableContainerRef }
446+ ref = { setTableContainer }
413447 >
414448 < table
415449 className = "w-100"
0 commit comments