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,222 @@
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-decrypt-text>` a harness query matches. */
11
+ interface WrDecryptTextHarnessFilters extends BaseHarnessFilters {
12
+ /**
13
+ * Match the string the component ANNOUNCES — the readable copy, never the scramble.
14
+ * A string is an exact match, a RegExp is tested.
15
+ *
16
+ * This is the only stable thing to address one of these by: the visible layer is a
17
+ * fresh random draw on every tick, so a filter written against it would match a
18
+ * different element each time it ran, or none at all.
19
+ */
20
+ readonly text?: string | RegExp;
21
+ /**
22
+ * Match only settled (`true`) or only scrambled (`false`) instances — the way to pick
23
+ * out the one that has not been revealed yet on a page holding several.
24
+ *
25
+ * Read from the encrypted characters rather than from the glyphs, so it needs no
26
+ * `Math.random` stub. An instance that rendered no characters at all matches NEITHER
27
+ * value: "nothing is encrypted" is true of nothing, and a query is the last place that
28
+ * vacuity should quietly hand back a match.
29
+ */
30
+ readonly revealed?: boolean;
31
+ }
32
+
33
+ /**
34
+ * @license
35
+ *
36
+ * Use of this source code is governed by an MIT-style license that can be
37
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
38
+ */
39
+
40
+ /**
41
+ * Test harness for `<wr-decrypt-text>`.
42
+ *
43
+ * **Two layers, two questions, and they are meant to disagree.** The component renders
44
+ * the real string once in a visually-hidden span and the scramble in a parallel
45
+ * `aria-hidden` layer, so {@link getAccessibleText} is what a screen reader hears and
46
+ * {@link getRenderedText} is what a sighted reader sees. Mid-reveal the second is
47
+ * literal gibberish while the first must be untouched — that is the contract, and it is
48
+ * the half nobody looks at. A spec asserting only the visible glyphs would pass on a
49
+ * component that announced `XQ#PZ`.
50
+ *
51
+ * **Prefer the class over the glyphs.** {@link getRevealedIndices},
52
+ * {@link getEncryptedCount} and {@link isFullyRevealed} read
53
+ * `.wr-decrypt-text__char--encrypted`, which is deterministic with no stubbing at all —
54
+ * they are what tell `revealDirection` `'start'` from `'end'` from `'center'`, one index
55
+ * per tick. {@link getRenderedText} reads the glyphs themselves, which are a fresh
56
+ * `Math.random()` draw per tick, so stub it (`vi.spyOn(Math, 'random')
57
+ * .mockReturnValue(0)`) before asserting a string.
58
+ *
59
+ * **The clock is the spec's, not the harness's.** The reveal is a `setInterval` this
60
+ * class does not own, so there is no `advance(ms)` and no `waitUntilRevealed()`: one
61
+ * would flake on a loaded machine and the other would silently require fake timers to
62
+ * have been installed. Fake `setInterval` / `clearInterval` and nothing else — faking
63
+ * the microtask queue freezes the stabilization every `await harness.*()` runs through —
64
+ * then advance the clock yourself between reads.
65
+ *
66
+ * **There is no keyboard path, and this harness does not pretend there is one.**
67
+ * {@link hover}, {@link mouseAway} and {@link click} are the component's only triggers;
68
+ * with `animateOn="click"` the host takes a click handler while carrying no role, no
69
+ * `tabindex` and no keydown handler, so a `focus()` / `sendKeys(Enter)` affordance here
70
+ * would make a spec pass while a keyboard or switch user could not reveal the text at
71
+ * all. There is no `enterViewport()` either: `'view'` and `'inViewHover'` hang off an
72
+ * `IntersectionObserver` jsdom does not implement, so the spec stubs the global and
73
+ * fires it.
74
+ *
75
+ * **Also deliberately absent:** `isAnimating()` — the component has such a signal and it
76
+ * never reaches the DOM, and deriving it from "something is encrypted" would be wrong in
77
+ * exactly the interesting case, since `animateOn="click"` rests fully encrypted and
78
+ * perfectly still. `getSpeed()` / `getMaxIterations()` / `getRevealDirection()` /
79
+ * `getClickMode()` / `getAnimateOn()` — inputs that are never reflected anywhere, so
80
+ * reporting them would mean reading the component instance rather than the rendered
81
+ * control; they are observable as behaviour, which is what the methods above measure.
82
+ * `getScrambleAlphabet()` — the pool is internal and each frame shows one random draw
83
+ * from it, so any reconstruction is a guess that gets worse the shorter the string.
84
+ * `getCharColors()` / `isDimmed()` — the `--encrypted` rule is deliberately EMPTY, a
85
+ * styling hook for consumers, so there is nothing painted to read in any environment.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * const secret = await loader.getHarness(WrDecryptTextHarness.with({ text: 'Hello' }));
90
+ *
91
+ * await secret.hover();
92
+ * vi.advanceTimersByTime(50);
93
+ *
94
+ * expect(await secret.getRevealedIndices()).toEqual([0]); // sequential, from the start
95
+ * expect(await secret.getAccessibleText()).toBe('Hello'); // untouched throughout
96
+ * ```
97
+ *
98
+ * @see https://ngwr.dev/guides/testing
99
+ */
100
+ declare class WrDecryptTextHarness extends ComponentHarness {
101
+ static hostSelector: string;
102
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
103
+ static with(options?: WrDecryptTextHarnessFilters): HarnessPredicate<WrDecryptTextHarness>;
104
+ private readonly chars;
105
+ private readonly encrypted;
106
+ /**
107
+ * The real string, as the one readable copy holds it.
108
+ *
109
+ * The single highest-value assertion here: it must stay equal to the `text` input on
110
+ * every frame, while the visible layer is unreadable by design. Lose this span and the
111
+ * component announces nothing at all — every drawn character sits under `aria-hidden`,
112
+ * so no other part of the DOM would change.
113
+ *
114
+ * Untrimmed, so that it can be compared against {@link getRenderedText} without one
115
+ * side of the comparison quietly forgiving a lost leading space.
116
+ */
117
+ getAccessibleText(): Promise<string>;
118
+ /**
119
+ * What is on screen this frame — scramble glyphs and all, in document order.
120
+ *
121
+ * Plain before anything triggers it, gibberish mid-reveal, and the real string once it
122
+ * settles; comparing it against {@link getAccessibleText} is how a spec says which of
123
+ * those it is looking at. `useOriginalCharsOnly` is visible only here — every glyph
124
+ * has to be one the text really contains.
125
+ *
126
+ * Nondeterministic unless the spec stubs `Math.random`, because each unrevealed
127
+ * position is an independent draw from the pool. When the assertion does not actually
128
+ * need the glyphs, {@link getRevealedIndices} answers the same progress question off
129
+ * the class and needs no stub.
130
+ */
131
+ getRenderedText(): Promise<string>;
132
+ /**
133
+ * How many characters the animation walks.
134
+ *
135
+ * CODE POINTS, not UTF-16 units: `'a🚀b'` is 3 here, and the indices
136
+ * {@link getRevealedIndices} returns line up with `[...text]` rather than with plain
137
+ * `text[i]`. That distinction is not pedantry — counting units gave a sequential
138
+ * reveal extra ticks with no character to spend them on, and made "everything is
139
+ * revealed" disagree with what was on screen.
140
+ */
141
+ getCharCount(): Promise<number>;
142
+ /**
143
+ * The positions that have settled on their real character, ascending.
144
+ *
145
+ * The payload method, and the deterministic one: it reads the `--encrypted` modifier
146
+ * rather than the glyphs, so no `Math.random` stub is involved. It is the only thing
147
+ * that separates `revealDirection` `'start'` (`[0]`, `[0, 1]`, …) from `'end'`
148
+ * (`[4]`, `[3, 4]`, …) from `'center'` (`[2]`, `[1, 2]`, …), and one index per tick is
149
+ * what makes a sequential reveal sequential.
150
+ *
151
+ * Ascending by position, NOT in the order the reveal visited them — the DOM records
152
+ * which characters have landed, never the path taken to them. Assert the set after
153
+ * each tick to pin an order.
154
+ */
155
+ getRevealedIndices(): Promise<number[]>;
156
+ /**
157
+ * How many characters are still scrambled.
158
+ *
159
+ * The coarse progress read, and the cheapest way to say "scrambled on hover", "plain on
160
+ * leave", or "a reader who asked for less motion never saw a scramble at all". Class-
161
+ * based like {@link getRevealedIndices}, so it too is random-proof.
162
+ */
163
+ getEncryptedCount(): Promise<number>;
164
+ /**
165
+ * Whether every character has settled.
166
+ *
167
+ * Deliberately does NOT also compare {@link getRenderedText} against
168
+ * {@link getAccessibleText}: folding two conditions into one boolean hides which of
169
+ * them broke, and a spec that wants both should write both lines.
170
+ *
171
+ * Throws when nothing was rendered. "No character is encrypted" is true of no
172
+ * characters, which is the same answer a component that stopped rendering its text
173
+ * would give — and this is the method most likely to be the only assertion in a test.
174
+ */
175
+ isFullyRevealed(): Promise<boolean>;
176
+ /**
177
+ * Whether the scramble layer is out of the accessibility tree.
178
+ *
179
+ * The contract that keeps a screen reader from spelling out `XQ#PZ` beside the real
180
+ * string it already read. Worth a method rather than an assumption because the wrapper
181
+ * holding the characters carries NO class — it can only be addressed as the readable
182
+ * span's next sibling, which any template reshuffle breaks.
183
+ *
184
+ * Throws when that structural selector finds nothing, rather than returning `false`: a
185
+ * missing wrapper and an unhidden one are different failures, and the answer to
186
+ * "is the scramble hidden" when there is no scramble layer at all is not `false`.
187
+ */
188
+ isAnimatedLayerHidden(): Promise<boolean>;
189
+ /**
190
+ * Point at it — the trigger for `animateOn` `'hover'` and `'inViewHover'`.
191
+ *
192
+ * Drives the component the way a user does, and works in a test because the trigger is
193
+ * an event rather than a gesture: `TestElement.hover()` dispatches `pointerenter`,
194
+ * `mouseover` and `mouseenter`, and the component listens for the last of those. A
195
+ * hand-rolled pointer-only dispatch would not reach it.
196
+ *
197
+ * Does nothing in the other two modes, exactly as a real pointer does nothing there.
198
+ */
199
+ hover(): Promise<void>;
200
+ /**
201
+ * Move the pointer off — the other half of hover mode.
202
+ *
203
+ * Pins the promise that leaving mid-scramble restores the real text IMMEDIATELY rather
204
+ * than letting the reveal play out: the reader took the effect away, so the string has
205
+ * to come back at once.
206
+ */
207
+ mouseAway(): Promise<void>;
208
+ /**
209
+ * Click the host — the `animateOn="click"` trigger, and the only way to tell
210
+ * `clickMode` `'once'` from `'toggle'`.
211
+ *
212
+ * There is no keyboard equivalent, and that is a statement about the component rather
213
+ * than about this harness: in click mode the host carries a click handler with no role,
214
+ * no `tabindex` and no keydown handler, so a keyboard or switch user cannot reveal the
215
+ * text at all. A `sendKeys(Enter)` here would invent an affordance that does not exist
216
+ * and let a spec pass over the top of it.
217
+ */
218
+ click(): Promise<void>;
219
+ }
220
+
221
+ export { WrDecryptTextHarness };
222
+ export type { WrDecryptTextHarnessFilters };
@@ -0,0 +1,79 @@
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-donut-chart>` a harness query matches. */
11
+ interface WrDonutChartHarnessFilters extends BaseHarnessFilters {
12
+ /** Match the chart's accessible name. */
13
+ readonly ariaLabel?: string | RegExp;
14
+ /** Match a chart whose legend holds this label. */
15
+ readonly sliceLabel?: string | RegExp;
16
+ }
17
+ /** One legend row — the only place a slice's label and value are readable. */
18
+ interface WrDonutChartLegendEntry {
19
+ readonly label: string;
20
+ readonly value: string;
21
+ }
22
+
23
+ /**
24
+ * @license
25
+ *
26
+ * Use of this source code is governed by an MIT-style license that can be
27
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
28
+ */
29
+
30
+ /**
31
+ * Test harness for `<wr-donut-chart>`.
32
+ *
33
+ * **The ring itself is unreadable, and that is by design.** The slices are SVG
34
+ * paths inside an `aria-hidden` drawing: a screen reader is given the chart's
35
+ * `aria-label` and nothing else, and a spec cannot get a label or a value off a
36
+ * path's `d`. {@link getLegend} is where the numbers are — which is also the
37
+ * argument for keeping `showLegend` on, since with it off the chart's data has no
38
+ * textual form at all.
39
+ *
40
+ * {@link getSliceCount} is therefore the one thing the ring answers: how many
41
+ * wedges were drawn. Compare it with the legend's length to catch a slice that was
42
+ * filtered out of one and not the other.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const chart = await loader.getHarness(WrDonutChartHarness);
47
+ *
48
+ * expect(await chart.getSliceCount()).toBe(3);
49
+ * expect(await chart.getLegend()).toEqual([
50
+ * { label: 'Direct', value: '48' },
51
+ * { label: 'Search', value: '32' },
52
+ * { label: 'Social', value: '20' },
53
+ * ]);
54
+ * ```
55
+ *
56
+ * @see https://ngwr.dev/guides/testing
57
+ */
58
+ declare class WrDonutChartHarness extends ComponentHarness {
59
+ static hostSelector: string;
60
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
61
+ static with(options?: WrDonutChartHarnessFilters): HarnessPredicate<WrDonutChartHarness>;
62
+ /** The name the ring announces — the whole chart's, since the slices announce nothing. */
63
+ getAccessibleName(): Promise<string | null>;
64
+ /** How many wedges were drawn. */
65
+ getSliceCount(): Promise<number>;
66
+ /** Whether the legend is rendered (`showLegend`, and at least one slice). */
67
+ hasLegend(): Promise<boolean>;
68
+ /** The legend rows in order — the only textual form the data has. */
69
+ getLegend(): Promise<WrDonutChartLegendEntry[]>;
70
+ /** The big number in the middle, or `null` when the chart has none. */
71
+ getCenterValue(): Promise<string | null>;
72
+ /** The caption under the centre value, or `null`. */
73
+ getCenterLabel(): Promise<string | null>;
74
+ /** The ring's diameter in pixels, as the component writes it inline. */
75
+ getSize(): Promise<number>;
76
+ }
77
+
78
+ export { WrDonutChartHarness };
79
+ export type { WrDonutChartHarnessFilters, WrDonutChartLegendEntry };
@@ -143,6 +143,18 @@ declare class WrDrawer {
143
143
  private focusTrap;
144
144
  private previouslyFocused;
145
145
  constructor();
146
+ /**
147
+ * Point the panel's `aria-labelledby` at the CURRENT `[wrDrawerTitle]`.
148
+ *
149
+ * Re-resolved after every render rather than once when the drawer opens,
150
+ * because the title element is projected content and can come and go while
151
+ * the drawer stays open — an `@if` around it, or a heading that only exists
152
+ * once the data arrives. A one-shot lookup leaves the attribute naming an
153
+ * element that has been removed or replaced, and a dangling reference is not
154
+ * a name: the dialog is announced as unnamed, with nothing on screen to say
155
+ * so. A missing title clears the attribute for the same reason.
156
+ */
157
+ private syncLabel;
146
158
  private openOverlay;
147
159
  private closeOverlay;
148
160
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrDrawer, never>;
@@ -97,6 +97,15 @@ declare class WrDropdown {
97
97
  /** Toggle the menu. */
98
98
  toggle(): void;
99
99
  /** @internal */
100
+ /**
101
+ * Whether the pointer, rather than the keyboard, opened the current menu.
102
+ *
103
+ * A hover-opened menu must not take focus — the APG shows a menu on hover and
104
+ * leaves the caret where it was — while a keyboard-opened one must. The
105
+ * trigger MODE cannot answer that on its own: a `trigger="hover"` menu is
106
+ * still openable with Enter, and that open should focus the first item.
107
+ */
108
+ private openedByPointer;
100
109
  protected onClick(event: MouseEvent): void;
101
110
  /** @internal */
102
111
  protected onMouseEnter(): void;
@@ -0,0 +1,301 @@
1
+ import { BaseHarnessFilters, ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
2
+ import { WrCalendarView } from 'ngwr/event-calendar';
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-event-calendar>` a harness query matches. */
12
+ interface WrEventCalendarHarnessFilters extends BaseHarnessFilters {
13
+ /** Match the header title as printed — a string is an exact match, a RegExp is tested. */
14
+ readonly title?: string | RegExp;
15
+ /** Match the view the calendar is showing. */
16
+ readonly view?: WrCalendarView;
17
+ }
18
+ /** Narrows which event chip a harness query matches. */
19
+ interface WrEventCalendarChipHarnessFilters extends BaseHarnessFilters {
20
+ /**
21
+ * Match the chip's accessible name, which is the time and the title together —
22
+ * the visible text is `aria-hidden`, so this is what a screen reader gets.
23
+ */
24
+ readonly label?: string | RegExp;
25
+ /** Match the chip's title text as printed. */
26
+ readonly title?: string | RegExp;
27
+ /** Match day-spanning bands (`true`) or timed chips (`false`). */
28
+ readonly band?: boolean;
29
+ }
30
+
31
+ /**
32
+ * @license
33
+ *
34
+ * Use of this source code is governed by an MIT-style license that can be
35
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
36
+ */
37
+
38
+ /** The arrow keys, by the direction a user would say out loud. */
39
+ type Arrow$1 = 'left' | 'right' | 'up' | 'down';
40
+ /**
41
+ * Test harness for one event chip inside a {@link WrEventCalendarHarness}.
42
+ *
43
+ * **The visible text is `aria-hidden`; the name is on the button.** A chip prints a
44
+ * time and a title for the eye, and publishes both as one `aria-label` for a screen
45
+ * reader — so {@link getLabel} is what the control announces and {@link getTitle} is
46
+ * what is drawn. They are separate because a consumer's `[wrCalendarEvent]` template
47
+ * replaces the drawn half and not the announced one.
48
+ *
49
+ * **Moving and resizing are on the keyboard here, and not because of jsdom.** The
50
+ * pointer drag is real, but the same two operations are bound to the arrows —
51
+ * `Alt` to move, `Alt` + `Shift` to resize — precisely so the calendar is usable
52
+ * without one. That is the path this harness drives; a synthetic drag would divide
53
+ * by a rect that measures 0×0 anyway.
54
+ *
55
+ * @see https://ngwr.dev/guides/testing
56
+ */
57
+ declare class WrEventCalendarChipHarness extends ComponentHarness {
58
+ static hostSelector: string;
59
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
60
+ static with(options?: WrEventCalendarChipHarnessFilters): HarnessPredicate<WrEventCalendarChipHarness>;
61
+ /** What the chip announces — its time and title in one string. */
62
+ getLabel(): Promise<string | null>;
63
+ /**
64
+ * The title as drawn, or `null` when a consumer template replaced it.
65
+ *
66
+ * The default chip prints its own `.wr-event-calendar__chip-title`; a
67
+ * `[wrCalendarEvent]` template renders whatever the consumer wrote instead, and
68
+ * there is no title element to find. {@link getText} is the fallback that always
69
+ * answers.
70
+ */
71
+ getTitle(): Promise<string | null>;
72
+ /** The chip's whole rendered text, whoever drew it. */
73
+ getText(): Promise<string>;
74
+ /** The time as printed on a timed chip, or `null` on a band, which prints none. */
75
+ getTime(): Promise<string | null>;
76
+ /**
77
+ * Whether this is a day-spanning BAND rather than a timed chip.
78
+ *
79
+ * One shape covers two places — a month cell and the all-day row — and the other
80
+ * is the block in a time grid. They size themselves completely differently (a
81
+ * `calc()` width across columns versus a percentage height down one), which is why
82
+ * the two are worth telling apart.
83
+ */
84
+ isBand(): Promise<boolean>;
85
+ /** Whether the event started before this cell — the chip is drawn cut off at the leading edge. */
86
+ continuesBefore(): Promise<boolean>;
87
+ /** Whether the event runs past this cell. */
88
+ continuesAfter(): Promise<boolean>;
89
+ /** Whether the chip is mid-drag. */
90
+ isDragging(): Promise<boolean>;
91
+ /**
92
+ * Whether the chip offers a resize grab area.
93
+ *
94
+ * It is `aria-hidden` and not focusable on purpose — the same resize is `Alt` +
95
+ * `Shift` + arrows on the chip itself, so a second tab stop here would lead
96
+ * nowhere. Its
97
+ * presence is still the mouse affordance, and it is dropped on a chip whose event
98
+ * continues past this cell.
99
+ */
100
+ hasResizeHandle(): Promise<boolean>;
101
+ /**
102
+ * Click the chip — which emits `eventClick` and does NOT bubble to the cell
103
+ * underneath.
104
+ *
105
+ * On an editable calendar this needs `document.elementFromPoint` to exist: the CDK
106
+ * dispatches a whole pointer sequence, and the chip's `pointerdown` hit-tests for
107
+ * the cell under the cursor. jsdom implements no such method, so stub it in your
108
+ * spec — `() => null` is the honest answer where nothing has a box — or the click
109
+ * throws out of the listener.
110
+ */
111
+ click(): Promise<void>;
112
+ /** Move keyboard focus to the chip. */
113
+ focus(): Promise<void>;
114
+ /** Whether the chip currently holds focus. */
115
+ isFocused(): Promise<boolean>;
116
+ /**
117
+ * Move the event — `Alt` + an arrow, a day sideways or a slot vertically.
118
+ *
119
+ * The calendar never mutates `events`: this emits `eventChange` and the host is
120
+ * expected to apply it. A spec whose host ignores the output is asserting a
121
+ * CANCELLED move, which is the contract and the most common surprise here. A
122
+ * calendar without `editable` ignores the keys entirely — the same gate the pointer
123
+ * path uses.
124
+ */
125
+ move(arrow: Arrow$1): Promise<void>;
126
+ /**
127
+ * Resize the event — `Alt` + `Shift` + an arrow, which moves the END alone.
128
+ *
129
+ * A resize that would end at or before the start emits nothing rather than
130
+ * inverting the event, which is the pointer path's rule too.
131
+ */
132
+ resize(arrow: Arrow$1): Promise<void>;
133
+ /** Press a bare arrow — which the chip ignores, letting the grid's own navigation run. */
134
+ pressArrow(arrow: Arrow$1): Promise<void>;
135
+ /** Press Escape — handing focus back to the cell the chip sits in. */
136
+ sendEscape(): Promise<void>;
137
+ }
138
+
139
+ /**
140
+ * @license
141
+ *
142
+ * Use of this source code is governed by an MIT-style license that can be
143
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
144
+ */
145
+
146
+ /** The arrow keys, by the direction a user would say out loud. */
147
+ type Arrow = 'left' | 'right' | 'up' | 'down';
148
+ /**
149
+ * Test harness for `<wr-event-calendar>` — month, week and day in one component.
150
+ *
151
+ * **Every chip lives inside the `role="gridcell"` where its event STARTS**, and
152
+ * reaches out from there with a `calc()` width or a percentage height. That is a
153
+ * deliberate structural choice — a floating events layer would leave `role="row"`
154
+ * owning something other than cells — and it is why {@link getChips} can be scoped
155
+ * to a cell at all.
156
+ *
157
+ * **The view is read from the DOM, not from the input.** A month renders one grid
158
+ * shape and week / day another, and those two are told apart by how many day columns
159
+ * they draw. That is the honest answer for a harness: `view` is a model a consumer
160
+ * can write, and the grid is what the user sees.
161
+ *
162
+ * **Nothing here mutates the events.** A move or a resize emits `eventChange` and
163
+ * the host applies it — so a spec whose host ignores the output is asserting a
164
+ * CANCELLED gesture, which is the contract rather than a bug.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * const calendar = await loader.getHarness(WrEventCalendarHarness);
169
+ *
170
+ * expect(await calendar.getView()).toBe('month');
171
+ * const [standup] = await calendar.getChips({ title: 'Standup' });
172
+ * await standup.move('right');
173
+ * ```
174
+ *
175
+ * @see https://ngwr.dev/guides/testing
176
+ */
177
+ declare class WrEventCalendarHarness extends ComponentHarness {
178
+ static hostSelector: string;
179
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
180
+ static with(options?: WrEventCalendarHarnessFilters): HarnessPredicate<WrEventCalendarHarness>;
181
+ private readonly grid;
182
+ /**
183
+ * Which view is on screen.
184
+ *
185
+ * `month` has its own grid modifier; `week` and `day` share the time grid and are
186
+ * told apart by the column count the component publishes on it. Reading the DOM
187
+ * rather than the `view` model is the point — that model is an input a consumer can
188
+ * set to anything, and this is what got rendered.
189
+ */
190
+ getView(): Promise<WrCalendarView>;
191
+ /** The header title — the month, the week's range, or the day. */
192
+ getTitle(): Promise<string>;
193
+ /** Whether the header is rendered at all (`hideHeader`). */
194
+ hasHeader(): Promise<boolean>;
195
+ /** The grid's accessible name. */
196
+ getAccessibleName(): Promise<string | null>;
197
+ /** Step back one month, week or day, depending on the view. */
198
+ previous(): Promise<void>;
199
+ /** Step forward one month, week or day. */
200
+ next(): Promise<void>;
201
+ /** Press the Today button — jumping to the period containing today. */
202
+ goToday(): Promise<void>;
203
+ /** The back arrow's accessible name — the two steps are icon-only. */
204
+ getPreviousLabel(): Promise<string | null>;
205
+ /** The forward arrow's accessible name. */
206
+ getNextLabel(): Promise<string | null>;
207
+ /** The Today button's label. */
208
+ getTodayLabel(): Promise<string>;
209
+ /**
210
+ * The view switcher's labels, in order — empty when the calendar was given a single
211
+ * view, which drops the switcher entirely.
212
+ */
213
+ getViewLabels(): Promise<string[]>;
214
+ /** The label of the view currently pressed, or `null` when there is no switcher. */
215
+ getActiveViewLabel(): Promise<string | null>;
216
+ /**
217
+ * Switch view by the label the switcher prints.
218
+ *
219
+ * By label rather than by the `WrCalendarView` value, because the label is what the
220
+ * DOM has — the value never reaches it, and a spec that passed one would be naming
221
+ * something only the component can see. {@link getViewLabels} lists them.
222
+ */
223
+ setView(label: string): Promise<void>;
224
+ /** The weekday column headers of the month view. */
225
+ getWeekdayNames(): Promise<string[]>;
226
+ /** The day numbers of the month grid, in order — 42 of them, spill days included. */
227
+ getDayNumbers(): Promise<string[]>;
228
+ /** The time-gutter labels down the left of a week or day view. */
229
+ getSlotLabels(): Promise<string[]>;
230
+ /** Whether the time views drew an all-day band, which they only do when something needs it. */
231
+ hasAllDayRow(): Promise<boolean>;
232
+ /**
233
+ * Every chip on screen, in DOM order.
234
+ *
235
+ * A multi-day event has ONE chip, in the cell where it starts — the band reaches
236
+ * across the rest — so this counts events-in-view rather than event-days.
237
+ */
238
+ getChips(filters?: WrEventCalendarChipHarnessFilters): Promise<WrEventCalendarChipHarness[]>;
239
+ /** The chips' announced names, in DOM order. */
240
+ getChipLabels(): Promise<(string | null)[]>;
241
+ /**
242
+ * The "+N more" overflow buttons of the month view, by the text they print.
243
+ *
244
+ * A month cell shows `maxLanes` chips and hides the rest behind one of these, which
245
+ * opens the day view — so its presence is the only sign that a cell has more than
246
+ * it is showing.
247
+ */
248
+ getOverflowLabels(): Promise<string[]>;
249
+ /** Click the first "+N more" button, which drops to the day view for that date. */
250
+ openOverflow(): Promise<void>;
251
+ /**
252
+ * Click an empty part of a cell — which emits `slotClick` with its date and minutes.
253
+ *
254
+ * A cell is addressed by its DAY and, in a time view, the minutes from midnight of
255
+ * its row (`-1` means a month cell or the all-day band). Both come off the two data
256
+ * attributes every cell publishes for the drag's own hit-testing, which makes them
257
+ * the one stable way to name a cell from outside — a row index would mean something
258
+ * different in each view.
259
+ */
260
+ clickCell(day: Date, minutes?: number): Promise<void>;
261
+ /** A cell's accessible name — the date, and the time for a slot. */
262
+ getCellLabel(day: Date, minutes?: number): Promise<string | null>;
263
+ /** The chips inside ONE cell, which is where a chip's event starts. */
264
+ getCellChips(day: Date, minutes?: number): Promise<WrEventCalendarChipHarness[]>;
265
+ /**
266
+ * The grid's single tab stop, as `"<day>:<minutes>"`.
267
+ *
268
+ * The cursor roves rather than every cell being tabbable, and it is RAW — it
269
+ * survives a view switch, so the same pair means a different cell in a month grid
270
+ * and a week one.
271
+ */
272
+ getCursor(): Promise<string | null>;
273
+ /** Move keyboard focus to a cell, which is also what moves the roving cursor onto it. */
274
+ focusCell(day: Date, minutes?: number): Promise<void>;
275
+ /**
276
+ * Press an arrow on the cell that currently holds the cursor.
277
+ *
278
+ * Sent to that cell rather than to the grid, because the component listens per cell
279
+ * — the element carrying the keyboard should be the one that holds focus — and
280
+ * anything originating inside a chip is deliberately let through untouched.
281
+ */
282
+ pressArrow(arrow: Arrow): Promise<void>;
283
+ /** Press Home on the cursor cell — the first day of its week. */
284
+ pressHome(): Promise<void>;
285
+ /** Press End on the cursor cell — the last day of its week. */
286
+ pressEnd(): Promise<void>;
287
+ private step;
288
+ private stepElement;
289
+ private headerButton;
290
+ /**
291
+ * The stamp a cell publishes for a day — local midnight, which is what the
292
+ * component's own `startOfDay` produces. Taking a `Date` and normalising here keeps
293
+ * the timestamp out of every spec.
294
+ */
295
+ private stamp;
296
+ private requireCell;
297
+ private cursorCell;
298
+ }
299
+
300
+ export { WrEventCalendarChipHarness, WrEventCalendarHarness };
301
+ export type { WrEventCalendarChipHarnessFilters, WrEventCalendarHarnessFilters };
@@ -220,13 +220,16 @@ declare class WrEventCalendar {
220
220
  private readonly document;
221
221
  private readonly dir;
222
222
  private readonly chipTpl;
223
- protected readonly todayLabel: string;
224
- protected readonly previousLabel: string;
225
- protected readonly nextLabel: string;
226
- protected readonly allDayLabel: string;
227
- protected readonly timeLabel: string;
223
+ protected readonly todayLabel: _angular_core.Signal<string>;
224
+ protected readonly previousLabel: _angular_core.Signal<string>;
225
+ protected readonly nextLabel: _angular_core.Signal<string>;
226
+ protected readonly allDayLabel: _angular_core.Signal<string>;
227
+ protected readonly timeLabel: _angular_core.Signal<string>;
228
228
  protected readonly moreLabel: (params?: ngwr_i18n.WrI18nParams) => string;
229
- protected readonly viewLabels: Record<WrCalendarView, string>;
229
+ private readonly monthLabel;
230
+ private readonly weekLabel;
231
+ private readonly dayLabel;
232
+ protected readonly viewLabels: _angular_core.Signal<Record<WrCalendarView, string>>;
230
233
  /** Accessible name of the grid; overridable for a page with several. */
231
234
  readonly ariaLabel: _angular_core.InputSignal<string | null>;
232
235
  protected readonly resolvedAriaLabel: _angular_core.Signal<string>;