ngwr 11.0.0 → 11.2.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.
Files changed (237) hide show
  1. package/action-sheet/styles/_index.scss +4 -1
  2. package/anchor/styles/_index.scss +2 -1
  3. package/calendar/styles/_index.scss +11 -2
  4. package/cascader/styles/_index.scss +3 -1
  5. package/circular-text/styles/_index.scss +11 -0
  6. package/command-palette/styles/_index.scss +14 -1
  7. package/context-menu/styles/_index.scss +2 -1
  8. package/falling-text/styles/_index.scss +2 -1
  9. package/fesm2022/ngwr-action-sheet-testing.mjs +313 -0
  10. package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -0
  11. package/fesm2022/ngwr-bar-chart-testing.mjs +89 -0
  12. package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -0
  13. package/fesm2022/ngwr-blur-text-testing.mjs +203 -0
  14. package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -0
  15. package/fesm2022/ngwr-blur-text.mjs +15 -1
  16. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  17. package/fesm2022/ngwr-border-glow-testing.mjs +325 -0
  18. package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -0
  19. package/fesm2022/ngwr-border-glow.mjs +2 -2
  20. package/fesm2022/ngwr-breadcrumbs.mjs +2 -2
  21. package/fesm2022/ngwr-calendar-heatmap-testing.mjs +95 -0
  22. package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -0
  23. package/fesm2022/ngwr-calendar-testing.mjs +368 -0
  24. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -0
  25. package/fesm2022/ngwr-calendar.mjs +43 -3
  26. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  27. package/fesm2022/ngwr-card.mjs +2 -2
  28. package/fesm2022/ngwr-cascader.mjs +14 -2
  29. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  30. package/fesm2022/ngwr-checkbox.mjs +8 -0
  31. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  32. package/fesm2022/ngwr-circular-text-testing.mjs +230 -0
  33. package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -0
  34. package/fesm2022/ngwr-circular-text.mjs +2 -2
  35. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  36. package/fesm2022/ngwr-color-picker-testing.mjs +432 -0
  37. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -0
  38. package/fesm2022/ngwr-color-picker.mjs +29 -3
  39. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  40. package/fesm2022/ngwr-context-menu.mjs +25 -5
  41. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  42. package/fesm2022/ngwr-date-picker-testing.mjs +10 -79
  43. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  44. package/fesm2022/ngwr-decrypt-text-testing.mjs +261 -0
  45. package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -0
  46. package/fesm2022/ngwr-decrypt-text.mjs +6 -1
  47. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
  48. package/fesm2022/ngwr-donut-chart-testing.mjs +93 -0
  49. package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -0
  50. package/fesm2022/ngwr-drag-drop.mjs +2 -2
  51. package/fesm2022/ngwr-drawer.mjs +36 -7
  52. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  53. package/fesm2022/ngwr-dropdown.mjs +32 -2
  54. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  55. package/fesm2022/ngwr-event-calendar-testing.mjs +446 -0
  56. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -0
  57. package/fesm2022/ngwr-event-calendar.mjs +43 -18
  58. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  59. package/fesm2022/ngwr-falling-text-testing.mjs +218 -0
  60. package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -0
  61. package/fesm2022/ngwr-falling-text.mjs +2 -2
  62. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  63. package/fesm2022/ngwr-fuzzy-text-testing.mjs +134 -0
  64. package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -0
  65. package/fesm2022/ngwr-gauge-testing.mjs +86 -0
  66. package/fesm2022/ngwr-gauge-testing.mjs.map +1 -0
  67. package/fesm2022/ngwr-glitch-text-testing.mjs +181 -0
  68. package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -0
  69. package/fesm2022/ngwr-glitch-text.mjs +2 -2
  70. package/fesm2022/ngwr-gradient-text-testing.mjs +223 -0
  71. package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -0
  72. package/fesm2022/ngwr-gradient-text.mjs +2 -2
  73. package/fesm2022/ngwr-i18n-en.mjs +14 -0
  74. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  75. package/fesm2022/ngwr-i18n-ru.mjs +14 -0
  76. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  77. package/fesm2022/ngwr-image-cropper-testing.mjs +156 -0
  78. package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -0
  79. package/fesm2022/ngwr-image-cropper.mjs +10 -3
  80. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  81. package/fesm2022/ngwr-input-number.mjs +73 -6
  82. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  83. package/fesm2022/ngwr-knob-testing.mjs +220 -0
  84. package/fesm2022/ngwr-knob-testing.mjs.map +1 -0
  85. package/fesm2022/ngwr-lightbox-testing.mjs +223 -0
  86. package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -0
  87. package/fesm2022/ngwr-lightbox.mjs +9 -2
  88. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  89. package/fesm2022/ngwr-line-chart-testing.mjs +119 -0
  90. package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -0
  91. package/fesm2022/ngwr-list.mjs +2 -2
  92. package/fesm2022/ngwr-loading-bar.mjs +2 -2
  93. package/fesm2022/ngwr-marquee-testing.mjs +336 -0
  94. package/fesm2022/ngwr-marquee-testing.mjs.map +1 -0
  95. package/fesm2022/ngwr-marquee.mjs +2 -2
  96. package/fesm2022/ngwr-meter-group-testing.mjs +80 -0
  97. package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -0
  98. package/fesm2022/ngwr-overlay.mjs +28 -1
  99. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  100. package/fesm2022/ngwr-popover.mjs +14 -0
  101. package/fesm2022/ngwr-popover.mjs.map +1 -1
  102. package/fesm2022/ngwr-rotating-text-testing.mjs +220 -0
  103. package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -0
  104. package/fesm2022/ngwr-rotating-text.mjs +2 -2
  105. package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
  106. package/fesm2022/ngwr-select.mjs +70 -11
  107. package/fesm2022/ngwr-select.mjs.map +1 -1
  108. package/fesm2022/ngwr-shiny-text-testing.mjs +237 -0
  109. package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -0
  110. package/fesm2022/ngwr-shiny-text.mjs +2 -2
  111. package/fesm2022/ngwr-slider.mjs +44 -6
  112. package/fesm2022/ngwr-slider.mjs.map +1 -1
  113. package/fesm2022/ngwr-sparkline-testing.mjs +76 -0
  114. package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -0
  115. package/fesm2022/ngwr-speed-dial-testing.mjs +279 -0
  116. package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -0
  117. package/fesm2022/ngwr-splash-cursor-testing.mjs +89 -0
  118. package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -0
  119. package/fesm2022/ngwr-split-text-testing.mjs +224 -0
  120. package/fesm2022/ngwr-split-text-testing.mjs.map +1 -0
  121. package/fesm2022/ngwr-split-text.mjs +15 -1
  122. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  123. package/fesm2022/ngwr-splitter-testing.mjs +245 -0
  124. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -0
  125. package/fesm2022/ngwr-spotlight-card-testing.mjs +368 -0
  126. package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -0
  127. package/fesm2022/ngwr-spotlight-card.mjs +2 -2
  128. package/fesm2022/ngwr-star-border-testing.mjs +172 -0
  129. package/fesm2022/ngwr-star-border-testing.mjs.map +1 -0
  130. package/fesm2022/ngwr-star-border.mjs +2 -2
  131. package/fesm2022/ngwr-table.mjs +13 -3
  132. package/fesm2022/ngwr-table.mjs.map +1 -1
  133. package/fesm2022/ngwr-tabs.mjs +15 -1
  134. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  135. package/fesm2022/ngwr-tilt-card-testing.mjs +190 -0
  136. package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -0
  137. package/fesm2022/ngwr-tilt-card.mjs +21 -3
  138. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  139. package/fesm2022/ngwr-toast.mjs +36 -7
  140. package/fesm2022/ngwr-toast.mjs.map +1 -1
  141. package/fesm2022/ngwr-tour-testing.mjs +173 -0
  142. package/fesm2022/ngwr-tour-testing.mjs.map +1 -0
  143. package/fesm2022/ngwr-tour.mjs +2 -2
  144. package/fesm2022/ngwr-tour.mjs.map +1 -1
  145. package/fesm2022/ngwr-tree.mjs +10 -3
  146. package/fesm2022/ngwr-tree.mjs.map +1 -1
  147. package/fesm2022/ngwr-typewriter-testing.mjs +228 -0
  148. package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -0
  149. package/fesm2022/ngwr-typewriter.mjs +2 -2
  150. package/fesm2022/ngwr-version.mjs +1 -1
  151. package/fesm2022/ngwr-version.mjs.map +1 -1
  152. package/fesm2022/ngwr-virtual-scroll.mjs +2 -2
  153. package/fesm2022/ngwr-waves-testing.mjs +128 -0
  154. package/fesm2022/ngwr-waves-testing.mjs.map +1 -0
  155. package/fesm2022/ngwr-waves.mjs +2 -2
  156. package/fesm2022/ngwr-window-testing.mjs +361 -0
  157. package/fesm2022/ngwr-window-testing.mjs.map +1 -0
  158. package/fesm2022/ngwr-window.mjs +84 -10
  159. package/fesm2022/ngwr-window.mjs.map +1 -1
  160. package/file-upload/styles/_index.scss +7 -2
  161. package/i18n/en.json +16 -2
  162. package/i18n/ru.json +16 -2
  163. package/llms-full.txt +147 -0
  164. package/llms.txt +27 -8
  165. package/mcp/providers.js +29 -0
  166. package/mcp/tools.js +1 -29
  167. package/package.json +145 -1
  168. package/result/styles/_index.scss +3 -1
  169. package/segmented/styles/_index.scss +5 -1
  170. package/select/styles/_index.scss +3 -1
  171. package/skills/ngwr/SKILL.md +115 -0
  172. package/skills/ngwr/references/catalog.md +212 -0
  173. package/skills/ngwr/references/setup.md +73 -0
  174. package/stepper/styles/_index.scss +18 -2
  175. package/table/styles/_index.scss +3 -1
  176. package/theme/styles/_colors.scss +15 -2
  177. package/theme/styles/_dark.scss +24 -12
  178. package/tree/styles/_index.scss +4 -2
  179. package/types/ngwr-action-sheet-testing.d.ts +260 -0
  180. package/types/ngwr-bar-chart-testing.d.ts +77 -0
  181. package/types/ngwr-blur-text-testing.d.ts +180 -0
  182. package/types/ngwr-blur-text.d.ts +12 -0
  183. package/types/ngwr-border-glow-testing.d.ts +219 -0
  184. package/types/ngwr-calendar-heatmap-testing.d.ts +79 -0
  185. package/types/ngwr-calendar-testing.d.ts +277 -0
  186. package/types/ngwr-calendar.d.ts +10 -0
  187. package/types/ngwr-checkbox.d.ts +13 -0
  188. package/types/ngwr-circular-text-testing.d.ts +196 -0
  189. package/types/ngwr-color-picker-testing.d.ts +291 -0
  190. package/types/ngwr-color-picker.d.ts +13 -1
  191. package/types/ngwr-context-menu.d.ts +15 -1
  192. package/types/ngwr-date-picker-testing.d.ts +6 -66
  193. package/types/ngwr-decrypt-text-testing.d.ts +222 -0
  194. package/types/ngwr-donut-chart-testing.d.ts +79 -0
  195. package/types/ngwr-drawer.d.ts +12 -0
  196. package/types/ngwr-dropdown.d.ts +9 -0
  197. package/types/ngwr-event-calendar-testing.d.ts +301 -0
  198. package/types/ngwr-event-calendar.d.ts +9 -6
  199. package/types/ngwr-falling-text-testing.d.ts +186 -0
  200. package/types/ngwr-fuzzy-text-testing.d.ts +130 -0
  201. package/types/ngwr-gauge-testing.d.ts +71 -0
  202. package/types/ngwr-glitch-text-testing.d.ts +172 -0
  203. package/types/ngwr-gradient-text-testing.d.ts +173 -0
  204. package/types/ngwr-image-cropper-testing.d.ts +134 -0
  205. package/types/ngwr-image-cropper.d.ts +1 -0
  206. package/types/ngwr-input-number.d.ts +33 -1
  207. package/types/ngwr-knob-testing.d.ts +150 -0
  208. package/types/ngwr-lightbox-testing.d.ts +154 -0
  209. package/types/ngwr-lightbox.d.ts +6 -0
  210. package/types/ngwr-line-chart-testing.d.ts +93 -0
  211. package/types/ngwr-marquee-testing.d.ts +280 -0
  212. package/types/ngwr-meter-group-testing.d.ts +67 -0
  213. package/types/ngwr-overlay.d.ts +19 -1
  214. package/types/ngwr-rotating-text-testing.d.ts +191 -0
  215. package/types/ngwr-select.d.ts +32 -0
  216. package/types/ngwr-shiny-text-testing.d.ts +165 -0
  217. package/types/ngwr-slider.d.ts +15 -0
  218. package/types/ngwr-sparkline-testing.d.ts +69 -0
  219. package/types/ngwr-speed-dial-testing.d.ts +195 -0
  220. package/types/ngwr-splash-cursor-testing.d.ts +95 -0
  221. package/types/ngwr-split-text-testing.d.ts +209 -0
  222. package/types/ngwr-split-text.d.ts +12 -0
  223. package/types/ngwr-splitter-testing.d.ts +174 -0
  224. package/types/ngwr-spotlight-card-testing.d.ts +303 -0
  225. package/types/ngwr-star-border-testing.d.ts +135 -0
  226. package/types/ngwr-table.d.ts +10 -1
  227. package/types/ngwr-tilt-card-testing.d.ts +158 -0
  228. package/types/ngwr-tilt-card.d.ts +2 -0
  229. package/types/ngwr-toast.d.ts +14 -5
  230. package/types/ngwr-tour-testing.d.ts +143 -0
  231. package/types/ngwr-tree.d.ts +7 -0
  232. package/types/ngwr-typewriter-testing.d.ts +201 -0
  233. package/types/ngwr-version.d.ts +1 -1
  234. package/types/ngwr-waves-testing.d.ts +121 -0
  235. package/types/ngwr-window-testing.d.ts +263 -0
  236. package/types/ngwr-window.d.ts +27 -10
  237. package/window/styles/_index.scss +44 -16
