Skip to content

Commit 651371d

Browse files
committed
[PANA-6072] add composedPathSelector to click actions target behind FF
1 parent ad6fed8 commit 651371d

File tree

10 files changed

+532
-5
lines changed

10 files changed

+532
-5
lines changed

packages/core/src/tools/experimentalFeatures.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export enum ExperimentalFeature {
2424
SOURCE_CODE_CONTEXT = 'source_code_context',
2525
LCP_SUBPARTS = 'lcp_subparts',
2626
INP_SUBPARTS = 'inp_subparts',
27+
COMPOSED_PATH_SELECTOR = 'composed_path_selector',
2728
}
2829

2930
const enabledExperimentalFeatures: Set<ExperimentalFeature> = new Set()

packages/rum-core/src/domain/action/trackClickActions.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ describe('trackClickActions', () => {
135135
selector: '#button',
136136
width: 100,
137137
height: 100,
138+
composed_path_selector: undefined,
138139
},
139140
position: { x: 50, y: 50 },
140141
events: [domEvent],
@@ -698,6 +699,24 @@ describe('trackClickActions', () => {
698699
expect(events[0].target?.selector).not.toContain(SHADOW_DOM_MARKER)
699700
})
700701
})
702+
703+
describe('when composed path selector is enabled', () => {
704+
it('should return a composed_path_selector', () => {
705+
addExperimentalFeatures([ExperimentalFeature.COMPOSED_PATH_SELECTOR])
706+
startClickActionsTracking()
707+
emulateClick({
708+
target: button,
709+
activity: {},
710+
eventProperty: {
711+
composed: true,
712+
composedPath: () => [button, document.body, document],
713+
},
714+
})
715+
716+
clock.tick(EXPIRE_DELAY)
717+
expect(events[0].target?.composed_path_selector).toBeDefined()
718+
})
719+
})
701720
})
702721

