@nextsparkjs/plugin-walkme 0.1.0-beta.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.env.example +23 -0
  2. package/LICENSE +21 -0
  3. package/README.md +625 -0
  4. package/components/WalkmeBeacon.tsx +64 -0
  5. package/components/WalkmeControls.tsx +111 -0
  6. package/components/WalkmeModal.tsx +144 -0
  7. package/components/WalkmeOverlay.tsx +107 -0
  8. package/components/WalkmeProgress.tsx +53 -0
  9. package/components/WalkmeProvider.tsx +674 -0
  10. package/components/WalkmeSpotlight.tsx +188 -0
  11. package/components/WalkmeTooltip.tsx +152 -0
  12. package/examples/basic-tour.ts +38 -0
  13. package/examples/conditional-tour.ts +56 -0
  14. package/examples/cross-window-tour.ts +54 -0
  15. package/hooks/useTour.ts +52 -0
  16. package/hooks/useTourProgress.ts +38 -0
  17. package/hooks/useTourState.ts +146 -0
  18. package/hooks/useWalkme.ts +52 -0
  19. package/jest.config.cjs +27 -0
  20. package/lib/conditions.ts +113 -0
  21. package/lib/core.ts +323 -0
  22. package/lib/plugin-env.ts +87 -0
  23. package/lib/positioning.ts +172 -0
  24. package/lib/storage.ts +203 -0
  25. package/lib/targeting.ts +186 -0
  26. package/lib/triggers.ts +127 -0
  27. package/lib/validation.ts +122 -0
  28. package/messages/en.json +21 -0
  29. package/messages/es.json +21 -0
  30. package/package.json +18 -0
  31. package/plugin.config.ts +26 -0
  32. package/providers/walkme-context.ts +17 -0
  33. package/tests/lib/conditions.test.ts +172 -0
  34. package/tests/lib/core.test.ts +514 -0
  35. package/tests/lib/positioning.test.ts +43 -0
  36. package/tests/lib/storage.test.ts +232 -0
  37. package/tests/lib/targeting.test.ts +191 -0
  38. package/tests/lib/triggers.test.ts +198 -0
  39. package/tests/lib/validation.test.ts +249 -0
  40. package/tests/setup.ts +52 -0
  41. package/tests/tsconfig.json +32 -0
  42. package/tsconfig.json +47 -0
  43. package/types/walkme.types.ts +316 -0
