turbo_tour 0.1.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +33 -0
- data/LICENSE.md +21 -0
- data/README.md +417 -0
- data/app/assets/javascripts/controllers/turbo_tour_controller.js +381 -0
- data/app/helpers/turbo_tour_helper.rb +63 -0
- data/app/views/turbo_tour/_tooltip.html.erb +47 -0
- data/config/importmap.rb +1 -0
- data/lib/generators/turbo_tour/install/install_generator.rb +71 -0
- data/lib/generators/turbo_tour/install/templates/example.yml +17 -0
- data/lib/generators/turbo_tour/install/templates/turbo_tour.rb +5 -0
- data/lib/generators/turbo_tour/install/views_generator.rb +21 -0
- data/lib/turbo_tour/configuration.rb +15 -0
- data/lib/turbo_tour/engine.rb +32 -0
- data/lib/turbo_tour/journey_loader.rb +106 -0
- data/lib/turbo_tour/version.rb +5 -0
- data/lib/turbo_tour.rb +41 -0
- data/turbo_tour.gemspec +34 -0
- metadata +144 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 542d69a4e7601b7e6a947a1990f3b38143f783a8f5d165c49c14ebcbfad05b20
|
|
4
|
+
data.tar.gz: f50a087fe9787cb34b8655d54a414c0e49929e4c64e01b3cedf4f0c7ba509da0
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f1b6de684acc357cfd277d19ad23c41b6e844b4e6590094c6b5361add0ee74806329a65dffa82f408a080d1501edc2ed792d540b3db1f4345b01c50cfbaa02f8
|
|
7
|
+
data.tar.gz: 1bf1b4817b5605657809e29ff1c3d661e627e25dfe852d2a266c62ac26d11cba799de95145e2cacd8ccbab8ef489be1497c3d6be73a12d529fbd2d666d36f635
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-03-30
|
|
6
|
+
|
|
7
|
+
Initial public release of Turbo Tour.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Rails engine integration for guided onboarding tours in Rails applications
|
|
12
|
+
- YAML-defined journeys loaded from `config/turbo_tours`
|
|
13
|
+
- One shared Stimulus controller for multiple journeys on the same page
|
|
14
|
+
- Targeting through `data-tour-step` and journey launches through `data-tour-journey`
|
|
15
|
+
- Helper-based rendering with support for auto-start and manual launch flows
|
|
16
|
+
- Browser runtime API with `TurboTour.start(...)`
|
|
17
|
+
- Runtime extension hooks including per-journey completion handling
|
|
18
|
+
- DOM analytics events for start, next, previous, complete, and skip
|
|
19
|
+
- Non-skippable tour support for required flows
|
|
20
|
+
- Framework-agnostic default tooltip partial with host-app override support
|
|
21
|
+
- Installer generators for base setup and tooltip view overrides
|
|
22
|
+
- Importmap-based controller consumption from the gem without copying JavaScript into the host app
|
|
23
|
+
|
|
24
|
+
### Documentation
|
|
25
|
+
|
|
26
|
+
- Full README covering installation, YAML structure, targeting, configuration, styling, events, and release flow
|
|
27
|
+
- Standalone documentation site with guides, reference examples, and interactive demos
|
|
28
|
+
|
|
29
|
+
### Quality
|
|
30
|
+
|
|
31
|
+
- Test coverage for configuration, journey loading, helper rendering, and generators
|
|
32
|
+
- GitHub Actions CI workflow for test and build verification
|
|
33
|
+
- GitHub Actions release workflow for automated RubyGems publishing on GitHub release
|
data/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) TurboTour contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/dhairyagabha/turbo_tour/main/.github/assets/turbo-tour-wordmark.png" alt="Turbo Tour" width="520">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://github.com/dhairyagabha/turbo_tour/actions/workflows/ci.yml">
|
|
7
|
+
<img src="https://github.com/dhairyagabha/turbo_tour/actions/workflows/ci.yml/badge.svg" alt="Tests">
|
|
8
|
+
</a>
|
|
9
|
+
<a href="https://rubygems.org/gems/turbo_tour">
|
|
10
|
+
<img src="https://img.shields.io/gem/v/turbo_tour.svg" alt="RubyGems version">
|
|
11
|
+
</a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://turbo-tour.dhairyagabhawala.com">Documentation</a>
|
|
16
|
+
·
|
|
17
|
+
<a href="https://turbo-tour.dhairyagabhawala.com/examples/product-tour">See the demo</a>
|
|
18
|
+
·
|
|
19
|
+
<a href="https://github.com/dhairyagabha/turbo_tour/blob/main/CHANGELOG.md">Changelog</a>
|
|
20
|
+
·
|
|
21
|
+
<a href="https://rubygems.org/gems/turbo_tour">RubyGems</a>
|
|
22
|
+
·
|
|
23
|
+
<a href="https://github.com/dhairyagabha/turbo_tour">GitHub</a>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
# Turbo Tour
|
|
27
|
+
|
|
28
|
+
`turbo_tour` is a lean Rails engine for guided onboarding tours built around Hotwire Turbo, a single Stimulus controller, YAML-defined journeys, and a framework-agnostic tooltip surface.
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- Multiple journeys loaded from YAML files in `config/turbo_tours`
|
|
33
|
+
- One shared Stimulus controller for every tour on the page
|
|
34
|
+
- Targeting via `data-tour-step="..."` instead of IDs or CSS selectors
|
|
35
|
+
- Configurable highlight classes for any host-app CSS approach
|
|
36
|
+
- Default tooltip partial with semantic classes that can be overridden in the host app
|
|
37
|
+
- Optional non-skippable tours for flows that must be completed
|
|
38
|
+
- Per-journey completion hooks and lightweight runtime extensions
|
|
39
|
+
- DOM analytics events with session and progress metadata
|
|
40
|
+
- Keyboard support, focus management, and lightweight positioning
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
Add the gem to your Rails app:
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
gem "turbo_tour"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then install it:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bundle install
|
|
54
|
+
bin/rails generate turbo_tour:install
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Turbo Tour now expects the host app to use the standard Rails importmap + Stimulus setup for loading the shared controller from the gem.
|
|
58
|
+
|
|
59
|
+
The installer will:
|
|
60
|
+
|
|
61
|
+
- create `config/turbo_tours/example.yml`
|
|
62
|
+
- create `config/initializers/turbo_tour.rb`
|
|
63
|
+
- make the shared Stimulus controller available from the gem in importmap-based apps
|
|
64
|
+
- register the controller only when your Stimulus setup uses manual registration
|
|
65
|
+
|
|
66
|
+
If you want a local copy of the default tooltip partial to style, run:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
bin/rails generate turbo_tour:install:views
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Define Tour Targets
|
|
73
|
+
|
|
74
|
+
Use `data-tour-step` attributes on the elements you want to spotlight:
|
|
75
|
+
|
|
76
|
+
```erb
|
|
77
|
+
<button data-tour-step="create-project">
|
|
78
|
+
Create Project
|
|
79
|
+
</button>
|
|
80
|
+
|
|
81
|
+
<section data-tour-step="dashboard-metrics">
|
|
82
|
+
...
|
|
83
|
+
</section>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## How Targeting Works
|
|
87
|
+
|
|
88
|
+
Turbo Tour uses two small data attributes when you launch tours from markup:
|
|
89
|
+
|
|
90
|
+
- `data-tour-step="create-project"` marks a DOM element that a YAML step can target
|
|
91
|
+
- `data-tour-journey="dashboard_intro"` tells `click->turbo-tour#start` which preloaded journey to start
|
|
92
|
+
|
|
93
|
+
That means this button:
|
|
94
|
+
|
|
95
|
+
```erb
|
|
96
|
+
<button data-tour-step="create-project">
|
|
97
|
+
Create Project
|
|
98
|
+
</button>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
is resolved by this YAML step:
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
- name: create_project
|
|
105
|
+
target: create-project
|
|
106
|
+
title: "Create your first project"
|
|
107
|
+
body: "Click here to begin."
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
At runtime, Turbo Tour turns the step's `target` into the selector `[data-tour-step="create-project"]`.
|
|
111
|
+
|
|
112
|
+
## Create Journeys
|
|
113
|
+
|
|
114
|
+
Journeys live in YAML. Step order is determined by array order, so you do not need explicit indexes.
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
journeys:
|
|
118
|
+
dashboard_intro:
|
|
119
|
+
- name: create_project
|
|
120
|
+
target: create-project
|
|
121
|
+
title: "Create your first project"
|
|
122
|
+
body: "Click here to create your first project."
|
|
123
|
+
|
|
124
|
+
- name: dashboard_metrics
|
|
125
|
+
target: dashboard-metrics
|
|
126
|
+
title: "Track performance"
|
|
127
|
+
body: "This area shows your analytics metrics."
|
|
128
|
+
|
|
129
|
+
invite_team:
|
|
130
|
+
- name: invite_button
|
|
131
|
+
target: invite-button
|
|
132
|
+
title: "Invite your team"
|
|
133
|
+
body: "Bring collaborators into your workspace."
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Each step key has one job:
|
|
137
|
+
|
|
138
|
+
- `name` is the step identifier used in events and analytics payloads
|
|
139
|
+
- `target` maps to the matching `data-tour-step` value in the DOM
|
|
140
|
+
- `title` is the tooltip heading
|
|
141
|
+
- `body` is the tooltip copy
|
|
142
|
+
|
|
143
|
+
Step order comes from the YAML array order. The first item is step 1, the second item is step 2, and so on. No explicit index is needed.
|
|
144
|
+
|
|
145
|
+
## Render a Tour
|
|
146
|
+
|
|
147
|
+
Render one or more journeys into a view:
|
|
148
|
+
|
|
149
|
+
```erb
|
|
150
|
+
<%= turbo_tour "dashboard_intro" %>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
To preload multiple journeys into one controller root:
|
|
154
|
+
|
|
155
|
+
```erb
|
|
156
|
+
<%= turbo_tour "dashboard_intro", "invite_team", auto_start: false %>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
By default, the first journey auto-starts. Set `auto_start: false` when you want to trigger tours manually.
|
|
160
|
+
|
|
161
|
+
If a tour should not be dismissible, pass `skippable: false`:
|
|
162
|
+
|
|
163
|
+
```erb
|
|
164
|
+
<%= turbo_tour "security_setup", auto_start: false, skippable: false do %>
|
|
165
|
+
...
|
|
166
|
+
<% end %>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
When a tour is not skippable, Turbo Tour hides the skip control and ignores the Escape key for that helper root.
|
|
170
|
+
|
|
171
|
+
If one helper root preloads several journeys, you can override skip behavior per journey:
|
|
172
|
+
|
|
173
|
+
```erb
|
|
174
|
+
<%= turbo_tour "dashboard_intro", "security_setup",
|
|
175
|
+
auto_start: false,
|
|
176
|
+
skippable: { dashboard_intro: true, security_setup: false } do %>
|
|
177
|
+
...
|
|
178
|
+
<% end %>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Start Tours Manually
|
|
182
|
+
|
|
183
|
+
The cleanest manual-start pattern is to wrap the relevant page markup with the helper and trigger the shared controller directly:
|
|
184
|
+
|
|
185
|
+
```erb
|
|
186
|
+
<%= turbo_tour "dashboard_intro", auto_start: false do %>
|
|
187
|
+
<button type="button" data-action="click->turbo-tour#start" data-tour-journey="dashboard_intro">
|
|
188
|
+
Start tour
|
|
189
|
+
</button>
|
|
190
|
+
|
|
191
|
+
<button data-tour-step="create-project">
|
|
192
|
+
Create Project
|
|
193
|
+
</button>
|
|
194
|
+
<% end %>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
This keeps the launch button, step targets, and tooltip template inside the same Stimulus scope without adding a second host-app controller.
|
|
198
|
+
|
|
199
|
+
Turbo Tour also exposes a browser API when you need to start a rendered journey from separate JavaScript:
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
TurboTour.start("dashboard_intro")
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
It also exposes a completion hook API so host apps can add behavior in separate JavaScript modules instead of editing the base controller:
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
TurboTour.onComplete("dashboard_intro", ({ detail }) => {
|
|
209
|
+
window.analytics?.track("Dashboard Intro Completed", detail)
|
|
210
|
+
})
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
If you prefer module imports over globals, the gem-provided controller module also exports `onComplete` and `registerExtension`, so you can keep host-specific behavior in a separate file.
|
|
214
|
+
|
|
215
|
+
The helper renders one controller root for the wrapped content:
|
|
216
|
+
|
|
217
|
+
```html
|
|
218
|
+
<div
|
|
219
|
+
data-controller="turbo-tour"
|
|
220
|
+
data-turbo-tour-journey="dashboard_intro"
|
|
221
|
+
...
|
|
222
|
+
></div>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
With the default Rails importmap + Stimulus setup, no controller file needs to be copied into the host app. Turbo Tour pins `controllers/turbo_tour_controller` from the gem, so `eagerLoadControllersFrom("controllers", application)` will pick it up automatically.
|
|
226
|
+
|
|
227
|
+
If your app uses manual Stimulus registration instead of eager or lazy loading, import the gem controller like this:
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
import TurboTourController from "controllers/turbo_tour_controller"
|
|
231
|
+
application.register("turbo-tour", TurboTourController)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Completion Hooks and Extensions
|
|
235
|
+
|
|
236
|
+
Register a reusable extension when you want grouped lifecycle behavior:
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
TurboTour.registerExtension({
|
|
240
|
+
name: "onboarding-follow-ups",
|
|
241
|
+
journeys: {
|
|
242
|
+
dashboard_intro: {
|
|
243
|
+
onComplete() {
|
|
244
|
+
window.location.assign("/projects/new")
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
invite_team: {
|
|
248
|
+
onComplete({ detail }) {
|
|
249
|
+
window.app?.celebrate(detail.journey_name)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Completion hooks receive a context object with:
|
|
257
|
+
|
|
258
|
+
- `detail`, which matches the DOM event payload
|
|
259
|
+
- `journeyName`, `stepName`, `stepIndex`, `totalSteps`
|
|
260
|
+
- `progress`, `progressPercentage`, `sessionId`
|
|
261
|
+
- `controller`, `target`, `panel`, `step`, and `steps`
|
|
262
|
+
|
|
263
|
+
Extensions can implement these lifecycle methods:
|
|
264
|
+
|
|
265
|
+
- `onStart`
|
|
266
|
+
- `onNext`
|
|
267
|
+
- `onPrevious`
|
|
268
|
+
- `onComplete`
|
|
269
|
+
- `onSkip`
|
|
270
|
+
|
|
271
|
+
## Multiple Journeys on the Same Page
|
|
272
|
+
|
|
273
|
+
More than one journey can exist on the same page. You can either:
|
|
274
|
+
|
|
275
|
+
- render separate helper roots for each journey
|
|
276
|
+
- preload several journeys into one helper root and start them by name
|
|
277
|
+
|
|
278
|
+
Every runtime path still uses the same `controllers/turbo_tour_controller` module from the gem unless the host app intentionally overrides that pin locally.
|
|
279
|
+
|
|
280
|
+
## Styling
|
|
281
|
+
|
|
282
|
+
Turbo Tour does not require Tailwind or any other CSS framework.
|
|
283
|
+
|
|
284
|
+
Highlighting is class-based so you can plug in whatever styling approach your host app already uses. The default highlight class string is empty:
|
|
285
|
+
|
|
286
|
+
```ruby
|
|
287
|
+
""
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Override them in the initializer:
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
TurboTour.configure do |config|
|
|
294
|
+
config.highlight_classes = "is-tour-highlighted"
|
|
295
|
+
end
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
If your app uses utility classes, component classes, or design-system hooks, pass those classes here. Turbo Tour only adds and removes the configured class string.
|
|
299
|
+
|
|
300
|
+
You can also make tours non-skippable by default:
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
TurboTour.configure do |config|
|
|
304
|
+
config.skippable = false
|
|
305
|
+
end
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Override the Tooltip Partial
|
|
309
|
+
|
|
310
|
+
Turbo Tour renders `turbo_tour/tooltip`, so the host app can override it by adding:
|
|
311
|
+
|
|
312
|
+
```text
|
|
313
|
+
app/views/turbo_tour/_tooltip.html.erb
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
The fastest way to start from the gem's default structure is:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
bin/rails generate turbo_tour:install:views
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
The shipped partial is intentionally framework-agnostic and exposes semantic classes such as:
|
|
323
|
+
|
|
324
|
+
- `turbo-tour-tooltip`
|
|
325
|
+
- `turbo-tour-tooltip__content`
|
|
326
|
+
- `turbo-tour-tooltip__title`
|
|
327
|
+
- `turbo-tour-tooltip__body`
|
|
328
|
+
- `turbo-tour-tooltip__button`
|
|
329
|
+
|
|
330
|
+
Keep these hooks in your override so the controller can populate and control the UI:
|
|
331
|
+
|
|
332
|
+
- `data-turbo-tour-panel`
|
|
333
|
+
- `data-turbo-tour-title`
|
|
334
|
+
- `data-turbo-tour-body`
|
|
335
|
+
- `data-turbo-tour-progress`
|
|
336
|
+
- `data-turbo-tour-prev`
|
|
337
|
+
- `data-turbo-tour-next`
|
|
338
|
+
|
|
339
|
+
Include `data-turbo-tour-skip` if you want the partial to render a skip control. Turbo Tour can run without it.
|
|
340
|
+
|
|
341
|
+
The default partial already includes the right `data-action` bindings, so host apps can copy and restyle it without reworking the controller contract.
|
|
342
|
+
|
|
343
|
+
## Analytics Events
|
|
344
|
+
|
|
345
|
+
Turbo Tour dispatches DOM events on `document`:
|
|
346
|
+
|
|
347
|
+
- `turbo-tour:start`
|
|
348
|
+
- `turbo-tour:next`
|
|
349
|
+
- `turbo-tour:previous`
|
|
350
|
+
- `turbo-tour:complete`
|
|
351
|
+
- `turbo-tour:skip-tour`
|
|
352
|
+
|
|
353
|
+
Each event includes:
|
|
354
|
+
|
|
355
|
+
```js
|
|
356
|
+
{
|
|
357
|
+
session_id: "abc123",
|
|
358
|
+
journey_name: "dashboard_intro",
|
|
359
|
+
step_name: "create_project",
|
|
360
|
+
step_index: 0,
|
|
361
|
+
total_steps: 3,
|
|
362
|
+
progress: 0.33,
|
|
363
|
+
progress_percentage: 33
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Example analytics hook:
|
|
368
|
+
|
|
369
|
+
```js
|
|
370
|
+
document.addEventListener("turbo-tour:complete", ({ detail }) => {
|
|
371
|
+
window.analytics?.track("Turbo Tour Completed", detail)
|
|
372
|
+
})
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Or, if the analytics call should only run for one specific journey:
|
|
376
|
+
|
|
377
|
+
```js
|
|
378
|
+
TurboTour.onComplete("dashboard_intro", ({ detail }) => {
|
|
379
|
+
window.analytics?.track("Dashboard Intro Completed", detail)
|
|
380
|
+
})
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Accessibility
|
|
384
|
+
|
|
385
|
+
The default controller and partial provide:
|
|
386
|
+
|
|
387
|
+
- keyboard navigation with left and right arrows, plus Escape when the tour is skippable
|
|
388
|
+
- focus transfer into the tooltip while a tour is active
|
|
389
|
+
- focus restoration when the tour ends
|
|
390
|
+
- `role="dialog"` and ARIA labeling on the tooltip panel
|
|
391
|
+
|
|
392
|
+
## Notes
|
|
393
|
+
|
|
394
|
+
- Journeys are loaded from `config/turbo_tours/**/*.yml` and `**/*.yaml`
|
|
395
|
+
- Duplicate journey names across files raise an error to keep behavior deterministic
|
|
396
|
+
- Missing target elements are skipped so partially-rendered pages do not crash the tour
|
|
397
|
+
|
|
398
|
+
## Example
|
|
399
|
+
|
|
400
|
+
```erb
|
|
401
|
+
<%= turbo_tour "dashboard_intro", auto_start: false do %>
|
|
402
|
+
<button type="button" data-action="click->turbo-tour#start" data-tour-journey="dashboard_intro">
|
|
403
|
+
Start tour
|
|
404
|
+
</button>
|
|
405
|
+
|
|
406
|
+
<button data-tour-step="create-project">Create Project</button>
|
|
407
|
+
<% end %>
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
```yaml
|
|
411
|
+
journeys:
|
|
412
|
+
dashboard_intro:
|
|
413
|
+
- name: create_project
|
|
414
|
+
target: create-project
|
|
415
|
+
title: "Create your first project"
|
|
416
|
+
body: "Click here to begin."
|
|
417
|
+
```
|