@robertlinde/react-tour-kit 1.1.1 → 1.1.2

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
@@ -2,6 +2,12 @@
2
2
 
3
3
  Releases are published automatically from `main`; each GitHub release lists its commits. This file describes the larger changes.
4
4
 
5
+ ## 1.1.2
6
+
7
+ ### Fixed
8
+
9
+ - Tours inside the app's own dialogs (Radix and shadcn/ui, for example) skipped steps whose target lay where the page under the dialog has a sticky or fixed element — a sidebar under a full-screen dialog, for one: the target counted as covered and could "not be scrolled into view". Only what is inside the dialog a target is in can cover it now; the page underneath, merely painted over, is left out. Sticky headers inside the dialog are still avoided.
10
+
5
11
  ## 1.1.1
6
12
 
7
13
  ### Fixed
@@ -3,9 +3,9 @@ import type { Box } from '../../shared/geometry/geometry.type.js';
3
3
  export declare function clearOccluderCache(): void;
4
4
  /**
5
5
  * Rectangles (viewport coordinates) of elements that can cover the target. Excluded: elements
6
- * containing the target (highlighting a link in a sticky navigation) or inside it, empty or
7
- * invisible ones, and ones covering more than half the screen (app shells, full-screen
8
- * backgrounds), which cannot be scrolled away from.
6
+ * containing the target (highlighting a link in a sticky navigation) or inside it, elements outside
7
+ * the dialog the target is in (the page under it), empty or invisible ones, and ones covering more
8
+ * than half the screen (app shells, full-screen backgrounds), which cannot be scrolled away from.
9
9
  */
10
10
  export declare function findOccluders(target: Element | undefined, layer: Element | undefined): Box[];
11
11
  /** The occluders overlapping `box`. */
