@glowhop/core-tour 1.3.0 → 1.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,187 @@
1
1
  # @glowhop/core-tour
2
2
 
3
+ ## 1.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b838fb5: Move `allowScroll` into the step behavior.
8
+
9
+ **Breaking changes**
10
+
11
+ - `StartOptions.allowScroll` and the `allowScroll` key of a JSON config are removed: use `behavior.allowScroll`. The workflow `behavior` still applies it to every step.
12
+
13
+ **Added**
14
+
15
+ - `behavior.allowScroll` can be set per step, and `context.props.update({ behavior: { allowScroll } })` locks or releases page scroll at once while the step is shown.
16
+ - cdd1efc: Make `behavior.autoFocus: false` never move focus during a step. It used to keep focus only when it was already in the popover, and moved it onto the Advance button otherwise, which is where almost every step starts.
17
+
18
+ **Breaking changes**
19
+
20
+ - With `autoFocus: false`, focus the page lost, such as the button that started the tour once a modal step makes the page inert, now stays on the body instead of moving onto the Advance or Previous button. Move it yourself once the step is shown if it needs to be somewhere.
21
+ - With `autoFocus: false`, focus is no longer pulled back into the popover when the focused target is removed, or when `allowInteraction` is turned off while the target has focus.
22
+
23
+ **Added**
24
+
25
+ - With `autoFocus: false` on a step that allows interaction, focus now stays wherever it is on the page, so a user filling in a form the tour walks them through keeps typing in their field.
26
+
27
+ The focus trap and the focus restoration at the end of the tour are unchanged.
28
+ - 8186981: Make step behavior part of the dynamic step props.
29
+
30
+ **Breaking changes**
31
+
32
+ - `WorkflowStepDefinition.behavior` is removed: the step behavior now lives in `props.behavior`, merged over the workflow `behavior` defaults like the other options.
33
+ - `props.set()` replaces `behavior` along with the other props: spread the current props to keep it.
34
+
35
+ **Added**
36
+
37
+ - `context.props.update({ behavior })` changes the step behavior while the tour runs, and the value appears in `state.currentStep.currentProps.behavior`. `allowInteraction` applies at once (modality, focus, indicator fade), `overlayClick` on the next click, `autoFocus` / `autoScroll` / `scroll` on the next entry, and `missingTarget` on the next target resolution.
38
+ - `props.set()` and `props.update()` validate `behavior`, like the other options.
39
+ - 59b4c73: Use the same component names in every adapter.
40
+
41
+ **Breaking changes**
42
+
43
+ - React and Solid: every component takes the `GlowTour` prefix (`GlowTourRoot`, `GlowTourPopover`, `GlowTourHeader`, `GlowTourContent`, `GlowTourFooter`, `GlowTourOverlay`, `GlowTourPointer`, `GlowTourAdvanceTrigger`, `GlowTourCancelTrigger`), `DefaultTour` becomes `GlowTourDefault` and `DefaultTourProps` becomes `GlowTourDefaultProps`.
44
+ - All adapters: the back trigger becomes the previous trigger. `BackTrigger` / `GlowTourBackTrigger` become `GlowTourPreviousTrigger`, the Angular selector and Vanilla tag `glow-tour-back-trigger` become `glow-tour-previous-trigger`, and the `backLabel` prop (Vanilla `back-label` attribute) becomes `previousLabel` (`previous-label`).
45
+ - The default label of the previous trigger is "Previous step" instead of "Back step".
46
+ - Vanilla: `createDefaultTourElement()` and `CreateDefaultTourElementOptions` are removed. Use the `<glow-tour-default>` element, with its `tour` property and `id-prefix` attribute. The `VanillaGlowTour` type alias is removed: use `Tour`.
47
+
48
+ **Added**
49
+
50
+ - Vanilla `<glow-tour-default>` element and `GlowTourDefaultElement` type, registered with the other elements.
51
+ - 89f2b09: Align the JSON config with the builder names and version its format.
52
+
53
+ - A workflow config carries `version: "1.1"`.
54
+ - Step config keys follow the builder: `beforeEnter`, `beforeLeave`, and `targetEvents` (entries keep `event` and `action`).
55
+ - `TargetEventConfig` replaces `EventHandlerConfig`, and the core `TargetEventHandler` type replaces `EventHandler`.
56
+ - e7b9c52: Designate steps by id when jumping to them.
57
+
58
+ **Breaking changes**
59
+
60
+ - `tour.goToStep(index)` is removed. Use `tour.goTo(id)` with the step's `id`, like `startAt` in `start()`: an index breaks as soon as steps are reordered or inserted.
61
+
62
+ **Added**
63
+
64
+ - `tour.goTo(id)` goes to the step with this id, skipping the steps in between. It does nothing while a transition is in progress or when that step is already shown, and throws when no step has this id.
65
+ - `context.goTo(id)` in step actions and target event handlers. Like `context.advance()`, it stops the remaining actions of the step. An unknown id fails the tour with `tour:error`.
66
+ - a0963a1: GlowTour.js 1.4 contains breaking changes: old names are removed, not deprecated. Upgrade every `@glowhop/*` package together and follow the migration guide, which lists every change with the code to update: https://glowtour.dev/docs/migration/1-4
67
+ - 3072d88: Add the `"detached"` missing-target strategy.
68
+
69
+ **Added**
70
+
71
+ - `behavior.missingTarget.strategy: "detached"` shows a step whose target is not found with its popover centered in the viewport, over a backdrop that covers the whole screen. It applies as soon as the target is missing, and also when a target disappears for good while its step is on screen.
72
+ - A detached step has no cutout and no pointer, does not scroll, and keeps the page blocked even when `allowInteraction` is `true`. Its `targetEvents` are not bound, and `context.target` is the document's `<body>`.
73
+ - The JSON config accepts `"detached"`.
74
+ - 337378c: Reshape the step options around positive booleans and grouped behavior settings.
75
+
76
+ **Breaking changes**
77
+
78
+ - `behavior.disableAutoFocus` and `behavior.disableAutoScroll` are replaced by `behavior.autoFocus` and `behavior.autoScroll`, which default to `true`: `disableAutoFocus: true` becomes `autoFocus: false`.
79
+ - `indicator.disabled` and `popover.arrow.disabled` are renamed to `indicator.hidden` and `popover.arrow.hidden`.
80
+ - `popover.arrow.disableAutoStyles` is replaced by `popover.arrow.autoStyles`, which defaults to `true`: `disableAutoStyles: true` becomes `autoStyles: false`.
81
+ - `popover.keyboardShortcuts` moves to the root `controls` option: `keyboardShortcuts: { advance: ["n"] }` becomes `controls: { advance: { keys: ["n"] } }`.
82
+ - `behavior.missingTargetStrategy` and `behavior.targetTimeout` are grouped into `behavior.missingTarget: { strategy, timeout }`.
83
+ - The JSON config follows the same shapes and rejects the old keys.
84
+
85
+ **Added**
86
+
87
+ - `MissingTargetOptions` type.
88
+ - 5d53fcc: Make the step `title` optional.
89
+
90
+ **Added**
91
+
92
+ - `title` can be left out of a step, in the builder and in the JSON config. Without a title, the header is not rendered, the popover dialog takes its accessible name from the content, and `aria-describedby` is removed so the content is not announced twice.
93
+ - A header you compose yourself follows the same rule: it renders nothing, or is hidden in the vanilla adapter, while the step has no title.
94
+ - 3e7ae77: Replace the popover button flags and keyboard shortcuts with a root `controls` option.
95
+
96
+ **Breaking changes**
97
+
98
+ - `popover.hideFooter`, `popover.hideAdvanceButton`, `popover.hidePreviousButton`, `popover.disableAdvanceButton` and `popover.disablePreviousButton` are removed. Set the `state` of `controls.advance`, `controls.previous` or `controls.cancel` to `"enabled"` or `"disabled"` instead: `disableAdvanceButton: true` becomes `controls: { advance: { state: "disabled" } }`. To hide a button, give it a class through `classNames` and hide that class in your CSS: `hideAdvanceButton: true` becomes `classNames: { advance: "tour-hidden" }`, and `hideFooter: true` becomes `classNames: { footer: "tour-hidden" }`.
99
+ - The keys that run each command sit next to its state, in `controls.<command>.keys`. A step's controls override the workflow ones field by field, so a step setting only `advance.state` keeps the workflow's `advance.keys`.
100
+ - A disabled control blocks its button, its keys and `overlayClick`. A button hidden through `classNames` keeps its keys, like `hideAdvanceButton` did; disable the control to block them too.
101
+ - The footer is always rendered, in the footer component and in the default tour component.
102
+ - The JSON config follows the same shape and rejects the old keys.
103
+
104
+ **Added**
105
+
106
+ - `controls.cancel.state` disables the cancel button, with its keys and `overlayClick: "cancel"`. `tour.cancel()` keeps working.
107
+ - `TourControls`, `TourControl` and `TourControlState` types.
108
+ - 3a74415: Remove the `cancellable` start option: the cancel control replaces it.
109
+
110
+ **Breaking changes**
111
+
112
+ - `cancellable` is removed from the start options and from the JSON config, which rejects it. To keep the user from ending a tour, set `controls: { cancel: { state: "disabled" } }` on the workflow: the Skip button is disabled, and `Escape` and `overlayClick: "cancel"` do nothing. To hide the button as well, give it a class through `classNames.cancel`.
113
+ - `tour.cancel()` and `context.cancel()` always cancel a running tour. `cancellable: false` used to ignore them.
114
+ - A step whose target disappears, with no step left to fall back to, always cancels the tour. With `cancellable: false` it used to fail with a missing-target error.
115
+ - The Skip button is always rendered, like the Previous and Advance buttons, and is disabled when the tour cannot be cancelled. It used to be removed.
116
+ - b933de7: Rename `tour.run(workflow)` to `tour.start(workflow)`.
117
+
118
+ **Breaking changes**
119
+
120
+ - All adapters: `tour.run(workflow, options?)` becomes `tour.start(workflow, options?)`, with the same arguments and the same promise. The value returned by `useGlowTour()` and `injectGlowTour()` exposes `start` instead of `run`.
121
+ - ecf9259: Add `classNames` to style a tour component on a given step.
122
+
123
+ **Added**
124
+
125
+ - `classNames` on the workflow options and on each step, in the builder and in the JSON config. It takes one entry per component (`overlay`, `popover`, `pointer`, `header`, `content`, `footer`, `previous`, `advance`, `cancel`), each a string or an array of strings.
126
+ - The classes are added after the classes given to the component itself, which are never replaced. A step's entry overrides the workflow entry for the same component, and the components the step leaves out keep the workflow classes.
127
+ - `context.props.update({ classNames })` changes the classes of the components it names during the step. Use the function form to add or remove a single class.
128
+ - `ClassValue` and `TourClassNames` types.
129
+ - 8f37579: Replace the step transition hooks with `beforeEnter` / `beforeLeave`, and stop resetting step props automatically.
130
+
131
+ **Breaking changes**
132
+
133
+ - Step props are no longer reset when the tour enters a step. A value set with `context.props.set()` is kept when the tour comes back to that step, until the workflow runs again. `resetPropsOnEnter` is removed from `StepParameters` and from the JSON `StepConfig`.
134
+ - `beforeAdvance()`, `beforePrevious()`, and `beforeCancel()` are removed from the step builder, along with the `BeforeActionStepContext` and `StepTransitionAction` types.
135
+ - JSON config: `advanceAction`, `previousAction`, `cancelAction`, and `resetPropsOnEnter` are no longer accepted and are reported as unknown step keys. `TransitionActionRef` is replaced by `StepHookActionRef` in `@glowhop/core-tour/config` and in every adapter's `/config` entry point.
136
+
137
+ **Added**
138
+
139
+ - `.beforeEnter(callback)` runs each time a step is entered, after its target is resolved and before the step is shown, so the props it sets are the first ones rendered. It does not run for a step skipped by `missingTarget: { strategy: "skip" }`.
140
+ - `.beforeLeave(callback)` runs before `advance()`, `previous()`, `goTo()`, or finishing the tour. It never runs on cancel.
141
+ - Both hooks receive a `StepHookContext` (`props`, `initialProps`, `target`, `signal`, `direction`) without navigation methods. JSON config: `beforeEnter` / `beforeLeave`.
142
+ - `context.props.update(patch)` merges a partial change into the step props, instead of spreading every level by hand: `props.update({ controls: { advance: { state: "enabled" } } })`. `data` is merged key by key, `overlay` / `popover` / `indicator` / `behavior` / `controls` are merged like step options over workflow defaults, and arrays are replaced. It also accepts a function of the current props.
143
+ - `StepContext` (actions and target event handlers) now exposes `initialProps` and `direction`, the direction of the navigation that entered the step.
144
+
145
+ **Migration**
146
+
147
+ - To keep resetting props on every visit:
148
+
149
+ ```ts
150
+ .beforeEnter(({ props, initialProps }) => props.set(initialProps))
151
+ ```
152
+
153
+ - Remove `resetPropsOnEnter: false`: not resetting is now the default.
154
+ - Replace `beforeAdvance(fn)` with `beforeLeave((context) => { if (context.direction === "advance") return fn(context); })`, and `beforePrevious(fn)` the same way with `"previous"`. The hook context no longer spreads the step props: read them with `context.props.get()`.
155
+ - Move `beforeCancel(fn)` to the workflow `onCancel` option, which receives a snapshot of the current step (`context.step`), or clean up from a step action with `context.signal`.
156
+ - JSON config: replace `advanceAction` / `previousAction` with `beforeLeave` (branch on `direction`) and `cancelAction` with the workflow `onCancel`.
157
+ - 82b913a: Let step hooks stop a navigation, report skipped steps, and emit each transition's events once.
158
+
159
+ **Breaking changes**
160
+
161
+ - Going back over steps skipped for a missing target no longer cancels the tour when it runs past the first step: the tour stays on the step it was on.
162
+ - `tour:start` is emitted once the first step's `beforeEnter` has passed, instead of right after `onStart`. A start that this `beforeEnter` aborts emits no event at all.
163
+ - A navigation that skips several steps emits a single `step:leave` for the step being left, instead of repeating it for every skipped step.
164
+ - `StepHookContext` is now an interface with an `abort()` method.
165
+
166
+ **Added**
167
+
168
+ - `abort()` in `beforeEnter` and `beforeLeave` stops the navigation: the tour stays on its current step and emits nothing. When the first step's `beforeEnter` aborts, the tour goes back to `idle`, like an `onStart` abort. Called after the hook has settled, it has no effect.
169
+ - `step:skip` monitoring event, emitted for each step passed over by `missingTarget: { strategy: "skip" }`.
170
+
171
+ **Event order**
172
+
173
+ A navigation emits `step:skip` for each skipped step, then `step:leave` for the step being left, then `step:enter` for the step shown.
174
+
175
+ ### Patch Changes
176
+
177
+ - c184788: Announce step changes to screen readers. Between two steps the popover was hidden from assistive technology (`aria-hidden` and `inert`) while its content changed, so the live region never announced the new step and focus left the dialog. The popover now stays exposed while it fades between steps, and is only hidden when the tour ends. Pointer input is still blocked during the fade, as before.
178
+ - 3606daf: Clear the previous tour when `start()` replaces it and the new start is aborted. An `onStart` hook, the first step's `beforeEnter`, or the `onFinish` hook of a workflow without steps calling `abort()` returned the tour to `idle` while the replaced tour's overlay and popover stayed on screen and the page stayed inert.
179
+ - 87bbca0: Make Enter activate the focused tour button. Enter was always read as the advance shortcut, so pressing it on a focused Back button moved forward and on Skip finished the tour; it now clicks that button like Space or a pointer does, so the button's own command runs after the consumer's `onClick`, which can prevent it, and the event `source` is `"trigger"`. Enter on other controls in the popover content is left to the browser. Going back onto a step whose Back button is unavailable, such as the first one, now focuses Advance instead of leaving focus on an unavailable control.
180
+ - 560f8e4: Restore focus to the element that started the tour when its first step is modal. Making the rest of the page inert blurred that element before the focus guard remembered it, so focus fell back to the document body when the tour ended, in every adapter.
181
+ - 293dc16: Keep screen readers on track when a modal step opens or the tour ends. The rest of the page now becomes inert, and the popover is marked `aria-modal`, as focus moves into the presented popover rather than at the start of the transition, like a native modal dialog. When the tour ends, focus returns to the element that started it once the popover has faded out, so screen readers announce it.
182
+
183
+ ## 1.3.1
184
+
3
185
  ## 1.3.0
