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 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
+ ```