@@ -34,7 +34,10 @@ import {
3434 Granularity ,
3535 isTimeSeriesDisplayType ,
3636} from '@hyperdx/common-utils/dist/core/utils' ;
37- import { getBlockingRequiredFilterNames } from '@hyperdx/common-utils/dist/dashboardFilterValues' ;
37+ import {
38+ getAdhocFilterTargetsForTile ,
39+ getBlockingRequiredFilterNames ,
40+ } from '@hyperdx/common-utils/dist/dashboardFilterValues' ;
3841import {
3942 getPromqlFilters ,
4043 getSqlFilters ,
@@ -143,6 +146,7 @@ import { DashboardKioskHeader } from '@/components/DashboardKioskHeader';
143146import DashboardTableOfContents from '@/components/DashboardTableOfContents' ;
144147import EditTimeChartForm from '@/components/DBEditTimeChartForm' ;
145148import DBNumberChart from '@/components/DBNumberChart' ;
149+ import type { RowSidePanelContextProps } from '@/components/DBRowSidePanel' ;
146150import DBTableChart from '@/components/DBTableChart' ;
147151import { DBTimeChart } from '@/components/DBTimeChart' ;
148152import { FavoriteButton } from '@/components/FavoriteButton' ;
@@ -172,6 +176,7 @@ import useDashboardContainers, {
172176import { useDashboardKioskMode } from '@/hooks/useDashboardKioskMode' ;
173177import { useReleaseAnnotations } from '@/hooks/useReleaseAnnotations' ;
174178import { QueryAttributionProvider } from '@/queryAttribution' ;
179+ import type { TileFilterHandler } from '@/types' ;
175180import { calculateNextTilePosition , makeId } from '@/utils/tilePositioning' ;
176181
177182import ChartContainer , {
@@ -450,6 +455,7 @@ const Tile = ({
450455 showReleaseAnnotations,
451456 isLive,
452457 readOnly,
458+ onFilter,
453459
454460 // Properties forwarded by grid layout
455461 className,
@@ -485,6 +491,8 @@ const Tile = ({
485491 showReleaseAnnotations ?: boolean ;
486492 isLive ?: boolean ;
487493 readOnly ?: boolean ;
494+ /** Set when an ad hoc filter broadcasts to this tile; clicked values update it. */
495+ onFilter ?: TileFilterHandler ;
488496
489497 // Properties forwarded by grid layout
490498 className ?: string ;
@@ -662,6 +670,22 @@ const Tile = ({
662670 ( ) => ( filters ? getSqlFilters ( filters ) : undefined ) ,
663671 [ filters ] ,
664672 ) ;
673+
674+ // `source` keeps the handler off rows from other sources, e.g. a linked trace
675+ const searchRowContext = useMemo < RowSidePanelContextProps | undefined > (
676+ ( ) =>
677+ onFilter && source && ( isLogSource ( source ) || isTraceSource ( source ) )
678+ ? {
679+ onPropertyAddClick : ( key , value , action ) =>
680+ onFilter (
681+ [ { key, value : String ( value ) } ] ,
682+ action === 'exclude' ? 'exclude' : 'include' ,
683+ ) ,
684+ source,
685+ }
686+ : undefined ,
687+ [ onFilter , source ] ,
688+ ) ;
665689 const promqlFilters = useMemo (
666690 ( ) => ( filters ? getPromqlFilters ( filters ) : undefined ) ,
667691 [ filters ] ,
@@ -1475,6 +1499,8 @@ const Tile = ({
14751499 queryKeyPrefix = { 'search' }
14761500 variant = "default"
14771501 errorVariant = "collapsible"
1502+ context = { searchRowContext }
1503+ filterOnAliasedExpressions
14781504 />
14791505 </ ChartContainer >
14801506 ) }
@@ -1556,6 +1582,7 @@ const Tile = ({
15561582 readOnly ,
15571583 tileVariables ,
15581584 variableNames ,
1585+ searchRowContext ,
15591586 ] ,
15601587 ) ;
15611588
@@ -2006,6 +2033,7 @@ function DBDashboardPage({
20062033 setFilterValue,
20072034 adhocConditionsByFilterId,
20082035 setAdhocConditions,
2036+ applyAdhocAction,
20092037 setFilterValueEntries,
20102038 filterValueEntries,
20112039 ignoredFilterExpressions,
@@ -2467,6 +2495,25 @@ function DBDashboardPage({
24672495 [ where , whereLanguage , getFilterQueriesForSource ] ,
24682496 ) ;
24692497
2498+ const getTileFilterHandler = useCallback (
2499+ (
2500+ chart : Tile ,
2501+ sourceId : string | undefined ,
2502+ ) : TileFilterHandler | undefined => {
2503+ // The filter bar is hidden in kiosk mode, so a click would add conditions
2504+ // the viewer can't see or remove.
2505+ if ( isKioskMode ) return undefined ;
2506+ const targetIds = getAdhocFilterTargetsForTile (
2507+ filters ,
2508+ chart . config ,
2509+ sourceId ,
2510+ ) . map ( filter => filter . id ) ;
2511+ if ( targetIds . length === 0 ) return undefined ;
2512+ return ( entries , action ) => applyAdhocAction ( targetIds , entries , action ) ;
2513+ } ,
2514+ [ isKioskMode , filters , applyAdhocAction ] ,
2515+ ) ;
2516+
24702517 const renderTileComponent = useCallback (
24712518 ( chart : Tile ) => {
24722519 // Resolve the tile's source ID so per-source-scoped filters can be
@@ -2486,6 +2533,7 @@ function DBDashboardPage({
24862533 isRefreshEnabled ? granularityOverride : ( granularity ?? undefined )
24872534 }
24882535 filters = { getTileFilters ( tileSourceId ) }
2536+ onFilter = { getTileFilterHandler ( chart , tileSourceId ) }
24892537 variables = { variables }
24902538 unsatisfiedRequiredFilters = { unsatisfiedRequiredFilters }
24912539 onTimeRangeSelect = { onTimeRangeSelect }
@@ -2588,6 +2636,7 @@ function DBDashboardPage({
25882636 showAlertAnnotations ,
25892637 showReleaseAnnotations ,
25902638 getTileFilters ,
2639+ getTileFilterHandler ,
25912640 variables ,
25922641 unsatisfiedRequiredFilters ,
25932642 moveTargetContainers ,
0 commit comments