@@ -1 +1 @@
1
- {"version":3,"file":"occluders.util.d.ts","sourceRoot":"","sources":["../../../src/react-platform/dom/occluders.util.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,wCAAwC,CAAC;AAgBhE,sGAAsG;AACtG,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAuCD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,GAAG,EAAE,CAiB5F;AAED,uCAAuC;AACvC,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,CAExE"}
1
+ {"version":3,"file":"occluders.util.d.ts","sourceRoot":"","sources":["../../../src/react-platform/dom/occluders.util.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,wCAAwC,CAAC;AAgBhE,sGAAsG;AACtG,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AA0DD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,GAAG,EAAE,CAuB5F;AAED,uCAAuC;AACvC,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,CAExE"}
@@ -1,5 +1,5 @@
1
1
  import { intersect } from '../../shared/geometry/box.util.js';
2
- import { checkVisibility, containsComposed, toBox } from './elements.util.js';
2
+ import { checkVisibility, containsComposed, parentOf, toBox } from './elements.util.js';
3
3
  /**
4
4
  * Elements that stay put while the page scrolls (`position: fixed` or `sticky`): navigation bars,
5
5
  * headers, floating buttons, chat widgets, cookie banners, toasts. Anything scrolled underneath
@@ -38,6 +38,22 @@ function stayingPutElements(layer) {
38
38
  cache = { at: now, elements: found };
39
39
  return found;
40
40
  }
41
+ /** The app's own dialogs: native `<dialog>` elements and the dialogs of modal libraries (Radix, Headless UI, …). */
42
+ const dialogSelector = 'dialog[open], [role="dialog"], [role="alertdialog"]';
43
+ /**
44
+ * The dialog the target is in, if any: the innermost one. A dialog is drawn above the page it opens
45
+ * over, so only what is inside it can cover the target. The page underneath stays laid out, its
46
+ * sticky sidebar included, merely painted over: counted, the sidebar would hide every target of the
47
+ * dialog that lies where the sidebar is.
48
+ */
49
+ function dialogOf(target) {
50
+ for (let current = target; current; current = parentOf(current)) {
51
+ if (current.matches(dialogSelector)) {
52
+ return current;
53
+ }
54
+ }
55
+ return undefined;
56
+ }
41
57
  /** False for elements that are there but cannot be seen: closed menus faded out with `visibility` or `opacity`. */
42
58
  function isSeen(element) {
43
59
  const checked = checkVisibility(element, { visibilityProperty: true, opacityProperty: true });
@@ -49,17 +65,22 @@ function isSeen(element) {
49
65
  }
50
66
  /**
51
67
  * Rectangles (viewport coordinates) of elements that can cover the target. Excluded: elements
52
- * containing the target (highlighting a link in a sticky navigation) or inside it, empty or
53
- * invisible ones, and ones covering more than half the screen (app shells, full-screen
54
- * backgrounds), which cannot be scrolled away from.
68
+ * containing the target (highlighting a link in a sticky navigation) or inside it, elements outside
69
+ * the dialog the target is in (the page under it), empty or invisible ones, and ones covering more
70
+ * than half the screen (app shells, full-screen backgrounds), which cannot be scrolled away from.
55
71
  */
56
72
  export function findOccluders(target, layer) {
57
73
  const screen = { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight };
74
+ const dialog = target ? dialogOf(target) : undefined;
58
75
  const occluders = [];
59
76
  for (const element of stayingPutElements(layer)) {
60
77
  if (!element.isConnected || (target && (containsComposed(element, target) || containsComposed(target, element)))) {
61
78
  continue;
62
79
  }
80
+ // The page under the dialog the target is in: painted over, it covers nothing in the dialog.
81
+ if (dialog && !containsComposed(dialog, element)) {
82
+ continue;
83
+ }
63
84
  const rect = toBox(element.getBoundingClientRect());
64
85
  const isCovering = rect.width > 0 && rect.height > 0 && rect.width * rect.height <= (screen.width * screen.height) / 2;
65
86
  if (isCovering && isSeen(element)) {
@@ -1 +1 @@
1
- {"version":3,"file":"occluders.util.js","sourceRoot":"","sources":["../../../src/react-platform/dom/occluders.util.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"occluders.util.js","sourceRoot":"","sources":["../../../src/react-platform/dom/occluders.util.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robertlinde/react-tour-kit",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "type": "module",
5
5
  "description": "Guided product tours for React and React Native. Smooth, accessible, and guaranteed to stay on screen: the target is scrolled into view and the tooltip never renders out of sight.",
6
6
  "author": "Robert Linde",
@@ -85,29 +85,29 @@
85
85
  },
86
86
  "devDependencies": {
87
87
  "@arethetypeswrong/cli": "0.18.5",
88
- "@axe-core/playwright": "4.13.0",
89
- "@commitlint/cli": "21.2.3",
90
- "@commitlint/config-conventional": "21.2.3",
88
+ "@axe-core/playwright": "4.14.0",
89
+ "@commitlint/cli": "21.2.4",
90
+ "@commitlint/config-conventional": "21.2.4",
91
91
  "@estruyf/github-actions-reporter": "1.13.0",
92
- "@playwright/test": "1.64.0",
93
- "@radix-ui/react-dialog": "1.1.23",
92
+ "@playwright/test": "1.64.1",
93
+ "@radix-ui/react-dialog": "1.3.1",
94
94
  "@types/node": "26.6.5",
95
95
  "@types/react": "19.3.0",
96
96
  "@types/react-dom": "19.3.0",
97
- "@vitest/browser": "5.0.3",
98
- "@vitest/browser-playwright": "5.0.3",
97
+ "@vitest/browser": "5.0.5",
98
+ "@vitest/browser-playwright": "5.0.5",
99
99
  "fast-check": "4.10.2",
100
100
  "husky": "9.1.7",
101
- "jsdom": "30.1.2",
101
+ "jsdom": "30.1.3",
102
102
  "lint-staged": "17.6.0",
103
103
  "prettier": "3.9.10",
104
104
  "publint": "0.3.25",
105
- "react": "19.3.0",
106
- "react-dom": "19.3.0",
107
- "react-native": "0.87.1",
105
+ "react": "19.3.1",
106
+ "react-dom": "19.3.1",
107
+ "react-native": "0.88.0",
108
108
  "react-native-web": "0.21.4",
109
109
  "typescript": "6.0.3",
110
- "vitest": "5.0.3",
110
+ "vitest": "5.0.5",
111
111
  "vitest-browser-react": "2.3.0",
112
112
  "xo": "2.0.2"
113
113
  },
@@ -1,6 +1,6 @@
1
1
  import {intersect} from '../../shared/geometry/box.util.js';
2
2
  import type {Box} from '../../shared/geometry/geometry.type.js';
3
- import {checkVisibility, containsComposed, toBox} from './elements.util.js';
3
+ import {checkVisibility, containsComposed, parentOf, toBox} from './elements.util.js';
4
4
 
5
5
  /**
6
6
  * Elements that stay put while the page scrolls (`position: fixed` or `sticky`): navigation bars,
@@ -46,6 +46,25 @@ function stayingPutElements(layer: Element | undefined): readonly Element[] {
46
46
  return found;
47
47
  }
48
48
 
49
+ /** The app's own dialogs: native `<dialog>` elements and the dialogs of modal libraries (Radix, Headless UI, …). */
50
+ const dialogSelector = 'dialog[open], [role="dialog"], [role="alertdialog"]';
51
+
52
+ /**
53
+ * The dialog the target is in, if any: the innermost one. A dialog is drawn above the page it opens
54
+ * over, so only what is inside it can cover the target. The page underneath stays laid out, its
55
+ * sticky sidebar included, merely painted over: counted, the sidebar would hide every target of the
56
+ * dialog that lies where the sidebar is.
57
+ */
58
+ function dialogOf(target: Element): Element | undefined {
59
+ for (let current: Element | undefined = target; current; current = parentOf(current)) {
60
+ if (current.matches(dialogSelector)) {
61
+ return current;
62
+ }
63
+ }
64
+
65
+ return undefined;
66
+ }
67
+
49
68
  /** False for elements that are there but cannot be seen: closed menus faded out with `visibility` or `opacity`. */
50
69
  function isSeen(element: Element): boolean {
51
70
  const checked = checkVisibility(element, {visibilityProperty: true, opacityProperty: true});
@@ -59,18 +78,24 @@ function isSeen(element: Element): boolean {
59
78
 
60
79
  /**
61
80
  * Rectangles (viewport coordinates) of elements that can cover the target. Excluded: elements
62
- * containing the target (highlighting a link in a sticky navigation) or inside it, empty or
63
- * invisible ones, and ones covering more than half the screen (app shells, full-screen
64
- * backgrounds), which cannot be scrolled away from.
81
+ * containing the target (highlighting a link in a sticky navigation) or inside it, elements outside
82
+ * the dialog the target is in (the page under it), empty or invisible ones, and ones covering more
83
+ * than half the screen (app shells, full-screen backgrounds), which cannot be scrolled away from.
65
84
  */
66
85
  export function findOccluders(target: Element | undefined, layer: Element | undefined): Box[] {
67
86
  const screen = {width: document.documentElement.clientWidth, height: document.documentElement.clientHeight};
87
+ const dialog = target ? dialogOf(target) : undefined;
68
88
  const occluders: Box[] = [];
69
89
  for (const element of stayingPutElements(layer)) {
70
90
  if (!element.isConnected || (target && (containsComposed(element, target) || containsComposed(target, element)))) {
71
91
  continue;
72
92
  }
73
93
 
94
+ // The page under the dialog the target is in: painted over, it covers nothing in the dialog.
95
+ if (dialog && !containsComposed(dialog, element)) {
96
+ continue;
97
+ }
98
+
74
99
  const rect = toBox(element.getBoundingClientRect());
75
100
  const isCovering =
76
101
  rect.width > 0 && rect.height > 0 && rect.width * rect.height <= (screen.width * screen.height) / 2;