@@ -0,0 +1,249 @@
1
+ import {
2
+ TourTriggerSchema,
3
+ TourStepSchema,
4
+ TourSchema,
5
+ TourArraySchema,
6
+ validateTour,
7
+ validateTours,
8
+ validateStep,
9
+ } from '../../lib/validation'
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Fixtures
13
+ // ---------------------------------------------------------------------------
14
+
15
+ const validStep = {
16
+ id: 'step-1',
17
+ type: 'modal',
18
+ title: 'Title',
19
+ content: 'Content',
20
+ actions: ['next'],
21
+ }
22
+
23
+ const validTooltipStep = {
24
+ id: 'tooltip-1',
25
+ type: 'tooltip',
26
+ target: '#my-element',
27
+ title: 'Tooltip',
28
+ content: 'Content',
29
+ position: 'bottom',
30
+ actions: ['next', 'prev'],
31
+ }
32
+
33
+ const validTour = {
34
+ id: 'tour-1',
35
+ name: 'My Tour',
36
+ trigger: { type: 'manual' },
37
+ steps: [validStep],
38
+ }
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // TourTriggerSchema
42
+ // ---------------------------------------------------------------------------
43
+
44
+ describe('TourTriggerSchema', () => {
45
+ it('accepts all valid trigger types', () => {
46
+ for (const type of ['onFirstVisit', 'onRouteEnter', 'onEvent', 'manual', 'scheduled']) {
47
+ expect(TourTriggerSchema.safeParse({ type }).success).toBe(true)
48
+ }
49
+ })
50
+
51
+ it('rejects invalid trigger type', () => {
52
+ expect(TourTriggerSchema.safeParse({ type: 'invalid' }).success).toBe(false)
53
+ })
54
+
55
+ it('accepts optional delay, route, event fields', () => {
56
+ const result = TourTriggerSchema.safeParse({
57
+ type: 'onRouteEnter',
58
+ delay: 500,
59
+ route: '/dashboard/*',
60
+ })
61
+ expect(result.success).toBe(true)
62
+ })
63
+
64
+ it('rejects negative delay', () => {
65
+ const result = TourTriggerSchema.safeParse({ type: 'manual', delay: -1 })
66
+ expect(result.success).toBe(false)
67
+ })
68
+ })
69
+
70
+ // ---------------------------------------------------------------------------
71
+ // TourStepSchema
72
+ // ---------------------------------------------------------------------------
73
+
74
+ describe('TourStepSchema', () => {
75
+ it('accepts a valid modal step (no target required)', () => {
76
+ const result = TourStepSchema.safeParse(validStep)
77
+ expect(result.success).toBe(true)
78
+ })
79
+
80
+ it('accepts a valid tooltip step with target', () => {
81
+ const result = TourStepSchema.safeParse(validTooltipStep)
82
+ expect(result.success).toBe(true)
83
+ })
84
+
85
+ it('rejects tooltip step without target', () => {
86
+ const result = TourStepSchema.safeParse({
87
+ id: 'tooltip-bad',
88
+ type: 'tooltip',
89
+ title: 'Bad',
90
+ content: 'No target',
91
+ actions: ['next'],
92
+ })
93
+ expect(result.success).toBe(false)
94
+ })
95
+
96
+ it('rejects spotlight step without target', () => {
97
+ const result = TourStepSchema.safeParse({
98
+ id: 'spotlight-bad',
99
+ type: 'spotlight',
100
+ title: 'Bad',
101
+ content: 'No target',
102
+ actions: ['next'],
103
+ })
104
+ expect(result.success).toBe(false)
105
+ })
106
+
107
+ it('rejects beacon step without target', () => {
108
+ const result = TourStepSchema.safeParse({
109
+ id: 'beacon-bad',
110
+ type: 'beacon',
111
+ title: 'Bad',
112
+ content: 'No target',
113
+ actions: ['next'],
114
+ })
115
+ expect(result.success).toBe(false)
116
+ })
117
+
118
+ it('rejects step with empty id', () => {
119
+ const result = TourStepSchema.safeParse({ ...validStep, id: '' })
120
+ expect(result.success).toBe(false)
121
+ })
122
+
123
+ it('rejects step with empty actions array', () => {
124
+ const result = TourStepSchema.safeParse({ ...validStep, actions: [] })
125
+ expect(result.success).toBe(false)
126
+ })
127
+
128
+ it('rejects step with invalid action', () => {
129
+ const result = TourStepSchema.safeParse({ ...validStep, actions: ['invalid'] })
130
+ expect(result.success).toBe(false)
131
+ })
132
+
133
+ it('accepts all valid step types', () => {
134
+ for (const type of ['tooltip', 'modal', 'spotlight', 'beacon', 'floating']) {
135
+ const step = type === 'modal' || type === 'floating'
136
+ ? { ...validStep, type }
137
+ : { ...validStep, type, target: '#el' }
138
+ expect(TourStepSchema.safeParse(step).success).toBe(true)
139
+ }
140
+ })
141
+ })
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // TourSchema
145
+ // ---------------------------------------------------------------------------
146
+
147
+ describe('TourSchema', () => {
148
+ it('accepts a valid tour', () => {
149
+ const result = TourSchema.safeParse(validTour)
150
+ expect(result.success).toBe(true)
151
+ })
152
+
153
+ it('rejects tour with empty id', () => {
154
+ const result = TourSchema.safeParse({ ...validTour, id: '' })
155
+ expect(result.success).toBe(false)
156
+ })
157
+
158
+ it('rejects tour with no steps', () => {
159
+ const result = TourSchema.safeParse({ ...validTour, steps: [] })
160
+ expect(result.success).toBe(false)
161
+ })
162
+
163
+ it('accepts tour with conditions', () => {
164
+ const result = TourSchema.safeParse({
165
+ ...validTour,
166
+ conditions: {
167
+ userRole: ['admin'],
168
+ completedTours: ['onboarding'],
169
+ },
170
+ })
171
+ expect(result.success).toBe(true)
172
+ })
173
+
174
+ it('accepts tour with priority', () => {
175
+ const result = TourSchema.safeParse({ ...validTour, priority: 10 })
176
+ expect(result.success).toBe(true)
177
+ })
178
+ })
179
+
180
+ // ---------------------------------------------------------------------------
181
+ // TourArraySchema
182
+ // ---------------------------------------------------------------------------
183
+
184
+ describe('TourArraySchema', () => {
185
+ it('validates an array of tours', () => {
186
+ const result = TourArraySchema.safeParse([validTour, { ...validTour, id: 'tour-2' }])
187
+ expect(result.success).toBe(true)
188
+ })
189
+
190
+ it('rejects if any tour is invalid', () => {
191
+ const result = TourArraySchema.safeParse([validTour, { id: '' }])
192
+ expect(result.success).toBe(false)
193
+ })
194
+ })
195
+
196
+ // ---------------------------------------------------------------------------
197
+ // validateTour
198
+ // ---------------------------------------------------------------------------
199
+
200
+ describe('validateTour', () => {
201
+ it('returns valid: true for a valid tour', () => {
202
+ const result = validateTour(validTour)
203
+ expect(result.valid).toBe(true)
204
+ expect(result.tour).toBeDefined()
205
+ expect(result.errors).toBeUndefined()
206
+ })
207
+
208
+ it('returns valid: false with errors for invalid tour', () => {
209
+ const result = validateTour({ id: '' })
210
+ expect(result.valid).toBe(false)
211
+ expect(result.errors).toBeDefined()
212
+ expect(result.tour).toBeUndefined()
213
+ })
214
+ })
215
+
216
+ // ---------------------------------------------------------------------------
217
+ // validateTours
218
+ // ---------------------------------------------------------------------------
219
+
220
+ describe('validateTours', () => {
221
+ it('filters valid tours from mixed input', () => {
222
+ const result = validateTours([validTour, { id: '' }, { ...validTour, id: 'tour-2' }])
223
+ expect(result.validTours).toHaveLength(2)
224
+ expect(result.errors).toHaveLength(1)
225
+ expect(result.valid).toBe(false)
226
+ })
227
+
228
+ it('returns valid: true when all tours are valid', () => {
229
+ const result = validateTours([validTour])
230
+ expect(result.valid).toBe(true)
231
+ expect(result.errors).toBeUndefined()
232
+ })
233
+ })
234
+
235
+ // ---------------------------------------------------------------------------
236
+ // validateStep
237
+ // ---------------------------------------------------------------------------
238
+
239
+ describe('validateStep', () => {
240
+ it('returns valid: true for a valid step', () => {
241
+ expect(validateStep(validStep).valid).toBe(true)
242
+ })
243
+
244
+ it('returns valid: false for invalid step', () => {
245
+ const result = validateStep({ id: '' })
246
+ expect(result.valid).toBe(false)
247
+ expect(result.errors).toBeDefined()
248
+ })
249
+ })
package/tests/setup.ts ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Jest test setup for WalkMe plugin.
3
+ * Polyfills and mocks needed for jsdom environment.
4
+ */
5
+
6
+ // Mock localStorage
7
+ const localStorageMock = (() => {
8
+ let store: Record<string, string> = {}
9
+ return {
10
+ getItem: jest.fn((key: string) => store[key] ?? null),
11
+ setItem: jest.fn((key: string, value: string) => {
12
+ store[key] = value
13
+ }),
14
+ removeItem: jest.fn((key: string) => {
15
+ delete store[key]
16
+ }),
17
+ clear: jest.fn(() => {
18
+ store = {}
19
+ }),
20
+ get length() {
21
+ return Object.keys(store).length
22
+ },
23
+ key: jest.fn((index: number) => Object.keys(store)[index] ?? null),
24
+ }
25
+ })()
26
+
27
+ Object.defineProperty(window, 'localStorage', {
28
+ value: localStorageMock,
29
+ })
30
+
31
+ // Mock MutationObserver
32
+ global.MutationObserver = jest.fn().mockImplementation(() => ({
33
+ observe: jest.fn(),
34
+ disconnect: jest.fn(),
35
+ takeRecords: jest.fn().mockReturnValue([]),
36
+ }))
37
+
38
+ // Mock ResizeObserver
39
+ global.ResizeObserver = jest.fn().mockImplementation(() => ({
40
+ observe: jest.fn(),
41
+ unobserve: jest.fn(),
42
+ disconnect: jest.fn(),
43
+ }))
44
+
45
+ // Mock scrollIntoView
46
+ Element.prototype.scrollIntoView = jest.fn()
47
+
48
+ // Reset localStorage between tests
49
+ beforeEach(() => {
50
+ localStorageMock.clear()
51
+ jest.clearAllMocks()
52
+ })
@@ -0,0 +1,32 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "moduleResolution": "node",
6
+ "jsx": "react-jsx",
7
+ "esModuleInterop": true,
8
+ "strict": true,
9
+ "skipLibCheck": true,
10
+ "types": ["jest"],
11
+ "typeRoots": [
12
+ "../../../packages/core/node_modules/@types"
13
+ ],
14
+ "resolveJsonModule": true,
15
+ "baseUrl": "..",
16
+ "paths": {
17
+ "~/*": ["./*"]
18
+ }
19
+ },
20
+ "include": [
21
+ "**/*.test.ts",
22
+ "**/*.test.tsx",
23
+ "setup.ts",
24
+ "../lib/**/*.ts",
25
+ "../types/**/*.ts",
26
+ "../hooks/**/*.ts",
27
+ "../hooks/**/*.tsx",
28
+ "../components/**/*.tsx",
29
+ "../providers/**/*.ts"
30
+ ],
31
+ "exclude": ["node_modules"]
32
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "extends": "../../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "baseUrl": ".",
6
+ "moduleResolution": "node",
7
+ "paths": {
8
+ "@/*": ["../../../*"],
9
+ "@/core/*": ["../../../core/*"],
10
+ "@/contents/*": ["../../../contents/*"],
11
+ "~/*": ["./*"]
12
+ }
13
+ },
14
+ "watchOptions": {
15
+ "watchFile": "useFsEvents",
16
+ "watchDirectory": "useFsEvents",
17
+ "fallbackPolling": "dynamicPriority",
18
+ "synchronousWatchDirectory": false,
19
+ "excludeDirectories": [
20
+ "**/node_modules",
21
+ "**/.next",
22
+ "**/dist",
23
+ "**/build",
24
+ "**/.turbo",
25
+ "**/coverage",
26
+ "**/.git"
27
+ ]
28
+ },
29
+ "include": [
30
+ "**/*.ts",
31
+ "**/*.tsx",
32
+ "plugin.config.ts"
33
+ ],
34
+ "exclude": [
35
+ "node_modules",
36
+ ".next",
37
+ "dist",
38
+ "build",
39
+ ".turbo",
40
+ "coverage",
41
+ ".git",
42
+ "**/*.test.ts",
43
+ "**/*.test.tsx",
44
+ "**/__tests__/**",
45
+ "**/tsconfig.tsbuildinfo"
46
+ ]
47
+ }
@@ -0,0 +1,316 @@
1
+ /**
2
+ * WalkMe Plugin - Type Definitions
3
+ *
4
+ * Complete type system for the guided tour and onboarding plugin.
5
+ * All types are exported from this single file as the source of truth.
6
+ */
7
+
8
+ // ---------------------------------------------------------------------------
9
+ // Enums / Literal Unions
10
+ // ---------------------------------------------------------------------------
11
+
12
+ /** Tour lifecycle states */
13
+ export type TourStatus = 'idle' | 'active' | 'paused' | 'completed' | 'skipped'
14
+
15
+ /** Visual step types */
16
+ export type StepType = 'tooltip' | 'modal' | 'spotlight' | 'beacon' | 'floating'
17
+
18
+ /** Tooltip positioning relative to target */
19
+ export type StepPosition = 'top' | 'bottom' | 'left' | 'right' | 'auto'
20
+
21
+ /** How a tour is activated */
22
+ export type TriggerType = 'onFirstVisit' | 'onRouteEnter' | 'onEvent' | 'manual' | 'scheduled'
23
+
24
+ /** User actions available within a step */
25
+ export type StepAction = 'next' | 'prev' | 'skip' | 'complete' | 'close'
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Tour Configuration
29
+ // ---------------------------------------------------------------------------
30
+
31
+ /** Defines a complete guided tour */
32
+ export interface Tour {
33
+ /** Unique identifier for the tour */
34
+ id: string
35
+ /** Human-readable name */
36
+ name: string
37
+ /** Optional description */
38
+ description?: string
39
+ /** How and when the tour should be triggered */
40
+ trigger: TourTrigger
41
+ /** Optional conditions that must be met to show the tour */
42
+ conditions?: TourConditions
43
+ /** Ordered list of steps in the tour */
44
+ steps: TourStep[]
45
+ /** Callback when the tour is completed */
46
+ onComplete?: () => void
47
+ /** Callback when the tour is skipped */
48
+ onSkip?: () => void
49
+ /** Priority for auto-trigger ordering (lower = higher priority) */
50
+ priority?: number
51
+ }
52
+
53
+ /** Defines a single step within a tour */
54
+ export interface TourStep {
55
+ /** Unique identifier for the step */
56
+ id: string
57
+ /** Visual type of the step */
58
+ type: StepType
59
+ /** Step title */
60
+ title: string
61
+ /** Step content / description */
62
+ content: string
63
+ /** CSS selector or data-walkme-target value to anchor to */
64
+ target?: string
65
+ /** Route path for cross-page tours (triggers navigation if different from current) */
66
+ route?: string
67
+ /** Positioning for tooltip/spotlight types */
68
+ position?: StepPosition
69
+ /** Available user actions for this step */
70
+ actions: StepAction[]
71
+ /** Delay in ms before showing this step */
72
+ delay?: number
73
+ /** Auto-advance to next step after this many ms */
74
+ autoAdvance?: number
75
+ /** Callback before the step is shown */
76
+ beforeShow?: () => Promise<void> | void
77
+ /** Callback after the step is shown */
78
+ afterShow?: () => Promise<void> | void
79
+ }
80
+
81
+ /** Defines when a tour should be activated */
82
+ export interface TourTrigger {
83
+ /** Trigger mechanism */
84
+ type: TriggerType
85
+ /** Delay in ms before activating after trigger condition is met */
86
+ delay?: number
87
+ /** Route pattern for onRouteEnter trigger */
88
+ route?: string
89
+ /** Event name for onEvent trigger */
90
+ event?: string
91
+ /** Activate after this many visits (for scheduled trigger) */
92
+ afterVisits?: number
93
+ /** Activate after this many days since first visit (for scheduled trigger) */
94
+ afterDays?: number
95
+ }
96
+
97
+ /** Conditions that must be met before a tour can start */
98
+ export interface TourConditions {
99
+ /** User must have one of these roles */
100
+ userRole?: string[]
101
+ /** All of these feature flags must be active */
102
+ featureFlags?: string[]
103
+ /** All of these tours must be completed first */
104
+ completedTours?: string[]
105
+ /** None of these tours should be completed */
106
+ notCompletedTours?: string[]
107
+ /** Custom condition function */
108
+ custom?: (context: ConditionContext) => boolean
109
+ }
110
+
111
+ /** Context passed to condition evaluators */
112
+ export interface ConditionContext {
113
+ /** Current user role */
114
+ userRole?: string
115
+ /** Active feature flags */
116
+ featureFlags?: string[]
117
+ /** IDs of completed tours */
118
+ completedTourIds: string[]
119
+ /** Total number of page visits */
120
+ visitCount: number
121
+ /** ISO date string of first visit */
122
+ firstVisitDate?: string
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // State Management
127
+ // ---------------------------------------------------------------------------
128
+
129
+ /** Runtime state for a single tour */
130
+ export interface TourState {
131
+ /** Tour identifier */
132
+ tourId: string
133
+ /** Current status */
134
+ status: TourStatus
135
+ /** Index of the current step */
136
+ currentStepIndex: number
137
+ /** ISO timestamp when the tour was started */
138
+ startedAt: string
139
+ /** ISO timestamp when completed */
140
+ completedAt?: string
141
+ /** ISO timestamp when skipped */
142
+ skippedAt?: string
143
+ }
144
+
145
+ /** Global state for the WalkMe system */
146
+ export interface WalkmeState {
147
+ /** Map of registered tours by ID */
148
+ tours: Record<string, Tour>
149
+ /** Currently active tour state (null if no tour is active) */
150
+ activeTour: TourState | null
151
+ /** IDs of completed tours */
152
+ completedTours: string[]
153
+ /** IDs of skipped tours */
154
+ skippedTours: string[]
155
+ /** Historical state of all tours */
156
+ tourHistory: Record<string, TourState>
157
+ /** Whether the system has been initialized */
158
+ initialized: boolean
159
+ /** Debug mode flag */
160
+ debug: boolean
161
+ }
162
+
163
+ /** Discriminated union of all reducer actions */
164
+ export type WalkmeAction =
165
+ | { type: 'INITIALIZE'; tours: Tour[] }
166
+ | { type: 'UPDATE_TOURS'; tours: Tour[] }
167
+ | { type: 'START_TOUR'; tourId: string }
168
+ | { type: 'NEXT_STEP' }
169
+ | { type: 'PREV_STEP' }
170
+ | { type: 'SKIP_TOUR' }
171
+ | { type: 'COMPLETE_TOUR' }
172
+ | { type: 'PAUSE_TOUR' }
173
+ | { type: 'RESUME_TOUR' }
174
+ | { type: 'RESET_TOUR'; tourId: string }
175
+ | { type: 'RESET_ALL' }
176
+ | { type: 'NAVIGATE_TO_STEP'; stepIndex: number }
177
+ | { type: 'SET_DEBUG'; enabled: boolean }
178
+ | { type: 'RESTORE_STATE'; completedTours: string[]; skippedTours: string[]; tourHistory: Record<string, TourState>; activeTour: TourState | null }
179
+
180
+ // ---------------------------------------------------------------------------
181
+ // Labels / i18n
182
+ // ---------------------------------------------------------------------------
183
+
184
+ /** Translatable UI strings for walkme components */
185
+ export interface WalkmeLabels {
186
+ /** "Next" button text */
187
+ next: string
188
+ /** "Previous" button text */
189
+ prev: string
190
+ /** "Skip tour" button text */
191
+ skip: string
192
+ /** "Complete" button text */
193
+ complete: string
194
+ /** "Close" button aria-label */
195
+ close: string
196
+ /** Progress text template, receives {current} and {total} */
197
+ progress: string
198
+ /** Beacon fallback aria-label */
199
+ tourAvailable: string
200
+ }
201
+
202
+ // ---------------------------------------------------------------------------
203
+ // Context / Provider
204
+ // ---------------------------------------------------------------------------
205
+
206
+ /** Value exposed by WalkmeContext to consumers */
207
+ export interface WalkmeContextValue {
208
+ /** Current global state */
209
+ state: WalkmeState
210
+ /** Start a specific tour by ID */
211
+ startTour: (tourId: string) => void
212
+ /** Pause the active tour */
213
+ pauseTour: () => void
214
+ /** Resume a paused tour */
215
+ resumeTour: () => void
216
+ /** Skip the active tour */
217
+ skipTour: () => void
218
+ /** Complete the active tour */
219
+ completeTour: () => void
220
+ /** Reset a specific tour (remove from completed/skipped) */
221
+ resetTour: (tourId: string) => void
222
+ /** Reset all tours to initial state */
223
+ resetAllTours: () => void
224
+ /** Advance to the next step */
225
+ nextStep: () => void
226
+ /** Go back to the previous step */
227
+ prevStep: () => void
228
+ /** Jump to a specific step index */
229
+ goToStep: (stepIndex: number) => void
230
+ /** Check if a tour has been completed */
231
+ isTourCompleted: (tourId: string) => boolean
232
+ /** Check if a tour is currently active */
233
+ isTourActive: (tourId: string) => boolean
234
+ /** Get the full Tour object for the active tour */
235
+ getActiveTour: () => Tour | null
236
+ /** Get the current TourStep for the active tour */
237
+ getActiveStep: () => TourStep | null
238
+ /** Emit a custom event (for onEvent triggers) */
239
+ emitEvent: (eventName: string) => void
240
+ }
241
+
242
+ /** Props for the WalkmeProvider component */
243
+ export interface WalkmeProviderProps {
244
+ /** Array of tour definitions */
245
+ tours: Tour[]
246
+ /** React children */
247
+ children: React.ReactNode
248
+ /** Enable debug logging */
249
+ debug?: boolean
250
+ /** Auto-start eligible tours */
251
+ autoStart?: boolean
252
+ /** Delay before auto-starting tours (ms) */
253
+ autoStartDelay?: number
254
+ /** Persist tour state in localStorage */
255
+ persistState?: boolean
256
+ /** Callback when a tour starts */
257
+ onTourStart?: (event: TourEvent) => void
258
+ /** Callback when a tour is completed */
259
+ onTourComplete?: (event: TourEvent) => void
260
+ /** Callback when a tour is skipped */
261
+ onTourSkip?: (event: TourEvent) => void
262
+ /** Callback when a step changes */
263
+ onStepChange?: (event: TourEvent) => void
264
+ /** Context for evaluating tour conditions */
265
+ conditionContext?: Omit<ConditionContext, 'completedTourIds' | 'visitCount' | 'firstVisitDate'>
266
+ /** Translated UI labels (defaults to English if not provided) */
267
+ labels?: Partial<WalkmeLabels>
268
+ /** User ID for scoping localStorage persistence per user */
269
+ userId?: string
270
+ }
271
+
272
+ // ---------------------------------------------------------------------------
273
+ // Storage
274
+ // ---------------------------------------------------------------------------
275
+
276
+ /** Schema for localStorage persistence */
277
+ export interface StorageSchema {
278
+ /** Schema version for migrations */
279
+ version: number
280
+ /** IDs of completed tours */
281
+ completedTours: string[]
282
+ /** IDs of skipped tours */
283
+ skippedTours: string[]
284
+ /** Currently active tour state */
285
+ activeTour: TourState | null
286
+ /** Historical state of all tours */
287
+ tourHistory: Record<string, TourState>
288
+ /** Total page visit count */
289
+ visitCount: number
290
+ /** ISO date string of first visit */
291
+ firstVisitDate: string
292
+ }
293
+
294
+ // ---------------------------------------------------------------------------
295
+ // Events / Analytics
296
+ // ---------------------------------------------------------------------------
297
+
298
+ /** Event emitted for analytics tracking */
299
+ export interface TourEvent {
300
+ /** Event type */
301
+ type: 'tour_started' | 'tour_completed' | 'tour_skipped' | 'step_changed' | 'step_completed'
302
+ /** Tour identifier */
303
+ tourId: string
304
+ /** Tour human-readable name */
305
+ tourName: string
306
+ /** Current step identifier */
307
+ stepId?: string
308
+ /** Current step index */
309
+ stepIndex?: number
310
+ /** Total steps in the tour */
311
+ totalSteps?: number
312
+ /** Unix timestamp */
313
+ timestamp: number
314
+ /** Additional metadata */
315
+ metadata?: Record<string, unknown>
316
+ }