4
186
 
5
187
  ### Minor Changes
package/README.md CHANGED
@@ -11,7 +11,7 @@ import { createGlowTour } from "@glowhop/core-tour";
11
11
  const tour = createGlowTour();
12
12
  const workflow = tour.create("intro").step({ id: "welcome", target: "#welcome", title: "Welcome", content: "Hello." }).build();
13
13
  // Pass tour to a mounted adapter/default composition, then call:
14
- // await tour.run(workflow);
14
+ // await tour.start(workflow);
15
15
  ```
16
16
 
17
17
  ## Builder and controller
@@ -23,11 +23,11 @@ const workflow = tour.create("intro").step({ id: "welcome", target: "#welcome",
23
23
  | Placement | `popover.placementTryOrder`, `indicator.placementTryOrder` | Try `top`, `bottom`, `left`, `right`; the resolved position may be `center`. |
24
24
  | Interaction | `behavior.allowInteraction` | Allows pointer interaction through the overlay. |
25
25
  | Scroll | step/start `scroll` | Uses `behavior`, `block`, and `inline` scroll options. The step appears without waiting for the scroll. |
26
- | Callbacks | `onStart`, `onCancel`, `onFinish`; `beforeAdvance`, `beforePrevious`, `beforeCancel` | Start callbacks are workflow options; transition callbacks are step builder methods. |
26
+ | Callbacks | `onStart`, `onCancel`, `onFinish`; `beforeEnter`, `beforeLeave` | Start callbacks are workflow options; step hooks are step builder methods and receive the navigation `direction`. Step props are not reset on enter; reset them in `beforeEnter`. |
27
27
  | Actions | `.do(fn)`, `.wait(ms)`, `.waitUntil(fn)`, `.waitUntilElement(selector)` | `waitUntil` defaults to a 16 ms interval and 3000 ms timeout. |
28
28
  | Target events | `.onTargetEvent("click", fn)` | Handlers receive the event and step context. |
29
29
 
30
- `tour.state.get()` returns status, current step, navigation capabilities, and errors; `tour.state.subscribe(listener)` observes changes. The controller exposes `run`, `advance`, `previous`, `goToStep`, and `cancel`. A new run or navigation cancels the previous operation; `dispose()` cancels pending work, releases the root, and makes the controller unusable.
30
+ `tour.state.get()` returns status, current step, navigation capabilities, and errors; `tour.state.subscribe(listener)` observes changes. The controller exposes `start`, `advance`, `previous`, `goTo`, and `cancel`. A new run or navigation cancels the previous operation; `dispose()` cancels pending work, releases the root, and makes the controller unusable.
31
31
 
32
32
  ## Errors and rendering fallbacks
33
33
 
@@ -1,5 +1,5 @@
1
1
  import { type WorkflowDefinition, type WorkflowStepDraft } from "../definition";
2
- import type { EventHandler, StartOptions, StepAction, StepContext, StepParameters, StepTransitionAction, WaitUntilOptions } from "../types";
2
+ import type { StartOptions, StepAction, StepContext, StepHookAction, StepParameters, TargetEventHandler, WaitUntilOptions } from "../types";
3
3
  declare const STEP_BUILDER_INTERNAL: unique symbol;
4
4
  /** Standard DOM event names supported by HTML elements. */
5
5
  export type EventName = keyof HTMLElementEventMap;
@@ -104,44 +104,43 @@ export declare class WorkflowStepBuilder<T> {
104
104
  */
105
105
  do(callback: StepAction<T>): this;
106
106
  /**
107
- * Add a callback that runs before advancing to the next step.
108
- * @param callback The transition callback.
107
+ * Add a callback that runs each time this step is entered, after its target is resolved and
108
+ * before the step is shown. Props set here are the first ones displayed, which makes it the place
109
+ * to reset them: `beforeEnter(({ props, initialProps }) => props.set(initialProps))`. Call
110
+ * `context.abort()` to stay on the current step instead of showing this one.
111
+ * @param callback The hook callback; `context.direction` is the direction of the navigation in progress.
109
112
  * @returns This builder for chaining.
110
113
  */
111
- beforeAdvance(callback: StepTransitionAction<T>): this;
114
+ beforeEnter(callback: StepHookAction<T>): this;
112
115
  /**
113
- * Add a callback that runs before going to the previous step.
114
- * @param callback The transition callback.
116
+ * Add a callback that runs before navigating away from this step: advance, previous, `goTo`,
117
+ * or finishing the tour. It does not run on cancel; use the workflow `onCancel` option instead.
118
+ * Call `context.abort()` to stay on this step.
119
+ * @param callback The hook callback; `context.direction` is the direction of the navigation in progress.
115
120
  * @returns This builder for chaining.
116
121
  */
117
- beforePrevious(callback: StepTransitionAction<T>): this;
118
- /**
119
- * Add a callback that runs before cancelling the tour.
120
- * @param callback The transition callback.
121
- * @returns This builder for chaining.
122
- */
123
- beforeCancel(callback: StepTransitionAction<T>): this;
122
+ beforeLeave(callback: StepHookAction<T>): this;
124
123
  /**
125
124
  * Add an event listener to the target element for a specific event name.
126
125
  * @param event The event name.
127
126
  * @param callback The handler function.
128
127
  * @returns This builder for chaining.
129
128
  */
130
- onTargetEvent<const TEventName extends EventName>(event: TEventName, callback: EventHandler<T, EventForName<TEventName>>["callback"]): this;
129
+ onTargetEvent<const TEventName extends EventName>(event: TEventName, callback: TargetEventHandler<T, EventForName<TEventName>>["callback"]): this;
131
130
  /**
132
131
  * Add an event listener to the target element for multiple event names.
133
132
  * @param events Array of event names.
134
133
  * @param callback The handler function.
135
134
  * @returns This builder for chaining.
136
135
  */
137
- onTargetEvent<const TEventNames extends readonly EventName[]>(events: TEventNames, callback: EventHandler<T, EventForName<TEventNames[number]>>["callback"]): this;
136
+ onTargetEvent<const TEventNames extends readonly EventName[]>(events: TEventNames, callback: TargetEventHandler<T, EventForName<TEventNames[number]>>["callback"]): this;
138
137
  /**
139
138
  * Add an event listener to the target element for a custom event.
140
139
  * @param event The event name.
141
140
  * @param callback The handler function.
142
141
  * @returns This builder for chaining.
143
142
  */
144
- onTargetEvent<TEvent extends Event>(event: string, callback: EventHandler<T, TEvent>["callback"]): this;
143
+ onTargetEvent<TEvent extends Event>(event: string, callback: TargetEventHandler<T, TEvent>["callback"]): this;
145
144
  [STEP_BUILDER_INTERNAL](): WorkflowStepDraft<T>;
146
145
  private assertActive;
147
146
  }
package/config/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type { CreateWorkflowFromConfigOptions } from "./from-config";
2
2
  export { createWorkflowFromConfig } from "./from-config";
3
- export type { BuiltinAction, ConfigValidationIssue, EventHandlerConfig, LifecycleActionRef, StepActionRef, StepConfig, TransitionActionRef, WorkflowConfig, WorkflowDefinitionFromConfig, } from "./types";
3
+ export type { BuiltinAction, ConfigValidationIssue, LifecycleActionRef, StepActionRef, StepConfig, StepHookActionRef, TargetEventConfig, WorkflowConfig, WorkflowDefinitionFromConfig, } from "./types";
4
4
  export { ConfigValidationError } from "./types";
5
5
  export type { ValidateWorkflowConfigOptions } from "./validate";
6
6
  export { validateWorkflowConfig } from "./validate";