703722
describe('finalizeClicks', () => {

packages/rum-core/src/domain/action/trackClickActions.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import type { Duration, ClocksState, TimeStamp } from '@datadog/browser-core'
2-
import { timeStampNow, Observable, timeStampToClocks, relativeToClocks, generateUUID } from '@datadog/browser-core'
2+
import {
3+
timeStampNow,
4+
Observable,
5+
timeStampToClocks,
6+
relativeToClocks,
7+
generateUUID,
8+
isExperimentalFeatureEnabled,
9+
ExperimentalFeature,
10+
} from '@datadog/browser-core'
311
import { isNodeShadowHost } from '../../browser/htmlDomUtils'
412
import type { FrustrationType } from '../../rawRumEvent.types'
513
import { ActionType } from '../../rawRumEvent.types'
@@ -13,6 +21,7 @@ import type { RumConfiguration } from '../configuration'
1321
import type { RumMutationRecord } from '../../browser/domMutationObservable'
1422
import { startEventTracker } from '../eventTracker'
1523
import type { StoppedEvent, DiscardedEvent, EventTracker } from '../eventTracker'
24+
import { getComposedPathSelector } from '../getComposedPathSelector'
1625
import type { ClickChain } from './clickChain'
1726
import { createClickChain } from './clickChain'
1827
import { getActionNameFromElement } from './getActionNameFromElement'
@@ -36,6 +45,7 @@ export interface ClickAction {
3645
nameSource: ActionNameSource
3746
target?: {
3847
selector: string | undefined
48+
composed_path_selector?: string
3949
width: number
4050
height: number
4151
}
@@ -236,6 +246,10 @@ function computeClickActionBase(
236246
const rect = target.getBoundingClientRect()
237247
const selector = getSelectorFromElement(target, configuration.actionNameAttribute)
238248

249+
const composedPathSelector = isExperimentalFeatureEnabled(ExperimentalFeature.COMPOSED_PATH_SELECTOR) && typeof event.composedPath === 'function'
250+
? getComposedPathSelector(event.composedPath(), configuration.actionNameAttribute, configuration.allowedHtmlAttributes || [])
251+
: undefined
252+
239253
if (selector) {
240254
updateInteractionSelector(event.timeStamp, selector)
241255
}
@@ -248,6 +262,7 @@ function computeClickActionBase(
248262
width: Math.round(rect.width),
249263
height: Math.round(rect.height),
250264
selector,
265+
composed_path_selector: composedPathSelector ?? undefined,
251266
},
252267
position: {
253268
// Use clientX and Y because for SVG element offsetX and Y are relatives to the <svg> element

packages/rum-core/src/domain/configuration/configuration.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@ describe('serializeRumConfiguration', () => {
631631
profilingSampleRate: 42,
632632
propagateTraceBaggage: true,
633633
betaTrackActionsInShadowDom: true,
634+
allowedHtmlAttributes: ['data-testid'],
634635
}
635636

636637
type MapRumInitConfigurationKey<Key extends string> = Key extends keyof InitConfiguration
@@ -641,6 +642,7 @@ describe('serializeRumConfiguration', () => {
641642
| 'excludedActivityUrls'
642643
| 'remoteConfigurationProxy'
643644
| 'allowedGraphQlUrls'
645+
| 'allowedHtmlAttributes'
644646
? `use_${CamelToSnakeCase<Key>}`
645647
: Key extends 'trackLongTasks'
646648
? 'track_long_task' // We forgot the s, keeping this for backward compatibility
@@ -687,6 +689,7 @@ describe('serializeRumConfiguration', () => {
687689
remote_configuration_id: '123',
688690
use_remote_configuration_proxy: true,
689691
profiling_sample_rate: 42,
692+
use_allowed_html_attributes: true,
690693
})
691694
})
692695
})

packages/rum-core/src/domain/configuration/configuration.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,15 @@ export interface RumInitConfiguration extends InitConfiguration {
280280
* @category Data Collection
281281
*/
282282
allowedGraphQlUrls?: Array<MatchOption | GraphQlUrlOption> | undefined
283+
284+
/**
285+
* A list of HTML attributes allowed to be used in the action selector collection.
286+
* Matches attributes against the event target and its ancestors.
287+
* If not provided, the SDK will use a default list of HTML attributes.
288+
*
289+
* @category Data Collection
290+
*/
291+
allowedHtmlAttributes?: MatchOption[] | undefined
283292
}
284293

285294
export type HybridInitConfiguration = Omit<RumInitConfiguration, 'applicationId' | 'clientToken'>
@@ -329,6 +338,7 @@ export interface RumConfiguration extends Configuration {
329338
profilingSampleRate: number
330339
propagateTraceBaggage: boolean
331340
allowedGraphQlUrls: GraphQlUrlOption[]
341+
allowedHtmlAttributes?: MatchOption[]
332342
}
333343

334344
export function validateAndBuildRumConfiguration(
@@ -408,6 +418,9 @@ export function validateAndBuildRumConfiguration(
408418
profilingSampleRate: initConfiguration.profilingSampleRate ?? 0,
409419
propagateTraceBaggage: !!initConfiguration.propagateTraceBaggage,
410420
allowedGraphQlUrls,
421+
allowedHtmlAttributes: Array.isArray(initConfiguration.allowedHtmlAttributes)
422+
? initConfiguration.allowedHtmlAttributes.filter(isMatchOption)
423+
: [],
411424
...baseConfiguration,
412425
}
413426
}
@@ -556,6 +569,7 @@ export function serializeRumConfiguration(configuration: RumInitConfiguration) {
556569
remote_configuration_id: configuration.remoteConfigurationId,
557570
profiling_sample_rate: configuration.profilingSampleRate,
558571
use_remote_configuration_proxy: !!configuration.remoteConfigurationProxy,
572+
use_allowed_html_attributes: isNonEmptyArray(configuration.allowedHtmlAttributes),
559573
...baseSerializedConfiguration,
560574
} satisfies RawTelemetryConfiguration
561575
}

0 commit comments

Comments
 (0)