ngwr 11.1.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 (205) hide show
  1. package/circular-text/styles/_index.scss +11 -0
  2. package/command-palette/styles/_index.scss +12 -0
  3. package/fesm2022/ngwr-action-sheet-testing.mjs +313 -0
  4. package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -0
  5. package/fesm2022/ngwr-bar-chart-testing.mjs +89 -0
  6. package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -0
  7. package/fesm2022/ngwr-blur-text-testing.mjs +203 -0
  8. package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -0
  9. package/fesm2022/ngwr-blur-text.mjs +15 -1
  10. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  11. package/fesm2022/ngwr-border-glow-testing.mjs +325 -0
  12. package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -0
  13. package/fesm2022/ngwr-calendar-heatmap-testing.mjs +95 -0
  14. package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -0
  15. package/fesm2022/ngwr-calendar-testing.mjs +368 -0
  16. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -0
  17. package/fesm2022/ngwr-calendar.mjs +43 -3
  18. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  19. package/fesm2022/ngwr-cascader.mjs +12 -0
  20. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  21. package/fesm2022/ngwr-checkbox.mjs +8 -0
  22. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  23. package/fesm2022/ngwr-circular-text-testing.mjs +230 -0
  24. package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -0
  25. package/fesm2022/ngwr-circular-text.mjs +2 -2
  26. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  27. package/fesm2022/ngwr-color-picker-testing.mjs +432 -0
  28. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -0
  29. package/fesm2022/ngwr-color-picker.mjs +29 -3
  30. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  31. package/fesm2022/ngwr-context-menu.mjs +25 -5
  32. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  33. package/fesm2022/ngwr-date-picker-testing.mjs +10 -79
  34. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  35. package/fesm2022/ngwr-decrypt-text-testing.mjs +261 -0
  36. package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -0
  37. package/fesm2022/ngwr-decrypt-text.mjs +6 -1
  38. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
  39. package/fesm2022/ngwr-donut-chart-testing.mjs +93 -0
  40. package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -0
  41. package/fesm2022/ngwr-drawer.mjs +36 -7
  42. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  43. package/fesm2022/ngwr-dropdown.mjs +32 -2
  44. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  45. package/fesm2022/ngwr-event-calendar-testing.mjs +446 -0
  46. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -0
  47. package/fesm2022/ngwr-event-calendar.mjs +43 -18
  48. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  49. package/fesm2022/ngwr-falling-text-testing.mjs +218 -0
  50. package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -0
  51. package/fesm2022/ngwr-fuzzy-text-testing.mjs +134 -0
  52. package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -0
  53. package/fesm2022/ngwr-gauge-testing.mjs +86 -0
  54. package/fesm2022/ngwr-gauge-testing.mjs.map +1 -0
  55. package/fesm2022/ngwr-glitch-text-testing.mjs +181 -0
  56. package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -0
  57. package/fesm2022/ngwr-gradient-text-testing.mjs +223 -0
  58. package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -0
  59. package/fesm2022/ngwr-i18n-en.mjs +14 -0
  60. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  61. package/fesm2022/ngwr-i18n-ru.mjs +14 -0
  62. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  63. package/fesm2022/ngwr-image-cropper-testing.mjs +156 -0
  64. package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -0
  65. package/fesm2022/ngwr-image-cropper.mjs +10 -3
  66. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  67. package/fesm2022/ngwr-input-number.mjs +73 -6
  68. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  69. package/fesm2022/ngwr-knob-testing.mjs +220 -0
  70. package/fesm2022/ngwr-knob-testing.mjs.map +1 -0
  71. package/fesm2022/ngwr-lightbox-testing.mjs +223 -0
  72. package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -0
  73. package/fesm2022/ngwr-lightbox.mjs +9 -2
  74. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  75. package/fesm2022/ngwr-line-chart-testing.mjs +119 -0
  76. package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -0
  77. package/fesm2022/ngwr-marquee-testing.mjs +336 -0
  78. package/fesm2022/ngwr-marquee-testing.mjs.map +1 -0
  79. package/fesm2022/ngwr-meter-group-testing.mjs +80 -0
  80. package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -0
  81. package/fesm2022/ngwr-overlay.mjs +28 -1
  82. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  83. package/fesm2022/ngwr-popover.mjs +14 -0
  84. package/fesm2022/ngwr-popover.mjs.map +1 -1
  85. package/fesm2022/ngwr-rotating-text-testing.mjs +220 -0
  86. package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -0
  87. package/fesm2022/ngwr-rotating-text.mjs +2 -2
  88. package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
  89. package/fesm2022/ngwr-select.mjs +70 -11
  90. package/fesm2022/ngwr-select.mjs.map +1 -1
  91. package/fesm2022/ngwr-shiny-text-testing.mjs +237 -0
  92. package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -0
  93. package/fesm2022/ngwr-slider.mjs +44 -6
  94. package/fesm2022/ngwr-slider.mjs.map +1 -1
  95. package/fesm2022/ngwr-sparkline-testing.mjs +76 -0
  96. package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -0
  97. package/fesm2022/ngwr-speed-dial-testing.mjs +279 -0
  98. package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -0
  99. package/fesm2022/ngwr-splash-cursor-testing.mjs +89 -0
  100. package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -0
  101. package/fesm2022/ngwr-split-text-testing.mjs +224 -0
  102. package/fesm2022/ngwr-split-text-testing.mjs.map +1 -0
  103. package/fesm2022/ngwr-split-text.mjs +15 -1
  104. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  105. package/fesm2022/ngwr-splitter-testing.mjs +245 -0
  106. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -0
  107. package/fesm2022/ngwr-spotlight-card-testing.mjs +368 -0
  108. package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -0
  109. package/fesm2022/ngwr-star-border-testing.mjs +172 -0
  110. package/fesm2022/ngwr-star-border-testing.mjs.map +1 -0
  111. package/fesm2022/ngwr-table.mjs +13 -3
  112. package/fesm2022/ngwr-table.mjs.map +1 -1
  113. package/fesm2022/ngwr-tabs.mjs +15 -1
  114. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  115. package/fesm2022/ngwr-tilt-card-testing.mjs +190 -0
  116. package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -0
  117. package/fesm2022/ngwr-tilt-card.mjs +19 -1
  118. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  119. package/fesm2022/ngwr-toast.mjs +36 -7
  120. package/fesm2022/ngwr-toast.mjs.map +1 -1
  121. package/fesm2022/ngwr-tour-testing.mjs +173 -0
  122. package/fesm2022/ngwr-tour-testing.mjs.map +1 -0
  123. package/fesm2022/ngwr-tour.mjs +2 -2
  124. package/fesm2022/ngwr-tour.mjs.map +1 -1
  125. package/fesm2022/ngwr-tree.mjs +10 -3
  126. package/fesm2022/ngwr-tree.mjs.map +1 -1
  127. package/fesm2022/ngwr-typewriter-testing.mjs +228 -0
  128. package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -0
  129. package/fesm2022/ngwr-version.mjs +1 -1
  130. package/fesm2022/ngwr-version.mjs.map +1 -1
  131. package/fesm2022/ngwr-waves-testing.mjs +128 -0
  132. package/fesm2022/ngwr-waves-testing.mjs.map +1 -0
  133. package/fesm2022/ngwr-window-testing.mjs +361 -0
  134. package/fesm2022/ngwr-window-testing.mjs.map +1 -0
  135. package/fesm2022/ngwr-window.mjs +84 -10
  136. package/fesm2022/ngwr-window.mjs.map +1 -1
  137. package/i18n/en.json +16 -2
  138. package/i18n/ru.json +16 -2
  139. package/llms-full.txt +147 -0
  140. package/llms.txt +27 -8
  141. package/mcp/providers.js +29 -0
  142. package/mcp/tools.js +1 -29
  143. package/package.json +145 -1
  144. package/skills/ngwr/SKILL.md +115 -0
  145. package/skills/ngwr/references/catalog.md +212 -0
  146. package/skills/ngwr/references/setup.md +73 -0
  147. package/types/ngwr-action-sheet-testing.d.ts +260 -0
  148. package/types/ngwr-bar-chart-testing.d.ts +77 -0
  149. package/types/ngwr-blur-text-testing.d.ts +180 -0
  150. package/types/ngwr-blur-text.d.ts +12 -0
  151. package/types/ngwr-border-glow-testing.d.ts +219 -0
  152. package/types/ngwr-calendar-heatmap-testing.d.ts +79 -0
  153. package/types/ngwr-calendar-testing.d.ts +277 -0
  154. package/types/ngwr-calendar.d.ts +10 -0
  155. package/types/ngwr-checkbox.d.ts +13 -0
  156. package/types/ngwr-circular-text-testing.d.ts +196 -0
  157. package/types/ngwr-color-picker-testing.d.ts +291 -0
  158. package/types/ngwr-color-picker.d.ts +13 -1
  159. package/types/ngwr-context-menu.d.ts +15 -1
  160. package/types/ngwr-date-picker-testing.d.ts +6 -66
  161. package/types/ngwr-decrypt-text-testing.d.ts +222 -0
  162. package/types/ngwr-donut-chart-testing.d.ts +79 -0
  163. package/types/ngwr-drawer.d.ts +12 -0
  164. package/types/ngwr-dropdown.d.ts +9 -0
  165. package/types/ngwr-event-calendar-testing.d.ts +301 -0
  166. package/types/ngwr-event-calendar.d.ts +9 -6
  167. package/types/ngwr-falling-text-testing.d.ts +186 -0
  168. package/types/ngwr-fuzzy-text-testing.d.ts +130 -0
  169. package/types/ngwr-gauge-testing.d.ts +71 -0
  170. package/types/ngwr-glitch-text-testing.d.ts +172 -0
  171. package/types/ngwr-gradient-text-testing.d.ts +173 -0
  172. package/types/ngwr-image-cropper-testing.d.ts +134 -0
  173. package/types/ngwr-image-cropper.d.ts +1 -0
  174. package/types/ngwr-input-number.d.ts +33 -1
  175. package/types/ngwr-knob-testing.d.ts +150 -0
  176. package/types/ngwr-lightbox-testing.d.ts +154 -0
  177. package/types/ngwr-lightbox.d.ts +6 -0
  178. package/types/ngwr-line-chart-testing.d.ts +93 -0
  179. package/types/ngwr-marquee-testing.d.ts +280 -0
  180. package/types/ngwr-meter-group-testing.d.ts +67 -0
  181. package/types/ngwr-overlay.d.ts +19 -1
  182. package/types/ngwr-rotating-text-testing.d.ts +191 -0
  183. package/types/ngwr-select.d.ts +32 -0
  184. package/types/ngwr-shiny-text-testing.d.ts +165 -0
  185. package/types/ngwr-slider.d.ts +15 -0
  186. package/types/ngwr-sparkline-testing.d.ts +69 -0
  187. package/types/ngwr-speed-dial-testing.d.ts +195 -0
  188. package/types/ngwr-splash-cursor-testing.d.ts +95 -0
  189. package/types/ngwr-split-text-testing.d.ts +209 -0
  190. package/types/ngwr-split-text.d.ts +12 -0
  191. package/types/ngwr-splitter-testing.d.ts +174 -0
  192. package/types/ngwr-spotlight-card-testing.d.ts +303 -0
  193. package/types/ngwr-star-border-testing.d.ts +135 -0
  194. package/types/ngwr-table.d.ts +10 -1
  195. package/types/ngwr-tilt-card-testing.d.ts +158 -0
  196. package/types/ngwr-tilt-card.d.ts +2 -0
  197. package/types/ngwr-toast.d.ts +14 -5
  198. package/types/ngwr-tour-testing.d.ts +143 -0
  199. package/types/ngwr-tree.d.ts +7 -0
  200. package/types/ngwr-typewriter-testing.d.ts +201 -0
  201. package/types/ngwr-version.d.ts +1 -1
  202. package/types/ngwr-waves-testing.d.ts +121 -0
  203. package/types/ngwr-window-testing.d.ts +263 -0
  204. package/types/ngwr-window.d.ts +27 -10
  205. package/window/styles/_index.scss +44 -16
