Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/true-chairs-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'posthog-js': patch
---

move tour event names to constants
163 changes: 82 additions & 81 deletions packages/browser/src/extensions/product-tours/product-tours.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import {
ProductTourBannerConfig,
ProductTourCallback,
ProductTourDismissReason,
ProductTourEventName,
ProductTourEventProperties,
ProductTourRenderReason,
ProductTourStepButton,
ShowTourOptions,
} from '../../posthog-product-tours-types'
import { SurveyEventName, SurveyEventProperties } from '../../posthog-surveys-types'
import {
addProductTourCSSVariablesToElement,
ElementFindResult,
findStepElement,
getElementMetadata,
getProductTourStylesheet,
Expand Down Expand Up @@ -462,11 +465,11 @@ export class ProductTourManager {
const rendered = this._renderCurrentStep()

if (rendered) {
this._captureEvent('product tour shown', {
$product_tour_id: tour.id,
$product_tour_name: tour.name,
$product_tour_iteration: tour.current_iteration || 1,
$product_tour_render_reason: renderReason,
this._captureEvent(ProductTourEventName.SHOWN, {
[ProductTourEventProperties.TOUR_ID]: tour.id,
[ProductTourEventProperties.TOUR_NAME]: tour.name,
[ProductTourEventProperties.TOUR_ITERATION]: tour.current_iteration || 1,
[ProductTourEventProperties.TOUR_RENDER_REASON]: renderReason,
})

if (!this._isPreviewMode) {
Expand Down Expand Up @@ -514,10 +517,10 @@ export class ProductTourManager {

const currentStep = this._activeTour.steps[this._currentStepIndex]

this._captureEvent('product tour step completed', {
$product_tour_id: this._activeTour.id,
$product_tour_step_id: currentStep.id,
$product_tour_step_order: this._currentStepIndex,
this._captureEvent(ProductTourEventName.STEP_COMPLETED, {
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
[ProductTourEventProperties.TOUR_STEP_ID]: currentStep.id,
[ProductTourEventProperties.TOUR_STEP_ORDER]: this._currentStepIndex,
})

if (this._currentStepIndex < this._activeTour.steps.length - 1) {
Expand All @@ -544,11 +547,11 @@ export class ProductTourManager {

const currentStep = this._activeTour.steps[this._currentStepIndex]

this._captureEvent('product tour dismissed', {
$product_tour_id: this._activeTour.id,
$product_tour_step_id: currentStep.id,
$product_tour_step_order: this._currentStepIndex,
$product_tour_dismiss_reason: reason,
this._captureEvent(ProductTourEventName.DISMISSED, {
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
[ProductTourEventProperties.TOUR_STEP_ID]: currentStep.id,
[ProductTourEventProperties.TOUR_STEP_ORDER]: this._currentStepIndex,
[ProductTourEventProperties.TOUR_DISMISS_REASON]: reason,
})

if (!this._isPreviewMode) {
Expand All @@ -570,16 +573,16 @@ export class ProductTourManager {
if (this._activeTour) {
const currentStep = this._activeTour.steps[this._currentStepIndex]
if (currentStep) {
this._captureEvent('product tour button clicked', {
$product_tour_id: this._activeTour.id,
$product_tour_name: this._activeTour.name,
$product_tour_iteration: this._activeTour.current_iteration || 1,
$product_tour_step_id: currentStep.id,
$product_tour_step_order: this._currentStepIndex,
$product_tour_button_text: button.text,
$product_tour_button_action: button.action,
...(button.link && { $product_tour_button_link: button.link }),
...(button.tourId && { $product_tour_button_tour_id: button.tourId }),
this._captureEvent(ProductTourEventName.BUTTON_CLICKED, {
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
[ProductTourEventProperties.TOUR_NAME]: this._activeTour.name,
[ProductTourEventProperties.TOUR_ITERATION]: this._activeTour.current_iteration || 1,
[ProductTourEventProperties.TOUR_STEP_ID]: currentStep.id,
[ProductTourEventProperties.TOUR_STEP_ORDER]: this._currentStepIndex,
[ProductTourEventProperties.TOUR_BUTTON_TEXT]: button.text,
[ProductTourEventProperties.TOUR_BUTTON_ACTION]: button.action,
...(button.link && { [ProductTourEventProperties.TOUR_BUTTON_LINK]: button.link }),
...(button.tourId && { [ProductTourEventProperties.TOUR_BUTTON_TOUR_ID]: button.tourId }),
})
}
}
Expand Down Expand Up @@ -611,9 +614,9 @@ export class ProductTourManager {
return
}

this._captureEvent('product tour completed', {
$product_tour_id: this._activeTour.id,
$product_tour_steps_count: this._activeTour.steps.length,
this._captureEvent(ProductTourEventName.COMPLETED, {
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
[ProductTourEventProperties.TOUR_STEPS_COUNT]: this._activeTour.steps.length,
})

if (!this._isPreviewMode) {
Expand Down Expand Up @@ -645,12 +648,7 @@ export class ProductTourManager {

// Banner step - render full-width banner
if (step.type === 'banner') {
this._captureEvent('product tour step shown', {
$product_tour_id: this._activeTour.id,
$product_tour_step_id: step.id,
$product_tour_step_order: this._currentStepIndex,
$product_tour_step_type: 'banner',
})
this._captureStepShown()

this._isResuming = false
this._renderBanner()
Expand All @@ -670,12 +668,7 @@ export class ProductTourManager {

// Screen-positioned step (no element targeting) - render without a target element
if (!hasElementTarget(step)) {
this._captureEvent('product tour step shown', {
$product_tour_id: this._activeTour.id,
$product_tour_step_id: step.id,
$product_tour_step_order: this._currentStepIndex,
$product_tour_step_type: step.type,
})
this._captureStepShown()

this._isResuming = false
this._renderTooltipWithPreact(null)
Expand All @@ -685,8 +678,8 @@ export class ProductTourManager {
const result = findStepElement(step)

const inferenceProps = {
$use_manual_selector: step.useManualSelector ?? false,
$inference_data_present: !!step.inferenceData,
[ProductTourEventProperties.USE_MANUAL_SELECTOR]: step.useManualSelector ?? false,
[ProductTourEventProperties.INFERENCE_DATA_PRESENT]: !!step.inferenceData,
}

const previousStep = this._currentStepIndex > 0 ? this._activeTour.steps[this._currentStepIndex - 1] : null
Expand All @@ -709,16 +702,9 @@ export class ProductTourManager {

const waitDurationMs = retryCount * retryTimeout

this._captureEvent('product tour step selector failed', {
$product_tour_id: this._activeTour.id,
$product_tour_step_id: step.id,
$product_tour_step_order: this._currentStepIndex,
$product_tour_step_selector: step.selector,
$product_tour_error: result.error,
$product_tour_matches_count: result.matchCount,
$product_tour_failure_phase: 'runtime',
$product_tour_waited_for_element: shouldWaitForElement,
$product_tour_wait_duration_ms: waitDurationMs,
this._captureStepSelectorFailed(result, {
[ProductTourEventProperties.TOUR_WAITED_FOR_ELEMENT]: shouldWaitForElement,
[ProductTourEventProperties.TOUR_WAIT_DURATION_MS]: waitDurationMs,
...inferenceProps,
})

Expand All @@ -738,16 +724,7 @@ export class ProductTourManager {
}

if (result.error === 'multiple_matches') {
this._captureEvent('product tour step selector failed', {
$product_tour_id: this._activeTour.id,
$product_tour_step_id: step.id,
$product_tour_step_order: this._currentStepIndex,
$product_tour_step_selector: step.selector,
$product_tour_error: result.error,
$product_tour_matches_count: result.matchCount,
$product_tour_failure_phase: 'runtime',
...inferenceProps,
})
this._captureStepSelectorFailed(result, inferenceProps)
// Continue with first match for multiple_matches case
}

Expand All @@ -758,16 +735,13 @@ export class ProductTourManager {
const element = result.element
const metadata = getElementMetadata(element)

this._captureEvent('product tour step shown', {
$product_tour_id: this._activeTour.id,
$product_tour_step_id: step.id,
$product_tour_step_order: this._currentStepIndex,
$product_tour_step_selector: step.selector,
$product_tour_step_selector_found: true,
$product_tour_step_element_tag: metadata.tag,
$product_tour_step_element_id: metadata.id,
$product_tour_step_element_classes: metadata.classes,
$product_tour_step_element_text: metadata.text,
this._captureStepShown({
[ProductTourEventProperties.TOUR_STEP_SELECTOR]: step.selector,
[ProductTourEventProperties.TOUR_STEP_SELECTOR_FOUND]: true,
[ProductTourEventProperties.TOUR_STEP_ELEMENT_TAG]: metadata.tag,
[ProductTourEventProperties.TOUR_STEP_ELEMENT_ID]: metadata.id,
[ProductTourEventProperties.TOUR_STEP_ELEMENT_CLASSES]: metadata.classes,
[ProductTourEventProperties.TOUR_STEP_ELEMENT_TEXT]: metadata.text,
...inferenceProps,
})

Expand Down Expand Up @@ -814,9 +788,9 @@ export class ProductTourManager {
const result = retrieveBannerShadow(this._activeTour, step.bannerConfig)

if (!result) {
this._captureEvent('product tour banner container selector failed', {
$product_tour_id: this._activeTour.id,
$product_tour_banner_selector: step?.bannerConfig?.selector,
this._captureEvent(ProductTourEventName.BANNER_CONTAINER_SELECTOR_FAILED, {
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
[ProductTourEventProperties.TOUR_BANNER_SELECTOR]: step?.bannerConfig?.selector,
})
this.dismissTour('container_unavailable')
return
Expand Down Expand Up @@ -854,12 +828,8 @@ export class ProductTourManager {
const questionId = step.linkedSurveyQuestionId
const questionText = step.survey?.questionText || ''

this._captureEvent('product tour step shown', {
$product_tour_id: this._activeTour.id,
$product_tour_step_id: step.id,
$product_tour_step_order: this._currentStepIndex,
$product_tour_step_type: 'survey',
$product_tour_linked_survey_id: surveyId,
this._captureStepShown({
[ProductTourEventProperties.TOUR_LINKED_SURVEY_ID]: surveyId,
})

this._captureEvent(SurveyEventName.SHOWN, {
Expand Down Expand Up @@ -1015,13 +985,44 @@ export class ProductTourManager {
})
}

private _captureEvent(eventName: string, properties: Record<string, any>): void {
private _captureEvent(eventName: ProductTourEventName | SurveyEventName, properties: Record<string, any>): void {
if (this._isPreviewMode) {
return
}
this._instance.capture(eventName, properties)
}

private _captureStepShown(extraProps?: Record<string, any>): void {
if (!this._activeTour) {
return
}
const step = this._activeTour.steps[this._currentStepIndex]
this._captureEvent(ProductTourEventName.STEP_SHOWN, {
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
[ProductTourEventProperties.TOUR_STEP_ID]: step.id,
[ProductTourEventProperties.TOUR_STEP_ORDER]: this._currentStepIndex,
[ProductTourEventProperties.TOUR_STEP_TYPE]: step.type,
...extraProps,
})
}

private _captureStepSelectorFailed(result: ElementFindResult, extraProps?: Record<string, any>): void {
if (!this._activeTour) {
return
}
const step = this._activeTour.steps[this._currentStepIndex]
this._captureEvent(ProductTourEventName.STEP_SELECTOR_FAILED, {
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
[ProductTourEventProperties.TOUR_STEP_ID]: step.id,
[ProductTourEventProperties.TOUR_STEP_ORDER]: this._currentStepIndex,
[ProductTourEventProperties.TOUR_STEP_SELECTOR]: step.selector,
[ProductTourEventProperties.TOUR_ERROR]: result.error,
[ProductTourEventProperties.TOUR_MATCHES_COUNT]: result.matchCount,
[ProductTourEventProperties.TOUR_FAILURE_PHASE]: 'runtime',
...extraProps,
})
}

// Public API methods delegated from PostHogProductTours
getActiveProductTours(callback: ProductTourCallback): void {
this._instance.productTours?.getProductTours((tours, context) => {
Expand Down
42 changes: 42 additions & 0 deletions packages/browser/src/posthog-product-tours-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,45 @@ export interface ShowTourOptions {
reason?: ProductTourRenderReason
enableStrictValidation?: boolean
}

export enum ProductTourEventName {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i had a colleague at amazon who sent this link to me all the time, i suppose it never stuck and it has come back to haunt me 😆

SHOWN = 'product tour shown',
DISMISSED = 'product tour dismissed',
COMPLETED = 'product tour completed',
STEP_SHOWN = 'product tour step shown',
STEP_COMPLETED = 'product tour step completed',
BUTTON_CLICKED = 'product tour button clicked',
STEP_SELECTOR_FAILED = 'product tour step selector failed',
BANNER_CONTAINER_SELECTOR_FAILED = 'product tour banner container selector failed',
}

export enum ProductTourEventProperties {
TOUR_ID = '$product_tour_id',
TOUR_NAME = '$product_tour_name',
TOUR_ITERATION = '$product_tour_iteration',
TOUR_RENDER_REASON = '$product_tour_render_reason',
TOUR_STEP_ID = '$product_tour_step_id',
TOUR_STEP_ORDER = '$product_tour_step_order',
TOUR_STEP_TYPE = '$product_tour_step_type',
TOUR_DISMISS_REASON = '$product_tour_dismiss_reason',
TOUR_BUTTON_TEXT = '$product_tour_button_text',
TOUR_BUTTON_ACTION = '$product_tour_button_action',
TOUR_BUTTON_LINK = '$product_tour_button_link',
TOUR_BUTTON_TOUR_ID = '$product_tour_button_tour_id',
TOUR_STEPS_COUNT = '$product_tour_steps_count',
TOUR_STEP_SELECTOR = '$product_tour_step_selector',
TOUR_STEP_SELECTOR_FOUND = '$product_tour_step_selector_found',
TOUR_STEP_ELEMENT_TAG = '$product_tour_step_element_tag',
TOUR_STEP_ELEMENT_ID = '$product_tour_step_element_id',
TOUR_STEP_ELEMENT_CLASSES = '$product_tour_step_element_classes',
TOUR_STEP_ELEMENT_TEXT = '$product_tour_step_element_text',
TOUR_ERROR = '$product_tour_error',
TOUR_MATCHES_COUNT = '$product_tour_matches_count',
TOUR_FAILURE_PHASE = '$product_tour_failure_phase',
TOUR_WAITED_FOR_ELEMENT = '$product_tour_waited_for_element',
TOUR_WAIT_DURATION_MS = '$product_tour_wait_duration_ms',
TOUR_BANNER_SELECTOR = '$product_tour_banner_selector',
TOUR_LINKED_SURVEY_ID = '$product_tour_linked_survey_id',
USE_MANUAL_SELECTOR = '$use_manual_selector',
INFERENCE_DATA_PRESENT = '$inference_data_present',
}
4 changes: 2 additions & 2 deletions packages/browser/src/utils/product-tour-event-receiver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PRODUCT_TOURS_ACTIVATED } from '../constants'
import { ProductTour } from '../posthog-product-tours-types'
import { ProductTour, ProductTourEventName } from '../posthog-product-tours-types'
import { PostHog } from '../posthog-core'
import { EventReceiver } from './event-receiver'
import { createLogger } from './logger'
Expand All @@ -18,7 +18,7 @@ export class ProductTourEventReceiver extends EventReceiver<ProductTour> {
}

protected _getShownEventName(): string {
return 'product tour shown'
return ProductTourEventName.SHOWN
}

protected _getItems(callback: (items: ProductTour[]) => void): void {
Expand Down
2 changes: 2 additions & 0 deletions packages/browser/terser-mangled-names.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
"_capturePageview",
"_captureSnapshot",
"_captureSnapshotBuffered",
"_captureStepSelectorFailed",
"_captureStepShown",
"_checkAction",
"_checkClickTimer",
"_checkClicks",
Expand Down
Loading