Skip to content

Commit d36a6ed

Browse files
authored
chore(product tours): move tour event names to constants (#3081)
## Problem magic strings and event definitions everywhere! <!-- Who are we building for, what are their needs, why is this important? --> ## Changes moving tour event names / prop names to constants and helper fns <!-- What is changed and what information would be useful to a reviewer? --> ## Release info Sub-libraries affected ### Libraries affected <!-- Please mark which libraries will require a version bump. --> - [ ] All of them - [x] posthog-js (web) - [ ] posthog-js-lite (web lite) - [ ] posthog-node - [ ] posthog-react-native - [ ] @posthog/react - [ ] @posthog/ai - [ ] @posthog/nextjs-config - [ ] @posthog/nuxt - [ ] @posthog/rollup-plugin - [ ] @posthog/webpack-plugin - [ ] @posthog/types ## Checklist - [ ] Tests for new code - [x] Accounted for the impact of any changes across different platforms - [x] Accounted for backwards compatibility of any changes (no breaking changes!) - [x] Took care not to unnecessarily increase the bundle size ### If releasing new changes - [x] Ran `pnpm changeset` to generate a changeset file - [x] Added the "release" label to the PR to indicate we're publishing new versions for the affected packages <!-- For more details check RELEASING.md -->
1 parent 25ea963 commit d36a6ed

File tree

5 files changed

+133
-83
lines changed

5 files changed

+133
-83
lines changed

.changeset/true-chairs-grow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'posthog-js': patch
3+
---
4+
5+
move tour event names to constants

packages/browser/src/extensions/product-tours/product-tours.tsx

Lines changed: 82 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ import {
55
ProductTourBannerConfig,
66
ProductTourCallback,
77
ProductTourDismissReason,
8+
ProductTourEventName,
9+
ProductTourEventProperties,
810
ProductTourRenderReason,
911
ProductTourStepButton,
1012
ShowTourOptions,
1113
} from '../../posthog-product-tours-types'
1214
import { SurveyEventName, SurveyEventProperties } from '../../posthog-surveys-types'
1315
import {
1416
addProductTourCSSVariablesToElement,
17+
ElementFindResult,
1518
findStepElement,
1619
getElementMetadata,
1720
getProductTourStylesheet,
@@ -462,11 +465,11 @@ export class ProductTourManager {
462465
const rendered = this._renderCurrentStep()
463466

464467
if (rendered) {
465-
this._captureEvent('product tour shown', {
466-
$product_tour_id: tour.id,
467-
$product_tour_name: tour.name,
468-
$product_tour_iteration: tour.current_iteration || 1,
469-
$product_tour_render_reason: renderReason,
468+
this._captureEvent(ProductTourEventName.SHOWN, {
469+
[ProductTourEventProperties.TOUR_ID]: tour.id,
470+
[ProductTourEventProperties.TOUR_NAME]: tour.name,
471+
[ProductTourEventProperties.TOUR_ITERATION]: tour.current_iteration || 1,
472+
[ProductTourEventProperties.TOUR_RENDER_REASON]: renderReason,
470473
})
471474

472475
if (!this._isPreviewMode) {
@@ -514,10 +517,10 @@ export class ProductTourManager {
514517

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

517-
this._captureEvent('product tour step completed', {
518-
$product_tour_id: this._activeTour.id,
519-
$product_tour_step_id: currentStep.id,
520-
$product_tour_step_order: this._currentStepIndex,
520+
this._captureEvent(ProductTourEventName.STEP_COMPLETED, {
521+
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
522+
[ProductTourEventProperties.TOUR_STEP_ID]: currentStep.id,
523+
[ProductTourEventProperties.TOUR_STEP_ORDER]: this._currentStepIndex,
521524
})
522525

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

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

547-
this._captureEvent('product tour dismissed', {
548-
$product_tour_id: this._activeTour.id,
549-
$product_tour_step_id: currentStep.id,
550-
$product_tour_step_order: this._currentStepIndex,
551-
$product_tour_dismiss_reason: reason,
550+
this._captureEvent(ProductTourEventName.DISMISSED, {
551+
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
552+
[ProductTourEventProperties.TOUR_STEP_ID]: currentStep.id,
553+
[ProductTourEventProperties.TOUR_STEP_ORDER]: this._currentStepIndex,
554+
[ProductTourEventProperties.TOUR_DISMISS_REASON]: reason,
552555
})
553556

554557
if (!this._isPreviewMode) {
@@ -570,16 +573,16 @@ export class ProductTourManager {
570573
if (this._activeTour) {
571574
const currentStep = this._activeTour.steps[this._currentStepIndex]
572575
if (currentStep) {
573-
this._captureEvent('product tour button clicked', {
574-
$product_tour_id: this._activeTour.id,
575-
$product_tour_name: this._activeTour.name,
576-
$product_tour_iteration: this._activeTour.current_iteration || 1,
577-
$product_tour_step_id: currentStep.id,
578-
$product_tour_step_order: this._currentStepIndex,
579-
$product_tour_button_text: button.text,
580-
$product_tour_button_action: button.action,
581-
...(button.link && { $product_tour_button_link: button.link }),
582-
...(button.tourId && { $product_tour_button_tour_id: button.tourId }),
576+
this._captureEvent(ProductTourEventName.BUTTON_CLICKED, {
577+
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
578+
[ProductTourEventProperties.TOUR_NAME]: this._activeTour.name,
579+
[ProductTourEventProperties.TOUR_ITERATION]: this._activeTour.current_iteration || 1,
580+
[ProductTourEventProperties.TOUR_STEP_ID]: currentStep.id,
581+
[ProductTourEventProperties.TOUR_STEP_ORDER]: this._currentStepIndex,
582+
[ProductTourEventProperties.TOUR_BUTTON_TEXT]: button.text,
583+
[ProductTourEventProperties.TOUR_BUTTON_ACTION]: button.action,
584+
...(button.link && { [ProductTourEventProperties.TOUR_BUTTON_LINK]: button.link }),
585+
...(button.tourId && { [ProductTourEventProperties.TOUR_BUTTON_TOUR_ID]: button.tourId }),
583586
})
584587
}
585588
}
@@ -611,9 +614,9 @@ export class ProductTourManager {
611614
return
612615
}
613616

614-
this._captureEvent('product tour completed', {
615-
$product_tour_id: this._activeTour.id,
616-
$product_tour_steps_count: this._activeTour.steps.length,
617+
this._captureEvent(ProductTourEventName.COMPLETED, {
618+
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
619+
[ProductTourEventProperties.TOUR_STEPS_COUNT]: this._activeTour.steps.length,
617620
})
618621

619622
if (!this._isPreviewMode) {
@@ -645,12 +648,7 @@ export class ProductTourManager {
645648

646649
// Banner step - render full-width banner
647650
if (step.type === 'banner') {
648-
this._captureEvent('product tour step shown', {
649-
$product_tour_id: this._activeTour.id,
650-
$product_tour_step_id: step.id,
651-
$product_tour_step_order: this._currentStepIndex,
652-
$product_tour_step_type: 'banner',
653-
})
651+
this._captureStepShown()
654652

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

671669
// Screen-positioned step (no element targeting) - render without a target element
672670
if (!hasElementTarget(step)) {
673-
this._captureEvent('product tour step shown', {
674-
$product_tour_id: this._activeTour.id,
675-
$product_tour_step_id: step.id,
676-
$product_tour_step_order: this._currentStepIndex,
677-
$product_tour_step_type: step.type,
678-
})
671+
this._captureStepShown()
679672

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

687680
const inferenceProps = {
688-
$use_manual_selector: step.useManualSelector ?? false,
689-
$inference_data_present: !!step.inferenceData,
681+
[ProductTourEventProperties.USE_MANUAL_SELECTOR]: step.useManualSelector ?? false,
682+
[ProductTourEventProperties.INFERENCE_DATA_PRESENT]: !!step.inferenceData,
690683
}
691684

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

710703
const waitDurationMs = retryCount * retryTimeout
711704

712-
this._captureEvent('product tour step selector failed', {
713-
$product_tour_id: this._activeTour.id,
714-
$product_tour_step_id: step.id,
715-
$product_tour_step_order: this._currentStepIndex,
716-
$product_tour_step_selector: step.selector,
717-
$product_tour_error: result.error,
718-
$product_tour_matches_count: result.matchCount,
719-
$product_tour_failure_phase: 'runtime',
720-
$product_tour_waited_for_element: shouldWaitForElement,
721-
$product_tour_wait_duration_ms: waitDurationMs,
705+
this._captureStepSelectorFailed(result, {
706+
[ProductTourEventProperties.TOUR_WAITED_FOR_ELEMENT]: shouldWaitForElement,
707+
[ProductTourEventProperties.TOUR_WAIT_DURATION_MS]: waitDurationMs,
722708
...inferenceProps,
723709
})
724710

@@ -738,16 +724,7 @@ export class ProductTourManager {
738724
}
739725

740726
if (result.error === 'multiple_matches') {
741-
this._captureEvent('product tour step selector failed', {
742-
$product_tour_id: this._activeTour.id,
743-
$product_tour_step_id: step.id,
744-
$product_tour_step_order: this._currentStepIndex,
745-
$product_tour_step_selector: step.selector,
746-
$product_tour_error: result.error,
747-
$product_tour_matches_count: result.matchCount,
748-
$product_tour_failure_phase: 'runtime',
749-
...inferenceProps,
750-
})
727+
this._captureStepSelectorFailed(result, inferenceProps)
751728
// Continue with first match for multiple_matches case
752729
}
753730

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

761-
this._captureEvent('product tour step shown', {
762-
$product_tour_id: this._activeTour.id,
763-
$product_tour_step_id: step.id,
764-
$product_tour_step_order: this._currentStepIndex,
765-
$product_tour_step_selector: step.selector,
766-
$product_tour_step_selector_found: true,
767-
$product_tour_step_element_tag: metadata.tag,
768-
$product_tour_step_element_id: metadata.id,
769-
$product_tour_step_element_classes: metadata.classes,
770-
$product_tour_step_element_text: metadata.text,
738+
this._captureStepShown({
739+
[ProductTourEventProperties.TOUR_STEP_SELECTOR]: step.selector,
740+
[ProductTourEventProperties.TOUR_STEP_SELECTOR_FOUND]: true,
741+
[ProductTourEventProperties.TOUR_STEP_ELEMENT_TAG]: metadata.tag,
742+
[ProductTourEventProperties.TOUR_STEP_ELEMENT_ID]: metadata.id,
743+
[ProductTourEventProperties.TOUR_STEP_ELEMENT_CLASSES]: metadata.classes,
744+
[ProductTourEventProperties.TOUR_STEP_ELEMENT_TEXT]: metadata.text,
771745
...inferenceProps,
772746
})
773747

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

816790
if (!result) {
817-
this._captureEvent('product tour banner container selector failed', {
818-
$product_tour_id: this._activeTour.id,
819-
$product_tour_banner_selector: step?.bannerConfig?.selector,
791+
this._captureEvent(ProductTourEventName.BANNER_CONTAINER_SELECTOR_FAILED, {
792+
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
793+
[ProductTourEventProperties.TOUR_BANNER_SELECTOR]: step?.bannerConfig?.selector,
820794
})
821795
this.dismissTour('container_unavailable')
822796
return
@@ -854,12 +828,8 @@ export class ProductTourManager {
854828
const questionId = step.linkedSurveyQuestionId
855829
const questionText = step.survey?.questionText || ''
856830

857-
this._captureEvent('product tour step shown', {
858-
$product_tour_id: this._activeTour.id,
859-
$product_tour_step_id: step.id,
860-
$product_tour_step_order: this._currentStepIndex,
861-
$product_tour_step_type: 'survey',
862-
$product_tour_linked_survey_id: surveyId,
831+
this._captureStepShown({
832+
[ProductTourEventProperties.TOUR_LINKED_SURVEY_ID]: surveyId,
863833
})
864834

865835
this._captureEvent(SurveyEventName.SHOWN, {
@@ -1015,13 +985,44 @@ export class ProductTourManager {
1015985
})
1016986
}
1017987

1018-
private _captureEvent(eventName: string, properties: Record<string, any>): void {
988+
private _captureEvent(eventName: ProductTourEventName | SurveyEventName, properties: Record<string, any>): void {
1019989
if (this._isPreviewMode) {
1020990
return
1021991
}
1022992
this._instance.capture(eventName, properties)
1023993
}
1024994

995+
private _captureStepShown(extraProps?: Record<string, any>): void {
996+
if (!this._activeTour) {
997+
return
998+
}
999+
const step = this._activeTour.steps[this._currentStepIndex]
1000+
this._captureEvent(ProductTourEventName.STEP_SHOWN, {
1001+
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
1002+
[ProductTourEventProperties.TOUR_STEP_ID]: step.id,
1003+
[ProductTourEventProperties.TOUR_STEP_ORDER]: this._currentStepIndex,
1004+
[ProductTourEventProperties.TOUR_STEP_TYPE]: step.type,
1005+
...extraProps,
1006+
})
1007+
}
1008+
1009+
private _captureStepSelectorFailed(result: ElementFindResult, extraProps?: Record<string, any>): void {
1010+
if (!this._activeTour) {
1011+
return
1012+
}
1013+
const step = this._activeTour.steps[this._currentStepIndex]
1014+
this._captureEvent(ProductTourEventName.STEP_SELECTOR_FAILED, {
1015+
[ProductTourEventProperties.TOUR_ID]: this._activeTour.id,
1016+
[ProductTourEventProperties.TOUR_STEP_ID]: step.id,
1017+
[ProductTourEventProperties.TOUR_STEP_ORDER]: this._currentStepIndex,
1018+
[ProductTourEventProperties.TOUR_STEP_SELECTOR]: step.selector,
1019+
[ProductTourEventProperties.TOUR_ERROR]: result.error,
1020+
[ProductTourEventProperties.TOUR_MATCHES_COUNT]: result.matchCount,
1021+
[ProductTourEventProperties.TOUR_FAILURE_PHASE]: 'runtime',
1022+
...extraProps,
1023+
})
1024+
}
1025+
10251026
// Public API methods delegated from PostHogProductTours
10261027
getActiveProductTours(callback: ProductTourCallback): void {
10271028
this._instance.productTours?.getProductTours((tours, context) => {

packages/browser/src/posthog-product-tours-types.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,45 @@ export interface ShowTourOptions {
169169
reason?: ProductTourRenderReason
170170
enableStrictValidation?: boolean
171171
}
172+
173+
export enum ProductTourEventName {
174+
SHOWN = 'product tour shown',
175+
DISMISSED = 'product tour dismissed',
176+
COMPLETED = 'product tour completed',
177+
STEP_SHOWN = 'product tour step shown',
178+
STEP_COMPLETED = 'product tour step completed',
179+
BUTTON_CLICKED = 'product tour button clicked',
180+
STEP_SELECTOR_FAILED = 'product tour step selector failed',
181+
BANNER_CONTAINER_SELECTOR_FAILED = 'product tour banner container selector failed',
182+
}
183+
184+
export enum ProductTourEventProperties {
185+
TOUR_ID = '$product_tour_id',
186+
TOUR_NAME = '$product_tour_name',
187+
TOUR_ITERATION = '$product_tour_iteration',
188+
TOUR_RENDER_REASON = '$product_tour_render_reason',
189+
TOUR_STEP_ID = '$product_tour_step_id',
190+
TOUR_STEP_ORDER = '$product_tour_step_order',
191+
TOUR_STEP_TYPE = '$product_tour_step_type',
192+
TOUR_DISMISS_REASON = '$product_tour_dismiss_reason',
193+
TOUR_BUTTON_TEXT = '$product_tour_button_text',
194+
TOUR_BUTTON_ACTION = '$product_tour_button_action',
195+
TOUR_BUTTON_LINK = '$product_tour_button_link',
196+
TOUR_BUTTON_TOUR_ID = '$product_tour_button_tour_id',
197+
TOUR_STEPS_COUNT = '$product_tour_steps_count',
198+
TOUR_STEP_SELECTOR = '$product_tour_step_selector',
199+
TOUR_STEP_SELECTOR_FOUND = '$product_tour_step_selector_found',
200+
TOUR_STEP_ELEMENT_TAG = '$product_tour_step_element_tag',
201+
TOUR_STEP_ELEMENT_ID = '$product_tour_step_element_id',
202+
TOUR_STEP_ELEMENT_CLASSES = '$product_tour_step_element_classes',
203+
TOUR_STEP_ELEMENT_TEXT = '$product_tour_step_element_text',
204+
TOUR_ERROR = '$product_tour_error',
205+
TOUR_MATCHES_COUNT = '$product_tour_matches_count',
206+
TOUR_FAILURE_PHASE = '$product_tour_failure_phase',
207+
TOUR_WAITED_FOR_ELEMENT = '$product_tour_waited_for_element',
208+
TOUR_WAIT_DURATION_MS = '$product_tour_wait_duration_ms',
209+
TOUR_BANNER_SELECTOR = '$product_tour_banner_selector',
210+
TOUR_LINKED_SURVEY_ID = '$product_tour_linked_survey_id',
211+
USE_MANUAL_SELECTOR = '$use_manual_selector',
212+
INFERENCE_DATA_PRESENT = '$inference_data_present',
213+
}

packages/browser/src/utils/product-tour-event-receiver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { PRODUCT_TOURS_ACTIVATED } from '../constants'
2-
import { ProductTour } from '../posthog-product-tours-types'
2+
import { ProductTour, ProductTourEventName } from '../posthog-product-tours-types'
33
import { PostHog } from '../posthog-core'
44
import { EventReceiver } from './event-receiver'
55
import { createLogger } from './logger'
@@ -18,7 +18,7 @@ export class ProductTourEventReceiver extends EventReceiver<ProductTour> {
1818
}
1919

2020
protected _getShownEventName(): string {
21-
return 'product tour shown'
21+
return ProductTourEventName.SHOWN
2222
}
2323

2424
protected _getItems(callback: (items: ProductTour[]) => void): void {

packages/browser/terser-mangled-names.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
"_capturePageview",
4949
"_captureSnapshot",
5050
"_captureSnapshotBuffered",
51+
"_captureStepSelectorFailed",
52+
"_captureStepShown",
5153
"_checkAction",
5254
"_checkClickTimer",
5355
"_checkClicks",

0 commit comments

Comments
 (0)