@@ -0,0 +1,195 @@
1
+ import { BaseHarnessFilters, ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
2
+ import { WrSpeedDialDirection } from 'ngwr/speed-dial';
3
+
4
+ /**
5
+ * @license
6
+ *
7
+ * Use of this source code is governed by an MIT-style license that can be
8
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
9
+ */
10
+
11
+ /** Narrows which `<wr-speed-dial>` a harness query matches. */
12
+ interface WrSpeedDialHarnessFilters extends BaseHarnessFilters {
13
+ /** Match the trigger's accessible name — a string is an exact match, a RegExp is tested. */
14
+ readonly triggerLabel?: string | RegExp;
15
+ /** Match the direction the actions fan out. */
16
+ readonly direction?: WrSpeedDialDirection;
17
+ /** Match only open (`true`) or only closed (`false`) dials. */
18
+ readonly open?: boolean;
19
+ /** Match only enabled (`false`) or only disabled (`true`) dials. */
20
+ readonly disabled?: boolean;
21
+ }
22
+ /** Narrows which action inside a speed dial a harness query matches. */
23
+ interface WrSpeedDialActionHarnessFilters extends BaseHarnessFilters {
24
+ /** Match the action's accessible name — a string is an exact match, a RegExp is tested. */
25
+ readonly label?: string | RegExp;
26
+ /** Match only enabled (`false`) or only disabled (`true`) actions. */
27
+ readonly disabled?: boolean;
28
+ }
29
+
30
+ /**
31
+ * @license
32
+ *
33
+ * Use of this source code is governed by an MIT-style license that can be
34
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
35
+ */
36
+
37
+ /**
38
+ * Test harness for one action inside a {@link WrSpeedDialHarness}.
39
+ *
40
+ * The label is read from `aria-label` rather than from the button's text, and that
41
+ * is the whole point of the control: an action renders an ICON, or — with no icon —
42
+ * the first glyph of its label. Its visible text is therefore `"S"` for "Share", or
43
+ * nothing at all. {@link getInitial} answers what is drawn; {@link getLabel} answers
44
+ * what it is.
45
+ *
46
+ * @see https://ngwr.dev/guides/testing
47
+ */
48
+ declare class WrSpeedDialActionHarness extends ComponentHarness {
49
+ /** The action button. `role="menuitem"` sits on this element too. */
50
+ static hostSelector: string;
51
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
52
+ static with(options?: WrSpeedDialActionHarnessFilters): HarnessPredicate<WrSpeedDialActionHarness>;
53
+ /** The action's accessible name — what a screen reader reads, and what `title` shows. */
54
+ getLabel(): Promise<string | null>;
55
+ /** The role the action announces — `menuitem`. */
56
+ getRole(): Promise<string | null>;
57
+ /** Whether the action draws an icon rather than a letter. */
58
+ hasIcon(): Promise<boolean>;
59
+ /** The icon's registered name, from `wr-icon`'s reflected `data-icon`, or `null`. */
60
+ getIconName(): Promise<string | null>;
61
+ /**
62
+ * The single glyph drawn in place of a missing icon, or `null` when there is one.
63
+ *
64
+ * A GLYPH, not a character: the component splits the label by code point, so an
65
+ * emoji label draws the whole emoji rather than the broken half of a surrogate pair.
66
+ */
67
+ getInitial(): Promise<string | null>;
68
+ /**
69
+ * Whether the action refuses interaction.
70
+ *
71
+ * Every action follows the DIAL's `disabled` — there is no per-action flag — so
72
+ * this is really "is the dial disabled", asked at the action. Worth having anyway:
73
+ * it is what the DOM and the CDK consult before delivering a click.
74
+ */
75
+ isDisabled(): Promise<boolean>;
76
+ /** Pick this action — emitting `pick` and closing the dial. */
77
+ click(): Promise<void>;
78
+ /** Move keyboard focus to the action. */
79
+ focus(): Promise<void>;
80
+ /** Whether the action currently holds focus. */
81
+ isFocused(): Promise<boolean>;
82
+ }
83
+
84
+ /**
85
+ * @license
86
+ *
87
+ * Use of this source code is governed by an MIT-style license that can be
88
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
89
+ */
90
+
91
+ /**
92
+ * Test harness for `<wr-speed-dial>` — the floating trigger and the actions that fan
93
+ * out of it.
94
+ *
95
+ * **The actions never leave the DOM.** They are hidden with `visibility`, which is
96
+ * what actually takes them out of the tab order and the accessibility tree —
97
+ * `opacity: 0` alone would leave them focusable — and CSS is exactly what a unit
98
+ * test does not have. So a closed dial's buttons are queryable, clickable and
99
+ * readable from a spec while being unreachable in a browser. {@link getActions} and
100
+ * {@link pick} therefore REFUSE while the dial is closed rather than answering with
101
+ * elements nobody can see; open it first, which is what a user has to do too.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * const dial = await loader.getHarness(WrSpeedDialHarness);
106
+ *
107
+ * await dial.open();
108
+ * expect(await dial.getActionLabels()).toEqual(['Share', 'Copy link']);
109
+ *
110
+ * await dial.pick({ label: 'Share' });
111
+ * expect(await dial.isOpen()).toBe(false);
112
+ * ```
113
+ *
114
+ * @see https://ngwr.dev/guides/testing
115
+ */
116
+ declare class WrSpeedDialHarness extends ComponentHarness {
117
+ static hostSelector: string;
118
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
119
+ static with(options?: WrSpeedDialHarnessFilters): HarnessPredicate<WrSpeedDialHarness>;
120
+ private readonly trigger;
121
+ private readonly menu;
122
+ /** Whether the dial is fanned out, from the trigger's `aria-expanded`. */
123
+ isOpen(): Promise<boolean>;
124
+ /** The direction the actions travel, from the host modifier. */
125
+ getDirection(): Promise<WrSpeedDialDirection>;
126
+ /**
127
+ * The trigger's accessible name.
128
+ *
129
+ * The trigger is icon-only, so this is its ONLY name — there is no text to fall
130
+ * back on, and a dial that loses it is announced as an unnamed button.
131
+ */
132
+ getTriggerLabel(): Promise<string | null>;
133
+ /** The trigger icon's registered name, from `wr-icon`'s reflected `data-icon`. */
134
+ getTriggerIcon(): Promise<string | null>;
135
+ /** Whether the dial refuses interaction, from the trigger's own `disabled` property. */
136
+ isDisabled(): Promise<boolean>;
137
+ /** Whether the host is padded for the device's safe area (`safeArea`). */
138
+ hasSafeArea(): Promise<boolean>;
139
+ /** The role the action list announces — `menu`. */
140
+ getMenuRole(): Promise<string | null>;
141
+ /**
142
+ * Whether the trigger's `aria-controls` names THIS dial's menu, and nothing else on
143
+ * the page.
144
+ *
145
+ * Both halves matter. The id is resolved inside the host first, so a trigger
146
+ * pointing at a SIBLING dial's menu — which resolves perfectly well from the
147
+ * document — fails here. Then it is counted across the document, because two menus
148
+ * answering to one id hand every reference to whichever comes first.
149
+ */
150
+ isMenuBound(): Promise<boolean>;
151
+ /**
152
+ * The actions, in DOM order.
153
+ *
154
+ * Throws while the dial is closed. The buttons are still in the DOM then — they
155
+ * have to be, for the fan-out to animate — and jsdom applies no `visibility`, so
156
+ * they would come back looking perfectly reachable when a real user cannot tab to
157
+ * one. A spec that read them closed would pass on a dial that never opens.
158
+ */
159
+ getActions(filters?: WrSpeedDialActionHarnessFilters): Promise<WrSpeedDialActionHarness[]>;
160
+ /** The accessible names of the actions, in DOM order. Throws while closed. */
161
+ getActionLabels(): Promise<(string | null)[]>;
162
+ /** How many actions the dial holds — readable while closed, since it is not a reach. */
163
+ getActionCount(): Promise<number>;
164
+ /** Open the dial. An already-open one is left alone; a disabled one throws. */
165
+ open(): Promise<void>;
166
+ /** Close the dial. An already-closed one is left alone. */
167
+ close(): Promise<void>;
168
+ /** Click the trigger once, whichever way that takes it. */
169
+ toggle(): Promise<void>;
170
+ /**
171
+ * Pick the first action matching the filters — emitting `pick` and closing the dial.
172
+ *
173
+ * Opens first if it has to, the way `WrDropdownHarness.clickItem` does: a spec that
174
+ * says "pick Share" means the whole gesture. Throws when nothing matches, naming
175
+ * what the dial does offer.
176
+ */
177
+ pick(filters: WrSpeedDialActionHarnessFilters): Promise<void>;
178
+ /**
179
+ * Press Escape — closing the dial and putting focus back on the trigger.
180
+ *
181
+ * Sent to the host, which is where the component listens. The focus return is the
182
+ * half worth asserting: `role="menu"` promises a way out, and the actions are
183
+ * ordinary tab stops, so without it a keyboard user has to walk through every
184
+ * action to leave.
185
+ */
186
+ sendEscape(): Promise<void>;
187
+ /** Move keyboard focus to the trigger. */
188
+ focusTrigger(): Promise<void>;
189
+ /** Whether the trigger currently holds focus. */
190
+ isTriggerFocused(): Promise<boolean>;
191
+ private requireOpen;
192
+ }
193
+
194
+ export { WrSpeedDialActionHarness, WrSpeedDialHarness };
195
+ export type { WrSpeedDialActionHarnessFilters, WrSpeedDialHarnessFilters };
@@ -0,0 +1,95 @@
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-splash-cursor>` a harness query matches. */
11
+ interface WrSplashCursorHarnessFilters extends BaseHarnessFilters {
12
+ /**
13
+ * Match only the page-wide overlay (`true`) or only a box-contained effect (`false`).
14
+ * The component has no name and no content, so its layout mode is the only thing that
15
+ * distinguishes two of them — see `WrSplashCursorHarness.isFullscreen`.
16
+ */
17
+ readonly fullscreen?: boolean;
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-splash-cursor>` — a WebGL fluid that splashes dye under the
29
+ * pointer.
30
+ *
31
+ * **This is a deliberately small surface, and the reason is the component.** The
32
+ * simulation is GL programs and framebuffers driven by `window` pointer listeners; the
33
+ * layout is `position: fixed`, `inset: 0`, `z-index: 50` and a `100dvh` canvas, all of
34
+ * it in the stylesheet. A unit test gets no WebGL context — the simulation is never
35
+ * constructed — and loads no stylesheet, so asking `getCssValue('position')` would
36
+ * answer `static` about a component whose whole layout contract is `fixed`. Three
37
+ * questions survive that, and they are the three below.
38
+ *
39
+ * There is no `isSimulating()`, `getSplatCount()` or `splat()`: the simulation returns
40
+ * `null` the moment the context is refused, so every one of those would report on
41
+ * something that was never built. No `moveMouse()` either — the listeners live on
42
+ * `window` and are only attached once a context exists, so a dispatched move reaches
43
+ * nothing. And no `isReducedMotion()` or `hasWebGL()`: the component renders
44
+ * byte-identical DOM whether it is running, standing down for reduced motion, or on a
45
+ * machine with no WebGL at all, so any answer would be the same for a working component
46
+ * and a broken one.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * const splash = await loader.getHarness(WrSplashCursorHarness);
51
+ *
52
+ * expect(await splash.isFullscreen()).toBe(true);
53
+ * expect(await splash.isDecorative()).toBe(true);
54
+ * ```
55
+ *
56
+ * @see https://ngwr.dev/guides/testing
57
+ */
58
+ declare class WrSplashCursorHarness extends ComponentHarness {
59
+ static hostSelector: string;
60
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
61
+ static with(options?: WrSplashCursorHarnessFilters): HarnessPredicate<WrSplashCursorHarness>;
62
+ /**
63
+ * Whether the effect covers the whole viewport, rather than filling the nearest
64
+ * positioned ancestor.
65
+ *
66
+ * The component's one behavioural switch: fullscreen is a fixed, click-through
67
+ * overlay above the page, contained is an absolutely positioned box whose walls the
68
+ * dye collides with. Note what is being read — there is no `--fullscreen` class, only
69
+ * a `--contained` one written from a NEGATED binding, so this method answers the
70
+ * absence of a class. That inversion is exactly the kind that gets flipped in a
71
+ * refactor and looks fine on a page where the effect is full-viewport anyway, which
72
+ * is the whole reason the method exists.
73
+ */
74
+ isFullscreen(): Promise<boolean>;
75
+ /**
76
+ * Whether the effect is hidden from assistive tech.
77
+ *
78
+ * It has no text and nothing to say, and at full size it sits in front of the entire
79
+ * page — so losing this puts a nameless graphic over everything a screen reader is
80
+ * trying to get through.
81
+ */
82
+ isDecorative(): Promise<boolean>;
83
+ /**
84
+ * Whether the drawing surface is in the markup.
85
+ *
86
+ * It is there in all three states where nothing is ever drawn on it — prerendered on
87
+ * the server, WebGL refused, or reduced motion — because the documented contract is
88
+ * that the component renders nothing VISIBLE and stops, never that it renders no
89
+ * element and never that it throws on a null context.
90
+ */
91
+ hasCanvas(): Promise<boolean>;
92
+ }
93
+
94
+ export { WrSplashCursorHarness };
95
+ export type { WrSplashCursorHarnessFilters };
@@ -0,0 +1,209 @@
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
+ /**
11
+ * The alignments `<wr-split-text>` takes.
12
+ *
13
+ * Restated here because the component declares the union inline on its `textAlign`
14
+ * input and exports no name for it — a filter typed `string` would take a value the
15
+ * component cannot produce and then quietly match nothing.
16
+ */
17
+ type WrSplitTextHarnessAlign = 'left' | 'center' | 'right' | 'justify';
18
+ /** Narrows which `<wr-split-text>` a harness query matches. */
19
+ interface WrSplitTextHarnessFilters extends BaseHarnessFilters {
20
+ /**
21
+ * Match the string the component announces — the readable copy, not the pieces.
22
+ * A string is an exact match, a RegExp is tested.
23
+ *
24
+ * Against the accessible copy rather than the host's own text, which holds the
25
+ * string twice: once readably and once as a span per character.
26
+ */
27
+ readonly text?: string | RegExp;
28
+ /**
29
+ * Match on how many animated pieces the split produced — one per character, or one
30
+ * per word. The only number about the animation that reaches the DOM.
31
+ */
32
+ readonly pieceCount?: number;
33
+ /**
34
+ * Match on the alignment the host was given.
35
+ *
36
+ * The most useful of the three in practice: a page usually carries several of these
37
+ * and alignment is what tells a centred hero from a left-aligned caption, whereas
38
+ * their text is the thing a spec is usually trying not to hard-code.
39
+ */
40
+ readonly textAlign?: WrSplitTextHarnessAlign;
41
+ }
42
+
43
+ /**
44
+ * @license
45
+ *
46
+ * Use of this source code is governed by an MIT-style license that can be
47
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
48
+ */
49
+
50
+ /**
51
+ * Test harness for `<wr-split-text>`.
52
+ *
53
+ * **The tween is unreadable from a test, and the absent methods are absent for that
54
+ * reason.** Each piece is animated from a `from` motion state to a `to` one through the
55
+ * Web Animations API, started by an `IntersectionObserver`. A unit test has no layout,
56
+ * no compositor and no WAAPI, so there is no `getOpacity()` and no `getTransform()`:
57
+ * both would report the frozen staged value in jsdom and a value the compositor is not
58
+ * painting in a browser. There is no `getFromState()` / `getToState()` either — those
59
+ * objects only reach the DOM as staged inline styles, on the one path that throws here,
60
+ * so reporting them would mean re-deriving an input from a string the component may
61
+ * never have written. No `isAnimating()`, no `whenAnimationComplete()`: nothing in the
62
+ * document says a tween is running, and completion is the `animationComplete` OUTPUT,
63
+ * which the host binds. No `enterViewport()` / `trigger()`, because an observer is not
64
+ * reachable from a `TestElement` — and observation here is additionally gated behind
65
+ * `document.fonts.ready`, which jsdom does not have, so a `waitForFonts()` would resolve
66
+ * instantly and prove nothing about the gating it claimed to test. Nothing measures
67
+ * either: `getLineCount()` / `didItWrap()` would need layout (and `lines` is not a split
68
+ * this port supports), and the `overflow: hidden` mask with its descender compensation
69
+ * is a stylesheet, which a test runs none of.
70
+ *
71
+ * **What is real is the split, the accessible layer and the alignment.** The string is
72
+ * carried once in a visually-hidden span and every drawn piece is `aria-hidden`, so
73
+ * {@link getAccessibleText} is what is heard while {@link getPieces} is what is drawn —
74
+ * two questions, and a spec asserting only the second would pass on a component that
75
+ * spells the text out letter by letter. {@link getRenderedText} closes the loop by
76
+ * putting the pieces and the spaces back together: the split has to be lossless, and
77
+ * that comparison is what catches a dropped space or a halved surrogate pair. Whether
78
+ * the hidden copy is genuinely off-screen is CSS, so nothing here claims to know.
79
+ *
80
+ * **{@link getTextAlign} is the one input this component reflects**, written straight
81
+ * onto the host as an inline style — an honest read with no layout involved, unlike
82
+ * anything else about how the text is arranged. And {@link hasStagedMotion} is the one
83
+ * fact about the tween worth pinning: that the pieces were taken over, not that anything
84
+ * is moving.
85
+ *
86
+ * Driving the reveal is the spec's job — jsdom supplies neither half of it, and the font
87
+ * gate defers observation by a microtask:
88
+ *
89
+ * ```ts
90
+ * // Before TestBed.createComponent — keep the callbacks so the spec can fire them.
91
+ * vi.stubGlobal('IntersectionObserver', StubObserver);
92
+ * // Otherwise `el.animate(...)` throws mid-stagger and leaves the DOM half-staged.
93
+ * Reflect.set(Element.prototype, 'animate', () => ({ cancel: () => undefined, onfinish: null }));
94
+ * // …and always settle before tearing down: observation is queued off a promise, and
95
+ * // landing after destroy() registers a teardown on a dead DestroyRef (NG0911).
96
+ * await fixture.whenStable();
97
+ * ```
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * const hero = await loader.getHarness(WrSplitTextHarness.with({ textAlign: 'center' }));
102
+ *
103
+ * expect(await hero.getPieces()).toEqual(['H', 'e', 'l', 'l', 'o']);
104
+ * expect(await hero.getRenderedText()).toBe(await hero.getAccessibleText());
105
+ * expect(await hero.isTextHidden()).toBe(true);
106
+ * ```
107
+ *
108
+ * @see https://ngwr.dev/guides/testing
109
+ */
110
+ declare class WrSplitTextHarness extends ComponentHarness {
111
+ static hostSelector: string;
112
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
113
+ static with(options?: WrSplitTextHarnessFilters): HarnessPredicate<WrSplitTextHarness>;
114
+ private readonly pieces;
115
+ private readonly spaces;
116
+ /**
117
+ * The whole string, as the one readable copy holds it.
118
+ *
119
+ * All a screen reader gets, since every drawn piece is out of the tree — lose this
120
+ * span and the component becomes a nameless pile of hidden spans, with no other
121
+ * symptom anywhere in the DOM.
122
+ *
123
+ * Untrimmed, deliberately: leading and trailing whitespace is content the split has to
124
+ * carry, and {@link getRenderedText} is meant to compare equal to this. A trimmed read
125
+ * on one side of that comparison would forgive the case it exists to catch.
126
+ */
127
+ getAccessibleText(): Promise<string>;
128
+ /**
129
+ * The animated pieces, in document order — one per character, or one per word.
130
+ *
131
+ * The split the stagger runs over: `['H', 'i']` at the default `splitType="chars"`
132
+ * against `['Hi', 'there']` for `"words"`, and `['a', '🚀']` for a surrogate pair the
133
+ * code-point split kept whole instead of halving into two broken glyphs. Whitespace is
134
+ * not here — it is drawn separately and never staggered, so see {@link getSpaceCount}.
135
+ */
136
+ getPieces(): Promise<string[]>;
137
+ /**
138
+ * How many pieces the stagger runs over.
139
+ *
140
+ * `delay` × this is the real length of the effect, and the only number about the
141
+ * animation present in the document.
142
+ */
143
+ getPieceCount(): Promise<number>;
144
+ /**
145
+ * How many whitespace runs sit between the pieces.
146
+ *
147
+ * Its own method because whitespace is deliberately excluded from the animated set:
148
+ * reclassifying spaces as pieces would leave {@link getRenderedText} identical, change
149
+ * the stagger count and the rhythm of the reveal, and show up nowhere else.
150
+ */
151
+ getSpaceCount(): Promise<number>;
152
+ /**
153
+ * Everything the component drew, pieces and spaces together, in document order.
154
+ *
155
+ * The assertion this exists for is `getRenderedText() === getAccessibleText()`: the
156
+ * split must be lossless. A swallowed space, a re-split that dropped a character or a
157
+ * surrogate pair cut in half all leave the accessible copy intact and are invisible
158
+ * from anywhere else.
159
+ */
160
+ getRenderedText(): Promise<string>;
161
+ /**
162
+ * Whether every drawn piece is out of the accessibility tree.
163
+ *
164
+ * The defect this component exists to avoid is a screen reader reading
165
+ * `H. e. l. l. o.`, and one piece missing `aria-hidden` brings it back — so this is
166
+ * all-or-nothing, across both element kinds: a readable space is heard as a pause in
167
+ * the middle of a word.
168
+ *
169
+ * Throws when the split produced nothing at all. An empty run of elements satisfies
170
+ * "every one of them is hidden" without anything being hidden, which is also what a
171
+ * component that stopped rendering its pieces would report.
172
+ */
173
+ isTextHidden(): Promise<boolean>;
174
+ /**
175
+ * The alignment the host was given, as the component wrote it.
176
+ *
177
+ * Off the inline `style` attribute rather than through `getCssValue`, and the
178
+ * difference is not pedantry: computed style answers for an element nobody wrote to —
179
+ * with the inherited or user-agent value — so a host that stopped reflecting the input
180
+ * altogether would keep reporting a plausible `left` or `start`. The attribute is the
181
+ * fact being asserted.
182
+ *
183
+ * `null` means the host carries no alignment at all, which the component never does:
184
+ * the binding is unconditional and the input defaults to `'center'`.
185
+ */
186
+ getTextAlign(): Promise<string | null>;
187
+ /**
188
+ * Whether the reveal has taken the pieces over — NOT whether anything is animating.
189
+ *
190
+ * Read off the inline `style` attribute the component writes itself, so it stays
191
+ * `true` once the reveal has finished: the tween commits its end state back to the
192
+ * same inline properties on `onfinish`. Anyone reaching for `isAnimating()` wants a
193
+ * frame, and there are no frames here.
194
+ *
195
+ * `false` is the answer worth asserting. It means no piece was written to, which is
196
+ * both the pre-trigger state and the reduced-motion promise — the component returns
197
+ * before staging anything, so a reader who asked for less motion cannot be left with
198
+ * text parked at `from.opacity: 0` by a tween that never runs.
199
+ *
200
+ * ANY piece counts, not all of them, and that is the conservative reading rather than
201
+ * a loose one: a stagger that stages the first piece and then throws leaves exactly
202
+ * one glyph invisible, which is a real failure — and one jsdom reproduces every time
203
+ * `Element.prototype.animate` is missing.
204
+ */
205
+ hasStagedMotion(): Promise<boolean>;
206
+ }
207
+
208
+ export { WrSplitTextHarness };
209
+ export type { WrSplitTextHarnessAlign, WrSplitTextHarnessFilters };
@@ -72,6 +72,18 @@ declare class WrSplitText {
72
72
  private readonly isBrowser;
73
73
  private readonly platform;
74
74
  private hasAnimated;
75
+ /**
76
+ * The ONE observer, replaced rather than stacked.
77
+ *
78
+ * `startObserver()` used to `new IntersectionObserver(…)` and register its own
79
+ * `destroyRef.onDestroy` every time, and the effect below calls it on every
80
+ * `text` change after setting `hasAnimated = false` — so the guard at the top
81
+ * of `startObserver` never blocked that path. An off-screen element (the only
82
+ * case where the observer does not immediately disconnect itself) therefore
83
+ * leaked one live observer AND one destroy callback per change, all of them
84
+ * watching the same host and all of them able to fire `animate()`.
85
+ */
86
+ private observer;
75
87
  constructor();
76
88
  private startObserver;
77
89
  private animate;