@@ -361,16 +361,33 @@ export function extractPoints(
361361 const trace = getTraceSource ( point ) ;
362362
363363 // FunnelArea: sector-based chart with no x/y coordinates.
364- // Pick funnel-area-specific keys directly; hovertemplate parsing does not
365- // apply since there are no axis values to substitute .
364+ // Pick funnel-area-specific keys, then merge any hovertemplate-parsed
365+ // fields (e.g. customdata columns) so user-defined fields are preserved .
366366 if ( trace . type === "funnelarea" ) {
367- return pick ( point , FUNNEL_AREA_DATA_KEYS ) ;
367+ const base = pick ( point , FUNNEL_AREA_DATA_KEYS ) ;
368+ const ht = Array . isArray ( trace . hovertemplate )
369+ ? trace . hovertemplate [ 0 ]
370+ : trace . hovertemplate ;
371+ if ( ! ht ) {
372+ return base ;
373+ }
374+ parser = parser ? parser . update ( ht ) : createParser ( ht ) ;
375+ return { ...base , ...parser . parse ( point ) } ;
368376 }
369377
370378 // Funnel: bar-like chart with x/y plus per-stage percent metrics.
371- // Return all funnel-specific keys so callers get percentInitial et al.
379+ // Pick funnel-specific keys, then merge hovertemplate-parsed fields so
380+ // callers get both percentInitial et al. and any user-defined columns.
372381 if ( trace . type === "funnel" ) {
373- return pick ( point , FUNNEL_DATA_KEYS ) ;
382+ const base = pick ( point , FUNNEL_DATA_KEYS ) ;
383+ const ht = Array . isArray ( trace . hovertemplate )
384+ ? trace . hovertemplate [ 0 ]
385+ : trace . hovertemplate ;
386+ if ( ! ht ) {
387+ return base ;
388+ }
389+ parser = parser ? parser . update ( ht ) : createParser ( ht ) ;
390+ return { ...base , ...parser . parse ( point ) } ;
374391 }
375392
376393 const standardPointFields = withInferredXY (
0 commit comments