@@ -0,0 +1,303 @@
1
+ import { BaseHarnessFilters, ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
2
+
3
+ /**
4
+ * @license
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
8
+ */
9
+
10
+ /** Narrows which `<wr-spotlight-card>` a harness query matches. */
11
+ interface WrSpotlightCardHarnessFilters extends BaseHarnessFilters {
12
+ /**
13
+ * Match the card's projected text — a string is an exact match, a RegExp is tested.
14
+ *
15
+ * The only filter offered, and deliberately: the card has no name of its own, no
16
+ * disabled or open state, and its one piece of state — where the highlight last sat —
17
+ * is a coordinate nobody would filter on. The content it wraps is what tells two cards
18
+ * on a page apart.
19
+ */
20
+ readonly text?: string | RegExp;
21
+ }
22
+ /** Narrows which `[wrSpotlight]` host a harness query matches. */
23
+ interface WrSpotlightHarnessFilters extends BaseHarnessFilters {
24
+ /** Match the decorated element's text — a string is an exact match, a RegExp is tested. */
25
+ readonly text?: string | RegExp;
26
+ /**
27
+ * Match a host carrying this class.
28
+ *
29
+ * The directive adds no class of its own — it writes custom properties and nothing
30
+ * else — so unlike every component harness here there is no `.wr-*` hook to narrow by.
31
+ * The consumer's own class is the stable discriminator, which is why this filter names
32
+ * the HOST's class rather than the directive's.
33
+ */
34
+ readonly hostClass?: string;
35
+ }
36
+
37
+ /**
38
+ * @license
39
+ *
40
+ * Use of this source code is governed by an MIT-style license that can be
41
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
42
+ */
43
+
44
+ /**
45
+ * Test harness for `<wr-spotlight-card>` — a card with a soft radial highlight that
46
+ * follows the cursor.
47
+ *
48
+ * **The component's entire output is three custom properties on its own host, and this
49
+ * harness reads them off the inline declaration.** There is no DOM for the highlight at
50
+ * all: it is a `::before` pseudo-element, painted by the stylesheet from those three
51
+ * values. So the paint is out of reach here and always will be — what is in reach is the
52
+ * arithmetic and the exact spelling that feeds it, which is where this component breaks.
53
+ *
54
+ * **This harness reads PIXELS. {@link WrSpotlightHarness}, the `[wrSpotlight]` directive
55
+ * shipped from the same entry point, reads PERCENTAGES.** Both write
56
+ * `--wr-spotlight-x` / `--wr-spotlight-y`, in different units, off different events —
57
+ * the card listens for `mousemove`, the directive for `pointermove`. That is deliberate
58
+ * and documented, and it is also the mistake waiting to happen: putting `wrSpotlight` on
59
+ * a `<wr-spotlight-card>` gives you two handlers overwriting one variable in units that
60
+ * do not agree. Reach for the harness that matches the thing under test.
61
+ *
62
+ * **{@link movePointerTo} needs the card to have a box, and in a unit test it has none.**
63
+ * The component subtracts its own `getBoundingClientRect()` from the client coordinate,
64
+ * and jsdom answers that with a 0×0 rect at the origin — so the subtraction is a no-op
65
+ * and the coordinate comes back unchanged. No throw here, unlike the directive's version:
66
+ * the answer is degenerate rather than wrong, and it is legitimately what a real card
67
+ * with no box would report. But a spec that asserts `x === clientX` against an unstubbed
68
+ * rect has proved nothing about the box-relative conversion, which is the only thing
69
+ * {@link getSpotlightPosition} is for. Give the host a box first:
70
+ *
71
+ * ```ts
72
+ * const card = fixture.nativeElement.querySelector('wr-spotlight-card');
73
+ * card.getBoundingClientRect = () => ({ left: 40, top: 20, width: 200, height: 100 }) as DOMRect;
74
+ * ```
75
+ *
76
+ * Several plausible methods are deliberately absent. There is no `isSpotlightVisible()`
77
+ * or `getSpotlightOpacity()`: the highlight sits at `opacity: 0` and is lifted to `0.6`
78
+ * only under `:hover` / `:focus-within`, both of which are stylesheet selectors on a
79
+ * pseudo-element — jsdom loads no stylesheet, matches no `:hover`, and cannot read a
80
+ * pseudo-element's style at any time, so any answer would be invented. No `getGradient()`
81
+ * for the same reason. No `hover()` / `mouseAway()` wrappers, which would be worse than
82
+ * missing: the CDK's versions dispatch `mouseenter` / `mouseover` / `mouseleave`, none of
83
+ * which this component listens to and none of which changes a `:hover` match in a test —
84
+ * a spec calling them would look like it exercised the reveal and would have exercised
85
+ * nothing. No `getSpotlightRect()` or `getCardSize()`, which are layout. And no
86
+ * `respectsReducedMotion()`: this is the one animation component in the library that
87
+ * deliberately does not implement the preference — a gradient tracks the cursor and no
88
+ * content moves — so the method would answer identically for a component that honoured it
89
+ * and one that ignored it.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * const card = await loader.getHarness(WrSpotlightCardHarness.with({ text: 'Hover me' }));
94
+ *
95
+ * expect(await card.getRadiusPercent()).toBe(80);
96
+ * expect(await card.getSpotlightColor()).toBeNull(); // the theme decides
97
+ * expect(await card.getSpotlightPosition()).toBeNull(); // nothing has pointed at it
98
+ * ```
99
+ *
100
+ * @see https://ngwr.dev/guides/testing
101
+ */
102
+ declare class WrSpotlightCardHarness extends ComponentHarness {
103
+ static hostSelector: string;
104
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
105
+ static with(options?: WrSpotlightCardHarnessFilters): HarnessPredicate<WrSpotlightCardHarness>;
106
+ /**
107
+ * Where the gradient fades out, as a percentage of its own radius.
108
+ *
109
+ * Returns the number, but the assertion it really makes is about the `%` that was
110
+ * stripped to get it — this method refuses a bare `80`. The stylesheet interpolates the
111
+ * variable straight into `transparent var(--wr-spotlight-radius, 80%)`, where a unitless
112
+ * value is not a length, so the whole `radial-gradient()` declaration is dropped and the
113
+ * highlight disappears with nothing in the console to say why. Reading this as a plain
114
+ * number would sail past exactly that.
115
+ *
116
+ * Always present, whatever the input says: a `[radius]` that is not a number coerces
117
+ * back to the default 80 rather than writing nonsense into the sheet.
118
+ */
119
+ getRadiusPercent(): Promise<number>;
120
+ /**
121
+ * The highlight colour the author asked for, or `null` when they left it to the theme.
122
+ *
123
+ * `null` IS the contract for an unset `[spotlightColor]`, not a gap in it. The
124
+ * stylesheet's own default is `rgba(var(--wr-color-on-surface-rgb), .15)`, and that
125
+ * token flips per theme — which is what makes one component read as a dark glow on a
126
+ * light surface and a light beam on a dark one. A card that started writing a concrete
127
+ * colour here would look right in whichever theme it was authored against and wrong in
128
+ * the other, and this is the only method that can tell.
129
+ */
130
+ getSpotlightColor(): Promise<string | null>;
131
+ /**
132
+ * Where the highlight last sat, in pixels from the card's own top-left corner. `null`
133
+ * until something has pointed at the card.
134
+ *
135
+ * Pixels, not percentages — see this class's docs for why the distinction matters
136
+ * inside this one entry point.
137
+ *
138
+ * `null` is a state of its own rather than a stand-in for `{ x: 0, y: 0 }`: before the
139
+ * first move the stylesheet's `50% / 50%` default applies, and a pointer at the card's
140
+ * own origin legitimately reports zero, so folding the two together would make an
141
+ * untouched card indistinguishable from one being pointed at in its corner.
142
+ *
143
+ * Note there is no reset when the cursor leaves, and that is not an oversight the
144
+ * harness papers over: the stylesheet fades the whole overlay to `opacity: 0` on leave,
145
+ * so nothing is parked visibly — but the coordinate stays written for the life of the
146
+ * element. `null` therefore means "no pointer has EVER arrived", not "none is here now".
147
+ */
148
+ getSpotlightPosition(): Promise<{
149
+ x: number;
150
+ y: number;
151
+ } | null>;
152
+ /**
153
+ * Point at the card, in client coordinates.
154
+ *
155
+ * A `mousemove`, not a `pointermove` — the component listens for the former, and the
156
+ * directive in the same entry point for the latter, so a pointer event dispatched here
157
+ * is silently ignored. It goes to the host itself, so it needs no hit test and cannot
158
+ * miss in a test that has no layout; the coordinates only ever reach the component's
159
+ * own subtraction.
160
+ *
161
+ * Stub the host's `getBoundingClientRect` before calling this, or the subtraction is
162
+ * invisible — see this class's docs.
163
+ */
164
+ movePointerTo(clientX: number, clientY: number): Promise<void>;
165
+ /**
166
+ * The projected content's text, trimmed.
167
+ *
168
+ * Read from the host, which is the whole assertion: the template is a bare
169
+ * `<ng-content />` with no wrapper element, so content lands as a direct child and an
170
+ * interactive one keeps its own name and role. The component adds no ARIA and no node
171
+ * of its own — there is nothing else here for a harness to ask about.
172
+ */
173
+ getContentText(): Promise<string>;
174
+ /** The host's inline declarations — the component's whole written output. */
175
+ private vars;
176
+ /** One written coordinate, as a number, insisting on the `px` the card promises. */
177
+ private parsePixels;
178
+ }
179
+
180
+ /**
181
+ * @license
182
+ *
183
+ * Use of this source code is governed by an MIT-style license that can be
184
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
185
+ */
186
+
187
+ /**
188
+ * Test harness for `[wrSpotlight]` — the directive that makes a cursor-following
189
+ * gradient possible on an element the consumer owns.
190
+ *
191
+ * **This harness reads PERCENTAGES. {@link WrSpotlightCardHarness}, shipped from the same
192
+ * entry point, reads PIXELS.** Both write `--wr-spotlight-x` / `--wr-spotlight-y`; the
193
+ * directive writes them as a share of its own box off `pointermove`, the card as a pixel
194
+ * offset off `mousemove`. That is why {@link getSpotlightPosition} hands back raw strings
195
+ * and not numbers — collapsing `'37.5%'` to `37.5` would hide the one thing that decides
196
+ * whether the gradient lands where the cursor is, and would make the two halves of this
197
+ * entry point look interchangeable when they are not.
198
+ *
199
+ * **The directive ships no styles and adds no node.** There is no class to assert, no
200
+ * wrapper, no ARIA: whatever the element announced before the directive applies is
201
+ * exactly what it announces after, and the gradient is entirely the consumer's own CSS.
202
+ * So there is no `isSpotlightVisible()` — there is nothing here to be visible — and no
203
+ * `getResetX()` / `getResetY()`, which would echo the inputs back rather than report the
204
+ * coordinate the directive actually WROTE. The echo would pass while the written value
205
+ * was wrong, which is not hypothetical (see {@link getSpotlightPosition}). And no
206
+ * `respectsReducedMotion()`: neither export in this entry point implements the
207
+ * preference — it is the library's one documented exemption, since a gradient tracks the
208
+ * cursor and no content moves — so the method would answer the same for a directive that
209
+ * honoured it and one that ignored it.
210
+ *
211
+ * @example
212
+ * ```ts
213
+ * const spotlight = await loader.getHarness(WrSpotlightHarness.with({ hostClass: 'card' }));
214
+ *
215
+ * expect(await spotlight.getSpotlightPosition()).toEqual({ x: '50%', y: '50%' });
216
+ * await spotlight.movePointerTo(150, 25); // stub the host's rect first
217
+ * ```
218
+ *
219
+ * @see https://ngwr.dev/guides/testing
220
+ */
221
+ declare class WrSpotlightHarness extends ComponentHarness {
222
+ static hostSelector: string;
223
+ /**
224
+ * Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`.
225
+ *
226
+ * Both options describe the HOST rather than the directive, because the directive
227
+ * leaves no mark of its own to match on.
228
+ */
229
+ static with(options?: WrSpotlightHarnessFilters): HarnessPredicate<WrSpotlightHarness>;
230
+ /**
231
+ * The coordinates the directive has written, as the raw strings it wrote them.
232
+ *
233
+ * Strings on purpose — see this class's docs. The values are percentages of the host's
234
+ * own box while a pointer is over it, and whatever `[resetX]` / `[resetY]` say
235
+ * otherwise, so a `px` appearing here means something else is writing these variables.
236
+ *
237
+ * **Present from construction, before any pointer arrives.** The directive seeds the
238
+ * reset coordinates in its constructor, so the first read is `50%` / `50%` on a fresh
239
+ * host — do not mistake that for a pointer having been tracked. Worth knowing that the
240
+ * constructor runs before signal inputs are bound, so a bound (or even a static)
241
+ * `resetX` is NOT what appears here initially: it is honoured from the first
242
+ * {@link leave} onward, and the highlight jumps at that point. Reading the written
243
+ * coordinate rather than the input is what makes that visible at all.
244
+ *
245
+ * Throws once the directive has been destroyed, rather than answering with the last
246
+ * value it happened to leave behind — {@link hasTrackedPointer} is the question to ask
247
+ * when the host may be gone.
248
+ */
249
+ getSpotlightPosition(): Promise<{
250
+ x: string;
251
+ y: string;
252
+ }>;
253
+ /**
254
+ * Point at the host, in client coordinates.
255
+ *
256
+ * **Refuses on a host with no box, which in a unit test is every host.** The directive
257
+ * divides the cursor offset by the element's own width and height, so a 0×0 rect writes
258
+ * literally `Infinity%` or — at the origin — `NaN%` into the style. jsdom accepts either
259
+ * string without complaint, so an unguarded harness would hand back something that looks
260
+ * like a coordinate and describes nothing. Give the host a box first:
261
+ *
262
+ * ```ts
263
+ * const el = fixture.nativeElement.querySelector('[wrSpotlight]');
264
+ * el.getBoundingClientRect = () => ({ left: 0, top: 0, width: 200, height: 100 }) as DOMRect;
265
+ * ```
266
+ *
267
+ * A `pointermove`, not a `mousemove` — the directive listens for the former, and the
268
+ * component in the same entry point for the latter. It is dispatched on the host itself,
269
+ * so it needs no hit test and cannot miss.
270
+ */
271
+ movePointerTo(clientX: number, clientY: number): Promise<void>;
272
+ /**
273
+ * Take the pointer away.
274
+ *
275
+ * Dispatches `pointerleave` — the directive's reset path, and the only place `[resetX]`
276
+ * / `[resetY]` are read after construction. Needs no box: the handler writes the reset
277
+ * strings straight through without measuring anything, which is why this one does not
278
+ * refuse the way {@link movePointerTo} does.
279
+ *
280
+ * Not the CDK's `mouseAway()`, though that dispatches `pointerleave` too. This says what
281
+ * it does, and it does not also fire the `mouseleave` / `mouseout` that the CDK helper
282
+ * sends alongside it — events this directive ignores, and that would make a spec look
283
+ * like it had covered more than it did.
284
+ */
285
+ leave(): Promise<void>;
286
+ /**
287
+ * Whether the directive still owns the host's spotlight variables.
288
+ *
289
+ * **This is "the directive is alive", not "a pointer has moved over it".** It is `true`
290
+ * from the moment the directive constructs, because the constructor seeds the reset
291
+ * coordinates — there is no earlier state to catch. What it distinguishes is a live
292
+ * directive from a torn-down one: destroy removes both declarations from the element,
293
+ * and nothing else in the repo asserts that cleanup. A directive that stopped removing
294
+ * them would leave a stale gradient position on an element the consumer went on using,
295
+ * and the only trace would be these two declarations outliving it.
296
+ */
297
+ hasTrackedPointer(): Promise<boolean>;
298
+ /** The host's inline declarations — the directive's whole written output. */
299
+ private vars;
300
+ }
301
+
302
+ export { WrSpotlightCardHarness, WrSpotlightHarness };
303
+ export type { WrSpotlightCardHarnessFilters, WrSpotlightHarnessFilters };
@@ -0,0 +1,135 @@
1
+ import { BaseHarnessFilters, ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
2
+
3
+ /**
4
+ * @license
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
8
+ */
9
+
10
+ /** Narrows which star border a harness query matches. */
11
+ interface WrStarBorderHarnessFilters extends BaseHarnessFilters {
12
+ /**
13
+ * Match the projected content's text — the only thing telling two of these apart, since
14
+ * the decoration carries no name of its own and the host has no role. A string is an
15
+ * exact match, a RegExp is tested.
16
+ */
17
+ readonly text?: string | RegExp;
18
+ }
19
+
20
+ /**
21
+ * @license
22
+ *
23
+ * Use of this source code is governed by an MIT-style license that can be
24
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
25
+ */
26
+
27
+ /**
28
+ * Test harness for `<wr-star-border>` and for the `[wr-star-border]` attribute form.
29
+ *
30
+ * **There is no `getRayCount()`, and no `hasTopRay()`.** `rays="single"` does not remove
31
+ * the top ray: both spans are always in the DOM and the stylesheet hides one with
32
+ * `display: none`. A count would answer 2 in both modes — flatly contradicting the input
33
+ * that was set — so the variant is read as {@link isSingleRay}, off the modifier class
34
+ * the CSS itself keys on. That class is public API, and it is the honest observable.
35
+ *
36
+ * Nothing here reports on the motion either. The sweep is a `@keyframes` rule in a
37
+ * stylesheet a unit test never loads, so `animationName` is `''` and `getAnimations()` is
38
+ * empty whether the component works or not; `mode="hover"` is a bare `:hover` selector
39
+ * with no state behind it, which is why there is no `hover()` to pair with anything; and
40
+ * the reduced-motion escape is a media query with no mirror in TypeScript, so the DOM is
41
+ * identical for a user who asked for less motion and one who did not.
42
+ *
43
+ * **The inline values are read off the `style` attribute rather than through
44
+ * `getCssValue()`, and that is not a jsdom concession.** The stylesheet declares its own
45
+ * `--wr-star-border-color` and `--wr-star-border-speed` on `.wr-star-border`, so in a
46
+ * real browser a computed read answers with the stylesheet's default at exactly the
47
+ * moment the host binding is the thing that broke. What the component wrote is the only
48
+ * value nothing else can forge.
49
+ *
50
+ * On the attribute form the host's `[class]` binding MERGES with the classes the consumer
51
+ * already put there, so read the variants through {@link isHoverOnly} and
52
+ * {@link isSingleRay} rather than comparing a whole `className` — the latter passes only
53
+ * as long as the host happens to carry nothing else.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * const loader = TestbedHarnessEnvironment.loader(fixture);
58
+ * const border = await loader.getHarness(WrStarBorderHarness);
59
+ *
60
+ * expect(await border.getText()).toBe('Upgrade');
61
+ * expect(await border.getSpeedSeconds()).toBe(6);
62
+ * ```
63
+ *
64
+ * @see https://ngwr.dev/guides/testing
65
+ */
66
+ declare class WrStarBorderHarness extends ComponentHarness {
67
+ static hostSelector: string;
68
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
69
+ static with(options?: WrStarBorderHarnessFilters): HarnessPredicate<WrStarBorderHarness>;
70
+ /**
71
+ * The projected content's text, trimmed.
72
+ *
73
+ * Read from the inner panel rather than from the host, which would pick up anything the
74
+ * rays ever came to hold. They are empty spans today, so the two agree — but the panel
75
+ * is the slot the content was put in, and it is the thing a reader actually sees.
76
+ */
77
+ getText(): Promise<string>;
78
+ /** Whether the rays wait for the pointer (`mode="hover"`) instead of running always. */
79
+ isHoverOnly(): Promise<boolean>;
80
+ /**
81
+ * Whether only the bottom ray is drawn (`rays="single"`).
82
+ *
83
+ * From the modifier class, because the DOM cannot be asked: both ray spans are rendered
84
+ * in both modes and the top one is hidden in CSS. See this class's docs.
85
+ */
86
+ isSingleRay(): Promise<boolean>;
87
+ /**
88
+ * The ray colour as the consumer passed it — a CSS value, not a resolved one — or
89
+ * `null` when none was given.
90
+ *
91
+ * `null` is the common case and a real answer rather than a missing one: with no
92
+ * `[color]` the theme decides, primary on a light surface and white on a dark one, and
93
+ * a component that started writing a colour of its own there would silently take that
94
+ * choice away.
95
+ */
96
+ getRayColor(): Promise<string | null>;
97
+ /**
98
+ * How long one ray sweep takes, in seconds.
99
+ *
100
+ * Refuses a value that lost its unit rather than parsing the number out of it. The
101
+ * component builds this string from a plain number input and the stylesheet drops it
102
+ * straight into `animation-duration`, where a bare `6` is invalid — the declaration is
103
+ * discarded, the stylesheet's own default takes over, and nothing about the page looks
104
+ * wrong. A `parseFloat` would report 6 either way.
105
+ */
106
+ getSpeedSeconds(): Promise<number>;
107
+ /**
108
+ * How far the rays bleed past the inner panel, in pixels.
109
+ *
110
+ * Read off the host's `padding`, which is where `[thickness]` lands: the component
111
+ * writes the SHORTHAND, `<n>px 0`. Worth knowing on the attribute form — an inline
112
+ * shorthand wins the cascade, so a `<button wr-star-border>` loses whatever padding its
113
+ * own classes gave it, horizontal included. The visible padding lives on the inner
114
+ * panel, so this is a bleed measurement rather than a spacing one.
115
+ *
116
+ * Throws when the host's padding is not that shape, which means either the binding
117
+ * stopped arriving or something else is writing the property inline.
118
+ */
119
+ getThickness(): Promise<number>;
120
+ /**
121
+ * Whether every ray is kept out of the accessible tree.
122
+ *
123
+ * The one question worth asking about both spans at once — not how many there are, but
124
+ * whether either has stopped being decoration. They carry no text and mean nothing, so
125
+ * a ray that reached a screen reader would be an empty announcement in the middle of a
126
+ * button. Answered for all of them together, so one ray losing the attribute is enough
127
+ * to report `false`.
128
+ */
129
+ areRaysDecorative(): Promise<boolean>;
130
+ /** Read one declaration off the host's inline `style` — see this class's docs for why not `getCssValue()`. */
131
+ private hostStyle;
132
+ }
133
+
134
+ export { WrStarBorderHarness };
135
+ export type { WrStarBorderHarnessFilters };
@@ -368,7 +368,16 @@ declare class WrTable {
368
368
  readonly selectGroupLabel: _angular_core.InputSignal<string | null>;
369
369
  /** Accessible name of a group band's collapse toggle. Falls back to `table.toggleGroup`. */
370
370
  readonly toggleGroupLabel: _angular_core.InputSignal<string | null>;
371
+ /** Accessible name of the loading overlay. Falls back to `table.loading`. */
372
+ readonly loadingLabel: _angular_core.InputSignal<string | null>;
371
373
  protected readonly resolvedEmptyLabel: _angular_core.Signal<string>;
374
+ /**
375
+ * The loading overlay was a bare spinner in a `<div>` — a busy state with no
376
+ * accessible name, which a screen reader reports as nothing at all while the
377
+ * table sits empty. `table.loading` shipped in both catalogs for it and was
378
+ * never read.
379
+ */
380
+ protected readonly resolvedLoadingLabel: _angular_core.Signal<string>;
372
381
  protected readonly resolvedSortLabel: _angular_core.Signal<string>;
373
382
  protected readonly resolvedSelectAllLabel: _angular_core.Signal<string>;
374
383
  protected readonly resolvedSelectRowLabel: _angular_core.Signal<string>;
@@ -681,7 +690,7 @@ declare class WrTable {
681
690
  protected getValue(item: Record<string, unknown>, key: unknown): unknown;
682
691
  protected templateFor(key: unknown): TemplateRef<WrTableCellContext> | undefined;
683
692
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrTable, never>;
684
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrTable, "wr-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "items": { "alias": "items"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "reorderable": { "alias": "reorderable"; "required": false; "isSignal": true; }; "columnOrder": { "alias": "columnOrder"; "required": false; "isSignal": true; }; "rowSelection": { "alias": "rowSelection"; "required": false; "isSignal": true; }; "rowKey": { "alias": "rowKey"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "childrenKey": { "alias": "childrenKey"; "required": false; "isSignal": true; }; "treeColumn": { "alias": "treeColumn"; "required": false; "isSignal": true; }; "toggleRowLabel": { "alias": "toggleRowLabel"; "required": false; "isSignal": true; }; "collapsedGroups": { "alias": "collapsedGroups"; "required": false; "isSignal": true; }; "groupSummary": { "alias": "groupSummary"; "required": false; "isSignal": true; }; "showGroupCount": { "alias": "showGroupCount"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; "emptyLabel": { "alias": "emptyLabel"; "required": false; "isSignal": true; }; "sortLabel": { "alias": "sortLabel"; "required": false; "isSignal": true; }; "selectAllLabel": { "alias": "selectAllLabel"; "required": false; "isSignal": true; }; "selectRowLabel": { "alias": "selectRowLabel"; "required": false; "isSignal": true; }; "expandRowLabel": { "alias": "expandRowLabel"; "required": false; "isSignal": true; }; "selectGroupLabel": { "alias": "selectGroupLabel"; "required": false; "isSignal": true; }; "toggleGroupLabel": { "alias": "toggleGroupLabel"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "page": { "alias": "page"; "required": false; "isSignal": true; }; "totalItems": { "alias": "totalItems"; "required": false; "isSignal": true; }; "virtualScroll": { "alias": "virtualScroll"; "required": false; "isSignal": true; }; "rowHeight": { "alias": "rowHeight"; "required": false; "isSignal": true; }; "viewportHeight": { "alias": "viewportHeight"; "required": false; "isSignal": true; }; "overscan": { "alias": "overscan"; "required": false; "isSignal": true; }; }, { "columnOrder": "columnOrderChange"; "selection": "selectionChange"; "expanded": "expandedChange"; "collapsedGroups": "collapsedGroupsChange"; "sort": "sortChange"; "filterChange": "filterChange"; "page": "pageChange"; }, ["cellTemplates", "expandTpl", "groupTpl"], never, true, never>;
693
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrTable, "wr-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "items": { "alias": "items"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "reorderable": { "alias": "reorderable"; "required": false; "isSignal": true; }; "columnOrder": { "alias": "columnOrder"; "required": false; "isSignal": true; }; "rowSelection": { "alias": "rowSelection"; "required": false; "isSignal": true; }; "rowKey": { "alias": "rowKey"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "childrenKey": { "alias": "childrenKey"; "required": false; "isSignal": true; }; "treeColumn": { "alias": "treeColumn"; "required": false; "isSignal": true; }; "toggleRowLabel": { "alias": "toggleRowLabel"; "required": false; "isSignal": true; }; "collapsedGroups": { "alias": "collapsedGroups"; "required": false; "isSignal": true; }; "groupSummary": { "alias": "groupSummary"; "required": false; "isSignal": true; }; "showGroupCount": { "alias": "showGroupCount"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; "emptyLabel": { "alias": "emptyLabel"; "required": false; "isSignal": true; }; "sortLabel": { "alias": "sortLabel"; "required": false; "isSignal": true; }; "selectAllLabel": { "alias": "selectAllLabel"; "required": false; "isSignal": true; }; "selectRowLabel": { "alias": "selectRowLabel"; "required": false; "isSignal": true; }; "expandRowLabel": { "alias": "expandRowLabel"; "required": false; "isSignal": true; }; "selectGroupLabel": { "alias": "selectGroupLabel"; "required": false; "isSignal": true; }; "toggleGroupLabel": { "alias": "toggleGroupLabel"; "required": false; "isSignal": true; }; "loadingLabel": { "alias": "loadingLabel"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "page": { "alias": "page"; "required": false; "isSignal": true; }; "totalItems": { "alias": "totalItems"; "required": false; "isSignal": true; }; "virtualScroll": { "alias": "virtualScroll"; "required": false; "isSignal": true; }; "rowHeight": { "alias": "rowHeight"; "required": false; "isSignal": true; }; "viewportHeight": { "alias": "viewportHeight"; "required": false; "isSignal": true; }; "overscan": { "alias": "overscan"; "required": false; "isSignal": true; }; }, { "columnOrder": "columnOrderChange"; "selection": "selectionChange"; "expanded": "expandedChange"; "collapsedGroups": "collapsedGroupsChange"; "sort": "sortChange"; "filterChange": "filterChange"; "page": "pageChange"; }, ["cellTemplates", "expandTpl", "groupTpl"], never, true, never>;
685
694
  }
686
695
 
687
696
  /**
@@ -0,0 +1,158 @@
1
+ import { BaseHarnessFilters, ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
2
+
3
+ /**
4
+ * @license
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
8
+ */
9
+
10
+ /** Narrows which tilted element a harness query matches. */
11
+ interface WrTiltHarnessFilters extends BaseHarnessFilters {
12
+ /** Match only the ones carrying a glare overlay (`true`) or only the ones without. */
13
+ readonly glare?: boolean;
14
+ /** Match only elements at rest (`true`) or only ones holding a tilt (`false`). */
15
+ readonly flat?: boolean;
16
+ }
17
+ /** The two rotations a tilt writes, in degrees. */
18
+ interface WrTiltRotation {
19
+ /** Positive tips the top of the card toward the viewer. */
20
+ readonly rotateX: number;
21
+ /** Positive tips the right edge away. */
22
+ readonly rotateY: number;
23
+ }
24
+
25
+ /**
26
+ * @license
27
+ *
28
+ * Use of this source code is governed by an MIT-style license that can be
29
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
30
+ */
31
+
32
+ /**
33
+ * Test harness for `[wrTilt]` and for `<wr-tilt-card>`, which applies it as a host
34
+ * directive — one class, because they are one behaviour on two hosts.
35
+ *
36
+ * **The tilt itself needs a box, and a unit test has none.** The directive turns a
37
+ * pointer position into two rotations by dividing by `getBoundingClientRect()`, which
38
+ * is 0×0 in jsdom, so a move dispatched at a component that has not been given
39
+ * dimensions writes `rotateX(NaNdeg)`. {@link movePointerTo} is still here because the
40
+ * fix is one line in the SPEC — stub the host's rect, the way
41
+ * `projects/lib/image-cropper/testing` documents — and because the two things worth
42
+ * asserting on this component are only reachable through it: that a move writes a
43
+ * transform at all, and that {@link leave} takes it away again.
44
+ *
45
+ * **{@link isFlat} is the reduced-motion assertion.** `onMove` returns before touching
46
+ * the host when the user asked for less motion, so "no inline transform after a move"
47
+ * is the whole contract, and it is one of the few reduced-motion behaviours in the
48
+ * animation set that a test can actually see — most of the others live in a `@media`
49
+ * block. Provide the `WrPlatform` stub and assert this.
50
+ *
51
+ * There is no `getGlareOpacity()` or `isGlareVisible()`: the overlay's gradient, its
52
+ * blend mode and its fade all live in the stylesheet. And no `getMaxTilt()` /
53
+ * `getPerspectiveInput()` — those would echo an input back rather than report what the
54
+ * directive wrote; {@link getPerspective} and {@link getScale} read the transform the
55
+ * host is actually carrying, which is the value that can be wrong.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const card = await loader.getHarness(WrTiltHarness);
60
+ * expect(await card.isFlat()).toBe(true);
61
+ *
62
+ * await card.movePointerTo(150, 50); // the spec stubbed the rect first
63
+ * expect((await card.getTilt())?.rotateY).toBeGreaterThan(0);
64
+ *
65
+ * await card.leave();
66
+ * expect(await card.isFlat()).toBe(true);
67
+ * ```
68
+ *
69
+ * @see https://ngwr.dev/guides/testing
70
+ */
71
+ declare class WrTiltHarness extends ComponentHarness {
72
+ /** The directive on a consumer's element, and the card that hosts it. */
73
+ static hostSelector: string;
74
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
75
+ static with(options?: WrTiltHarnessFilters): HarnessPredicate<WrTiltHarness>;
76
+ /**
77
+ * Whether the host is at rest — no inline transform at all.
78
+ *
79
+ * True before the first move, after {@link leave}, and for the whole life of a card
80
+ * whose user asked for less motion. It is the difference between "flat" and
81
+ * "rotated by zero": the directive CLEARS the property rather than writing an
82
+ * identity transform, so an empty string is the state and `rotateX(0deg)` would be
83
+ * a component that tilted and came back the long way.
84
+ */
85
+ isFlat(): Promise<boolean>;
86
+ /** The inline `transform` exactly as written, or `''` at rest. */
87
+ getTransform(): Promise<string>;
88
+ /**
89
+ * The two rotations the host is holding, in degrees, or `null` while it is flat.
90
+ *
91
+ * Parsed from the transform rather than recomputed: the harness has no more access
92
+ * to the pointer maths than a consumer does, and the string is what the compositor
93
+ * will read.
94
+ */
95
+ getTilt(): Promise<WrTiltRotation | null>;
96
+ /** The perspective the transform was written with, in pixels, or `null` while flat. */
97
+ getPerspective(): Promise<number | null>;
98
+ /** The scale the transform was written with, or `null` while flat. */
99
+ getScale(): Promise<number | null>;
100
+ /**
101
+ * Whether the host was set up to hold children in 3-D.
102
+ *
103
+ * Written once in the constructor, so it is true from first render and stays true —
104
+ * which makes it the cheapest evidence that the directive is applied at all, on a
105
+ * host where every other read is `''` until something moves.
106
+ */
107
+ isPreserve3d(): Promise<boolean>;
108
+ /** Whether the glare overlay is installed (`[glare]`). */
109
+ hasGlare(): Promise<boolean>;
110
+ /**
111
+ * Whether the glare is kept out of the accessibility tree.
112
+ *
113
+ * It is a decorative highlight with no text, so a screen reader must not stop on
114
+ * it. Asserted rather than assumed because the overlay is created imperatively —
115
+ * there is no template line for a reader to check.
116
+ */
117
+ isGlareDecorative(): Promise<boolean>;
118
+ /**
119
+ * Where the glare is pointing, as the two percentages the directive writes, or
120
+ * `null` when there is no glare or nothing has moved yet.
121
+ *
122
+ * Cleared when the overlay is removed: a card that has given `[glare]` back should
123
+ * not look like it is still tracking a pointer.
124
+ */
125
+ getGlarePosition(): Promise<{
126
+ x: string;
127
+ y: string;
128
+ } | null>;
129
+ /**
130
+ * Move a pointer over the host.
131
+ *
132
+ * **Stub the host's rect in your spec first.** The directive divides the pointer
133
+ * offset by the host's width and height, and jsdom measures every element at 0×0,
134
+ * so without a box the transform comes out as `NaN` — which is not a failure the
135
+ * component would ever produce in a browser. One line does it:
136
+ *
137
+ * ```ts
138
+ * host.getBoundingClientRect = () => ({ left: 0, top: 0, width: 200, height: 100 }) as DOMRect;
139
+ * ```
140
+ */
141
+ movePointerTo(clientX: number, clientY: number): Promise<void>;
142
+ /** Take the pointer off the host — which clears the transform and settles it flat. */
143
+ leave(): Promise<void>;
144
+ /** The projected content's text, trimmed. */
145
+ getContentText(): Promise<string>;
146
+ /**
147
+ * One inline declaration, read off the `style` ATTRIBUTE.
148
+ *
149
+ * Not `getCssValue`, which is `getComputedStyle`: that resolves the stylesheet as
150
+ * well, so `transform` would come back as the resolved matrix in a browser and
151
+ * `--wr-tilt-glare-x` would answer with the sheet's own default. The attribute holds
152
+ * what the directive wrote and nothing else, which is the only thing worth pinning.
153
+ */
154
+ private inlineStyle;
155
+ }
156
+
157
+ export { WrTiltHarness };
158
+ export type { WrTiltHarnessFilters, WrTiltRotation };
@@ -25,6 +25,8 @@ declare class WrTilt {
25
25
  private readonly platform;
26
26
  private readonly isBrowser;
27
27
  private glareEl;
28
+ private priorOverflow;
29
+ private priorPosition;
28
30
  constructor();
29
31
  /** @internal */
30
32
  protected onMove(event: PointerEvent): void;