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,325 @@
1
+ import { 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
+ /** The seven halo steps, in the order the component writes them: 100%, 60%, 50%, 40%, 30%, 20%, 10%. */
10
+ const GLOW_VAR_SUFFIXES = ['', '-60', '-50', '-40', '-30', '-20', '-10'];
11
+ /** The seven mesh-gradient slots, spelled out rather than numbered — as the component names them. */
12
+ const GRADIENT_VAR_SLOTS = ['one', 'two', 'three', 'four', 'five', 'six', 'seven'];
13
+ /** `hsl(40deg 80% 80% / 65%)` — the alpha, which is the only part `glowIntensity` moves. */
14
+ const GLOW_ALPHA = /\/\s*(-?[\d.]+)%\s*\)\s*$/;
15
+ /** `radial-gradient(at 80% 55%, #c084fc 0px, transparent 50%)` — the colour, not the position. */
16
+ const RADIAL_GRADIENT_COLOR = /^radial-gradient\(\s*at\s+[^,]+,\s*(.+?)\s+0px\s*,/;
17
+ /** `linear-gradient(#c084fc 0 100%)` — the flat base slice. */
18
+ const LINEAR_GRADIENT_COLOR = /^linear-gradient\(\s*(.+?)\s+0\s+100%\s*\)$/;
19
+ /**
20
+ * Split an inline `style` attribute into its declarations.
21
+ *
22
+ * Read from the attribute rather than through `getCssValue()` on purpose. jsdom echoes
23
+ * an inline custom property back through `getComputedStyle`, so the computed read passes
24
+ * here and would answer something else entirely in a browser — where the stylesheet's
25
+ * own per-theme default resolves and "the author left this to the theme" quietly becomes
26
+ * a colour. The attribute holds what the component itself wrote, and nothing else.
27
+ */
28
+ function parseInlineDeclarations(style) {
29
+ const declarations = new Map();
30
+ for (const declaration of style.split(';')) {
31
+ const colon = declaration.indexOf(':');
32
+ if (colon === -1)
33
+ continue;
34
+ const name = declaration.slice(0, colon).trim();
35
+ if (name !== '')
36
+ declarations.set(name, declaration.slice(colon + 1).trim());
37
+ }
38
+ return declarations;
39
+ }
40
+ /**
41
+ * Test harness for `<wr-border-glow>` — a card whose border lights up under the cursor.
42
+ *
43
+ * **Nearly all of this component is a bag of custom properties written onto the host,
44
+ * and that is what the harness reads.** The paint itself — the conic mask, the stacked
45
+ * `box-shadow` halo, the soft-light bloom — lives in the stylesheet, so a unit test can
46
+ * never see it. What it CAN see is the arithmetic that feeds it, and that is where the
47
+ * breakages are: the seven-step alpha ramp {@link getGlowAlphaRamp} derives from
48
+ * `glowIntensity`, the `[0,1,2,0,1,2,1]` palette mapping in {@link getGradientColors}
49
+ * with its clamp for short palettes, and the pointer maths behind
50
+ * {@link getPointerAngle} / {@link getEdgeProximity}.
51
+ *
52
+ * **Every custom-property getter reads the inline declaration** — see
53
+ * {@link parseInlineDeclarations} for why a computed read would be a different, and
54
+ * wrong, question.
55
+ *
56
+ * **A colour getter answering `null` means the component has never written that
57
+ * variable**, which is the contract when the input is unset: the stylesheet's per-theme
58
+ * default takes over. Note that it does not un-write them either — setting `[glowColor]`
59
+ * and then putting it back to `null` leaves the last value on the host — so `null` is
60
+ * "never set", not "not set now".
61
+ *
62
+ * There is deliberately no `getGlowOpacity()`, `isGlowVisible()` or `isHovered()`: the
63
+ * visible strength is a `calc()` in the stylesheet gated behind `:not(:hover)`, and
64
+ * jsdom loads no stylesheet and matches no `:hover`, so any number would be invented.
65
+ * There is no measured halo extent either — `.wr-border-glow__edge` is 0×0 in a test,
66
+ * and {@link getGlowRadius} reads what the component asked for instead. And there is no
67
+ * `getSweepProgress()` / `waitForSweep()`: the mount sweep is a four-second chain of
68
+ * `requestAnimationFrame` tweens, so awaiting it either hangs the spec or samples one
69
+ * frame and reports a value that was never settled. {@link isSweeping} answers the one
70
+ * durable fact — that the sweep started.
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * const card = await loader.getHarness(WrBorderGlowHarness.with({ text: /Hover me/ }));
75
+ *
76
+ * expect(await card.getBorderRadius()).toBe(28);
77
+ * expect(await card.getPointerAngle()).toBeNull(); // nothing has pointed at it yet
78
+ * ```
79
+ *
80
+ * @see https://ngwr.dev/guides/testing
81
+ */
82
+ class WrBorderGlowHarness extends ComponentHarness {
83
+ static hostSelector = 'wr-border-glow';
84
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
85
+ static with(options = {}) {
86
+ return new HarnessPredicate(WrBorderGlowHarness, options)
87
+ .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getContentText(), text))
88
+ .addOption('sweeping', options.sweeping, async (harness, sweeping) => (await harness.isSweeping()) === sweeping);
89
+ }
90
+ edge = this.locatorFor('.wr-border-glow__edge');
91
+ /** The corner radius the stylesheet is told to round to, in pixels. */
92
+ async getBorderRadius() {
93
+ return this.readNumberVar('--wr-border-glow-radius', 'getBorderRadius');
94
+ }
95
+ /** How far past the card edge the halo is told to reach, in pixels. */
96
+ async getGlowRadius() {
97
+ return this.readNumberVar('--wr-border-glow-padding', 'getGlowRadius');
98
+ }
99
+ /**
100
+ * The width of the lit cone, as a share of the perimeter.
101
+ *
102
+ * Unitless by contract — the stylesheet multiplies it — so a `px` creeping in here is
103
+ * a real regression that leaves the mask with a length where it wants a number.
104
+ */
105
+ async getConeSpread() {
106
+ return this.readNumberVar('--wr-border-glow-cone-spread', 'getConeSpread');
107
+ }
108
+ /** How sharply the halo fades as the cursor leaves an edge; lower is a wider falloff. */
109
+ async getEdgeSensitivity() {
110
+ return this.readNumberVar('--wr-border-glow-edge-sensitivity', 'getEdgeSensitivity');
111
+ }
112
+ /** Strength of the interior soft-light bloom, again unitless. */
113
+ async getFillOpacity() {
114
+ return this.readNumberVar('--wr-border-glow-fill-opacity', 'getFillOpacity');
115
+ }
116
+ /**
117
+ * The card fill the author asked for, or `null` when they left it to the theme.
118
+ *
119
+ * The distinction is the whole point: an unset `[backgroundColor]` means the surface
120
+ * token decides, light or dark, and a card that starts declaring its own fill has
121
+ * stopped following the theme.
122
+ */
123
+ async getBackgroundColor() {
124
+ return this.readVar('--wr-border-glow-bg');
125
+ }
126
+ /**
127
+ * The halo colour at full strength, or `null` when the theme decides.
128
+ *
129
+ * Comes back as the `hsl(H deg S% L% / A%)` the component assembled from the bare
130
+ * `'H S L'` input, so this also pins that assembly. Be aware the parse is forgiving to
131
+ * a fault: anything that is not three bare numbers — a hex colour, `rgb(…)`, even
132
+ * `hsl(40deg 80% 80%)` with its units — silently becomes the default amber, so a
133
+ * plausible-looking value here does not mean the input was understood.
134
+ */
135
+ async getGlowColor() {
136
+ return this.readVar('--wr-border-glow-color');
137
+ }
138
+ /**
139
+ * The seven halo alphas in percent, brightest first, or `null` when no glow colour
140
+ * was ever set.
141
+ *
142
+ * This is the whole of `[glowIntensity]`: the steps are `[100, 60, 50, 40, 30, 20,
143
+ * 10]` multiplied by the intensity and clamped at 100, so `0.5` gives
144
+ * `[50, 30, 25, 20, 15, 10, 5]` and anything above 1 saturates from the top down. The
145
+ * clamp is one-sided — a negative intensity produces negative percentages, which a
146
+ * browser drops as invalid declarations and the halo vanishes — so the numbers are
147
+ * returned as written rather than corrected, which is how a spec can see it.
148
+ */
149
+ async getGlowAlphaRamp() {
150
+ const declarations = await this.declarations();
151
+ if (!declarations.has('--wr-border-glow-color'))
152
+ return null;
153
+ return GLOW_VAR_SUFFIXES.map(suffix => {
154
+ const name = `--wr-border-glow-color${suffix}`;
155
+ const raw = declarations.get(name);
156
+ const alpha = raw === undefined ? null : GLOW_ALPHA.exec(raw);
157
+ if (!alpha) {
158
+ throw new Error(`WrBorderGlowHarness.getGlowAlphaRamp(): \`${name}\` reads "${raw ?? '(absent)'}". The seven halo steps ` +
159
+ 'are written together from one glow colour, so a step that is missing or carries no ` / N%` alpha ' +
160
+ 'means the ramp stopped being built — the halo would fade in bands the stylesheet cannot use.');
161
+ }
162
+ return Number.parseFloat(alpha[1]);
163
+ });
164
+ }
165
+ /**
166
+ * The seven mesh-gradient colours, in slot order, or `null` when no palette was set.
167
+ *
168
+ * The slots do not map one-to-one onto the palette: they take colours
169
+ * `[0, 1, 2, 0, 1, 2, 1]`, each index clamped to the last entry the palette actually
170
+ * has. So a two-colour palette repeats the second colour rather than reading past the
171
+ * end — which is the case worth a spec, since `undefined` in a `radial-gradient()`
172
+ * kills the whole declaration silently.
173
+ */
174
+ async getGradientColors() {
175
+ const declarations = await this.declarations();
176
+ if (!declarations.has('--wr-border-glow-gradient-one'))
177
+ return null;
178
+ return GRADIENT_VAR_SLOTS.map(slot => {
179
+ const name = `--wr-border-glow-gradient-${slot}`;
180
+ const raw = declarations.get(name);
181
+ const colour = raw === undefined ? null : RADIAL_GRADIENT_COLOR.exec(raw);
182
+ if (!colour) {
183
+ throw new Error(`WrBorderGlowHarness.getGradientColors(): \`${name}\` reads "${raw ?? '(absent)'}", which is not the ` +
184
+ '`radial-gradient(at X% Y%, <colour> 0px, …)` the component builds. All seven slots are written from ' +
185
+ 'one palette, so one of them being different means the mapping broke, not the palette.');
186
+ }
187
+ return colour[1];
188
+ });
189
+ }
190
+ /**
191
+ * The flat colour behind the mesh, or `null` when no palette was set.
192
+ *
193
+ * Always the palette's FIRST entry. Worth its own method because the base slice is the
194
+ * one that shows where the gradients are transparent, and it drifting to the last
195
+ * colour changes the card's whole cast without touching a gradient.
196
+ */
197
+ async getBaseGradientColor() {
198
+ const raw = await this.readVar('--wr-border-glow-gradient-base');
199
+ if (raw === null)
200
+ return null;
201
+ const colour = LINEAR_GRADIENT_COLOR.exec(raw);
202
+ if (!colour) {
203
+ throw new Error(`WrBorderGlowHarness.getBaseGradientColor(): \`--wr-border-glow-gradient-base\` reads "${raw}", which is ` +
204
+ 'not the `linear-gradient(<colour> 0 100%)` the component builds from the palette.');
205
+ }
206
+ return colour[1];
207
+ }
208
+ /**
209
+ * Where the cursor last sat, in degrees: `0` is straight up and the angle grows
210
+ * clockwise. `null` until something has pointed at the card.
211
+ *
212
+ * `null` is a state of its own rather than a stand-in for zero — the stylesheet's own
213
+ * default applies until the first pointer arrives, and dead centre legitimately
214
+ * reports `0`, so a harness that folded the two together could not tell an untouched
215
+ * card from one being pointed at from the middle.
216
+ */
217
+ async getPointerAngle() {
218
+ const raw = await this.readVar('--wr-border-glow-angle');
219
+ return raw === null ? null : Number.parseFloat(raw);
220
+ }
221
+ /**
222
+ * How close the cursor is to the perimeter: `0` at dead centre, `100` at the edge.
223
+ * `null` until something has pointed at the card.
224
+ *
225
+ * On a diagonal the NEARER axis wins rather than the two averaging, so a cursor level
226
+ * with the bottom edge reads 100 wherever it is along it.
227
+ */
228
+ async getEdgeProximity() {
229
+ const raw = await this.readVar('--wr-border-glow-edge-proximity');
230
+ return raw === null ? null : Number.parseFloat(raw);
231
+ }
232
+ /**
233
+ * Point at the card, in client coordinates.
234
+ *
235
+ * **Refuses on a card with no box, which in a unit test is every card.** The component
236
+ * measures the pointer against its own centre, and with a 0×0 rect that maths
237
+ * degenerates: the centre is the top-left corner, and `1 / Math.min(0, 0)` pins the
238
+ * proximity at 100 for every position on the page. Both would come back as confident
239
+ * numbers describing nothing. Give the host a box first, exactly as the component's own
240
+ * spec does:
241
+ *
242
+ * ```ts
243
+ * const card = fixture.nativeElement.querySelector('wr-border-glow');
244
+ * card.getBoundingClientRect = () => ({ left: 0, top: 0, width: 200, height: 100 }) as DOMRect;
245
+ * ```
246
+ *
247
+ * A `pointermove` is dispatched on the host itself, so it needs no hit test and cannot
248
+ * miss — the coordinates only ever reach the component's own arithmetic.
249
+ */
250
+ async movePointerTo(clientX, clientY) {
251
+ const host = await this.host();
252
+ const { width, height } = await host.getDimensions();
253
+ if (!width || !height) {
254
+ throw new Error(`WrBorderGlowHarness.movePointerTo(${clientX}, ${clientY}): this card measures ${width}×${height}, so a ` +
255
+ 'pointer position over it means nothing — the component reads the cursor against the card centre, and ' +
256
+ 'with no box the centre is the top-left corner and the proximity saturates for every coordinate. Stub ' +
257
+ '`getBoundingClientRect` on the host first.');
258
+ }
259
+ await host.dispatchEvent('pointermove', { clientX, clientY, pointerId: 1, isPrimary: true });
260
+ }
261
+ /**
262
+ * Whether the one-shot mount sweep is running.
263
+ *
264
+ * The class is added synchronously when the sweep starts and removed when it ends
265
+ * about four seconds later, or when the component is torn down — so a spec asserts
266
+ * that it STARTED, and leaves the angle alone while it does. This is also the
267
+ * reduced-motion assertion: `[animated]="true"` for someone who asked for less motion
268
+ * must leave this `false`, and no angle written at all.
269
+ */
270
+ async isSweeping() {
271
+ return (await this.host()).hasClass('wr-border-glow--sweeping');
272
+ }
273
+ /** Whether the halo layer is in the markup — the element the whole outer glow paints on. */
274
+ async hasHaloLayer() {
275
+ return (await this.locatorForOptional('.wr-border-glow__edge')()) !== null;
276
+ }
277
+ /**
278
+ * Whether the decoration stays out of the accessibility tree.
279
+ *
280
+ * The halo layer is the component's only decorative NODE — every other layer is a
281
+ * pseudo-element and so was never in the tree to begin with. It carries no text and no
282
+ * name, so losing its `aria-hidden` puts an anonymous element between the reader and
283
+ * the card's content.
284
+ */
285
+ async isDecorationHiddenFromAssistiveTech() {
286
+ return (await (await this.edge()).getAttribute('aria-hidden')) === 'true';
287
+ }
288
+ /**
289
+ * The projected content's text, trimmed.
290
+ *
291
+ * Read from `.wr-border-glow__inner` rather than the host, which is the assertion:
292
+ * content that lands as a bare host child instead of inside the wrapper renders
293
+ * underneath the interior bloom, and reading the host would report the same string
294
+ * either way.
295
+ */
296
+ async getContentText() {
297
+ return (await this.locatorFor('.wr-border-glow__inner')()).text();
298
+ }
299
+ /** The host's inline declarations — the component's whole written output. */
300
+ async declarations() {
301
+ return parseInlineDeclarations((await (await this.host()).getAttribute('style')) ?? '');
302
+ }
303
+ /** One inline custom property, or `null` when the component has not written it. */
304
+ async readVar(name) {
305
+ return (await this.declarations()).get(name) ?? null;
306
+ }
307
+ /** One inline custom property that is always written, as a number. */
308
+ async readNumberVar(name, method) {
309
+ const raw = await this.readVar(name);
310
+ const value = raw === null ? Number.NaN : Number.parseFloat(raw);
311
+ if (Number.isNaN(value)) {
312
+ throw new Error(`WrBorderGlowHarness.${method}(): \`${name}\` reads "${raw ?? '(absent)'}" on the host. The component ` +
313
+ 'writes this one on every input change whatever the inputs say, so a missing or unreadable value means ' +
314
+ 'the variable bag stopped reaching the element at all.');
315
+ }
316
+ return value;
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Generated bundle index. Do not edit.
322
+ */
323
+
324
+ export { WrBorderGlowHarness };
325
+ //# sourceMappingURL=ngwr-border-glow-testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngwr-border-glow-testing.mjs","sources":["../../../projects/lib/border-glow/testing/wr-border-glow-harness.ts","../../../projects/lib/border-glow/testing/ngwr-border-glow-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrBorderGlowHarnessFilters } from './interfaces';\n\n/** The seven halo steps, in the order the component writes them: 100%, 60%, 50%, 40%, 30%, 20%, 10%. */\nconst GLOW_VAR_SUFFIXES = ['', '-60', '-50', '-40', '-30', '-20', '-10'] as const;\n\n/** The seven mesh-gradient slots, spelled out rather than numbered — as the component names them. */\nconst GRADIENT_VAR_SLOTS = ['one', 'two', 'three', 'four', 'five', 'six', 'seven'] as const;\n\n/** `hsl(40deg 80% 80% / 65%)` — the alpha, which is the only part `glowIntensity` moves. */\nconst GLOW_ALPHA = /\\/\\s*(-?[\\d.]+)%\\s*\\)\\s*$/;\n\n/** `radial-gradient(at 80% 55%, #c084fc 0px, transparent 50%)` — the colour, not the position. */\nconst RADIAL_GRADIENT_COLOR = /^radial-gradient\\(\\s*at\\s+[^,]+,\\s*(.+?)\\s+0px\\s*,/;\n\n/** `linear-gradient(#c084fc 0 100%)` — the flat base slice. */\nconst LINEAR_GRADIENT_COLOR = /^linear-gradient\\(\\s*(.+?)\\s+0\\s+100%\\s*\\)$/;\n\n/**\n * Split an inline `style` attribute into its declarations.\n *\n * Read from the attribute rather than through `getCssValue()` on purpose. jsdom echoes\n * an inline custom property back through `getComputedStyle`, so the computed read passes\n * here and would answer something else entirely in a browser — where the stylesheet's\n * own per-theme default resolves and \"the author left this to the theme\" quietly becomes\n * a colour. The attribute holds what the component itself wrote, and nothing else.\n */\nfunction parseInlineDeclarations(style: string): Map<string, string> {\n const declarations = new Map<string, string>();\n for (const declaration of style.split(';')) {\n const colon = declaration.indexOf(':');\n if (colon === -1) continue;\n const name = declaration.slice(0, colon).trim();\n if (name !== '') declarations.set(name, declaration.slice(colon + 1).trim());\n }\n return declarations;\n}\n\n/**\n * Test harness for `<wr-border-glow>` — a card whose border lights up under the cursor.\n *\n * **Nearly all of this component is a bag of custom properties written onto the host,\n * and that is what the harness reads.** The paint itself — the conic mask, the stacked\n * `box-shadow` halo, the soft-light bloom — lives in the stylesheet, so a unit test can\n * never see it. What it CAN see is the arithmetic that feeds it, and that is where the\n * breakages are: the seven-step alpha ramp {@link getGlowAlphaRamp} derives from\n * `glowIntensity`, the `[0,1,2,0,1,2,1]` palette mapping in {@link getGradientColors}\n * with its clamp for short palettes, and the pointer maths behind\n * {@link getPointerAngle} / {@link getEdgeProximity}.\n *\n * **Every custom-property getter reads the inline declaration** — see\n * {@link parseInlineDeclarations} for why a computed read would be a different, and\n * wrong, question.\n *\n * **A colour getter answering `null` means the component has never written that\n * variable**, which is the contract when the input is unset: the stylesheet's per-theme\n * default takes over. Note that it does not un-write them either — setting `[glowColor]`\n * and then putting it back to `null` leaves the last value on the host — so `null` is\n * \"never set\", not \"not set now\".\n *\n * There is deliberately no `getGlowOpacity()`, `isGlowVisible()` or `isHovered()`: the\n * visible strength is a `calc()` in the stylesheet gated behind `:not(:hover)`, and\n * jsdom loads no stylesheet and matches no `:hover`, so any number would be invented.\n * There is no measured halo extent either — `.wr-border-glow__edge` is 0×0 in a test,\n * and {@link getGlowRadius} reads what the component asked for instead. And there is no\n * `getSweepProgress()` / `waitForSweep()`: the mount sweep is a four-second chain of\n * `requestAnimationFrame` tweens, so awaiting it either hangs the spec or samples one\n * frame and reports a value that was never settled. {@link isSweeping} answers the one\n * durable fact — that the sweep started.\n *\n * @example\n * ```ts\n * const card = await loader.getHarness(WrBorderGlowHarness.with({ text: /Hover me/ }));\n *\n * expect(await card.getBorderRadius()).toBe(28);\n * expect(await card.getPointerAngle()).toBeNull(); // nothing has pointed at it yet\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrBorderGlowHarness extends ComponentHarness {\n static hostSelector = 'wr-border-glow';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrBorderGlowHarnessFilters = {}): HarnessPredicate<WrBorderGlowHarness> {\n return new HarnessPredicate(WrBorderGlowHarness, options)\n .addOption('text', options.text, (harness, text) =>\n HarnessPredicate.stringMatches(harness.getContentText(), text)\n )\n .addOption('sweeping', options.sweeping, async (harness, sweeping) => (await harness.isSweeping()) === sweeping);\n }\n\n private readonly edge = this.locatorFor('.wr-border-glow__edge');\n\n /** The corner radius the stylesheet is told to round to, in pixels. */\n async getBorderRadius(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-radius', 'getBorderRadius');\n }\n\n /** How far past the card edge the halo is told to reach, in pixels. */\n async getGlowRadius(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-padding', 'getGlowRadius');\n }\n\n /**\n * The width of the lit cone, as a share of the perimeter.\n *\n * Unitless by contract — the stylesheet multiplies it — so a `px` creeping in here is\n * a real regression that leaves the mask with a length where it wants a number.\n */\n async getConeSpread(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-cone-spread', 'getConeSpread');\n }\n\n /** How sharply the halo fades as the cursor leaves an edge; lower is a wider falloff. */\n async getEdgeSensitivity(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-edge-sensitivity', 'getEdgeSensitivity');\n }\n\n /** Strength of the interior soft-light bloom, again unitless. */\n async getFillOpacity(): Promise<number> {\n return this.readNumberVar('--wr-border-glow-fill-opacity', 'getFillOpacity');\n }\n\n /**\n * The card fill the author asked for, or `null` when they left it to the theme.\n *\n * The distinction is the whole point: an unset `[backgroundColor]` means the surface\n * token decides, light or dark, and a card that starts declaring its own fill has\n * stopped following the theme.\n */\n async getBackgroundColor(): Promise<string | null> {\n return this.readVar('--wr-border-glow-bg');\n }\n\n /**\n * The halo colour at full strength, or `null` when the theme decides.\n *\n * Comes back as the `hsl(H deg S% L% / A%)` the component assembled from the bare\n * `'H S L'` input, so this also pins that assembly. Be aware the parse is forgiving to\n * a fault: anything that is not three bare numbers — a hex colour, `rgb(…)`, even\n * `hsl(40deg 80% 80%)` with its units — silently becomes the default amber, so a\n * plausible-looking value here does not mean the input was understood.\n */\n async getGlowColor(): Promise<string | null> {\n return this.readVar('--wr-border-glow-color');\n }\n\n /**\n * The seven halo alphas in percent, brightest first, or `null` when no glow colour\n * was ever set.\n *\n * This is the whole of `[glowIntensity]`: the steps are `[100, 60, 50, 40, 30, 20,\n * 10]` multiplied by the intensity and clamped at 100, so `0.5` gives\n * `[50, 30, 25, 20, 15, 10, 5]` and anything above 1 saturates from the top down. The\n * clamp is one-sided — a negative intensity produces negative percentages, which a\n * browser drops as invalid declarations and the halo vanishes — so the numbers are\n * returned as written rather than corrected, which is how a spec can see it.\n */\n async getGlowAlphaRamp(): Promise<number[] | null> {\n const declarations = await this.declarations();\n if (!declarations.has('--wr-border-glow-color')) return null;\n\n return GLOW_VAR_SUFFIXES.map(suffix => {\n const name = `--wr-border-glow-color${suffix}`;\n const raw = declarations.get(name);\n const alpha = raw === undefined ? null : GLOW_ALPHA.exec(raw);\n if (!alpha) {\n throw new Error(\n `WrBorderGlowHarness.getGlowAlphaRamp(): \\`${name}\\` reads \"${raw ?? '(absent)'}\". The seven halo steps ` +\n 'are written together from one glow colour, so a step that is missing or carries no ` / N%` alpha ' +\n 'means the ramp stopped being built — the halo would fade in bands the stylesheet cannot use.'\n );\n }\n return Number.parseFloat(alpha[1]);\n });\n }\n\n /**\n * The seven mesh-gradient colours, in slot order, or `null` when no palette was set.\n *\n * The slots do not map one-to-one onto the palette: they take colours\n * `[0, 1, 2, 0, 1, 2, 1]`, each index clamped to the last entry the palette actually\n * has. So a two-colour palette repeats the second colour rather than reading past the\n * end — which is the case worth a spec, since `undefined` in a `radial-gradient()`\n * kills the whole declaration silently.\n */\n async getGradientColors(): Promise<string[] | null> {\n const declarations = await this.declarations();\n if (!declarations.has('--wr-border-glow-gradient-one')) return null;\n\n return GRADIENT_VAR_SLOTS.map(slot => {\n const name = `--wr-border-glow-gradient-${slot}`;\n const raw = declarations.get(name);\n const colour = raw === undefined ? null : RADIAL_GRADIENT_COLOR.exec(raw);\n if (!colour) {\n throw new Error(\n `WrBorderGlowHarness.getGradientColors(): \\`${name}\\` reads \"${raw ?? '(absent)'}\", which is not the ` +\n '`radial-gradient(at X% Y%, <colour> 0px, …)` the component builds. All seven slots are written from ' +\n 'one palette, so one of them being different means the mapping broke, not the palette.'\n );\n }\n return colour[1];\n });\n }\n\n /**\n * The flat colour behind the mesh, or `null` when no palette was set.\n *\n * Always the palette's FIRST entry. Worth its own method because the base slice is the\n * one that shows where the gradients are transparent, and it drifting to the last\n * colour changes the card's whole cast without touching a gradient.\n */\n async getBaseGradientColor(): Promise<string | null> {\n const raw = await this.readVar('--wr-border-glow-gradient-base');\n if (raw === null) return null;\n\n const colour = LINEAR_GRADIENT_COLOR.exec(raw);\n if (!colour) {\n throw new Error(\n `WrBorderGlowHarness.getBaseGradientColor(): \\`--wr-border-glow-gradient-base\\` reads \"${raw}\", which is ` +\n 'not the `linear-gradient(<colour> 0 100%)` the component builds from the palette.'\n );\n }\n return colour[1];\n }\n\n /**\n * Where the cursor last sat, in degrees: `0` is straight up and the angle grows\n * clockwise. `null` until something has pointed at the card.\n *\n * `null` is a state of its own rather than a stand-in for zero — the stylesheet's own\n * default applies until the first pointer arrives, and dead centre legitimately\n * reports `0`, so a harness that folded the two together could not tell an untouched\n * card from one being pointed at from the middle.\n */\n async getPointerAngle(): Promise<number | null> {\n const raw = await this.readVar('--wr-border-glow-angle');\n return raw === null ? null : Number.parseFloat(raw);\n }\n\n /**\n * How close the cursor is to the perimeter: `0` at dead centre, `100` at the edge.\n * `null` until something has pointed at the card.\n *\n * On a diagonal the NEARER axis wins rather than the two averaging, so a cursor level\n * with the bottom edge reads 100 wherever it is along it.\n */\n async getEdgeProximity(): Promise<number | null> {\n const raw = await this.readVar('--wr-border-glow-edge-proximity');\n return raw === null ? null : Number.parseFloat(raw);\n }\n\n /**\n * Point at the card, in client coordinates.\n *\n * **Refuses on a card with no box, which in a unit test is every card.** The component\n * measures the pointer against its own centre, and with a 0×0 rect that maths\n * degenerates: the centre is the top-left corner, and `1 / Math.min(0, 0)` pins the\n * proximity at 100 for every position on the page. Both would come back as confident\n * numbers describing nothing. Give the host a box first, exactly as the component's own\n * spec does:\n *\n * ```ts\n * const card = fixture.nativeElement.querySelector('wr-border-glow');\n * card.getBoundingClientRect = () => ({ left: 0, top: 0, width: 200, height: 100 }) as DOMRect;\n * ```\n *\n * A `pointermove` is dispatched on the host itself, so it needs no hit test and cannot\n * miss — the coordinates only ever reach the component's own arithmetic.\n */\n async movePointerTo(clientX: number, clientY: number): Promise<void> {\n const host = await this.host();\n const { width, height } = await host.getDimensions();\n if (!width || !height) {\n throw new Error(\n `WrBorderGlowHarness.movePointerTo(${clientX}, ${clientY}): this card measures ${width}×${height}, so a ` +\n 'pointer position over it means nothing — the component reads the cursor against the card centre, and ' +\n 'with no box the centre is the top-left corner and the proximity saturates for every coordinate. Stub ' +\n '`getBoundingClientRect` on the host first.'\n );\n }\n await host.dispatchEvent('pointermove', { clientX, clientY, pointerId: 1, isPrimary: true });\n }\n\n /**\n * Whether the one-shot mount sweep is running.\n *\n * The class is added synchronously when the sweep starts and removed when it ends\n * about four seconds later, or when the component is torn down — so a spec asserts\n * that it STARTED, and leaves the angle alone while it does. This is also the\n * reduced-motion assertion: `[animated]=\"true\"` for someone who asked for less motion\n * must leave this `false`, and no angle written at all.\n */\n async isSweeping(): Promise<boolean> {\n return (await this.host()).hasClass('wr-border-glow--sweeping');\n }\n\n /** Whether the halo layer is in the markup — the element the whole outer glow paints on. */\n async hasHaloLayer(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-border-glow__edge')()) !== null;\n }\n\n /**\n * Whether the decoration stays out of the accessibility tree.\n *\n * The halo layer is the component's only decorative NODE — every other layer is a\n * pseudo-element and so was never in the tree to begin with. It carries no text and no\n * name, so losing its `aria-hidden` puts an anonymous element between the reader and\n * the card's content.\n */\n async isDecorationHiddenFromAssistiveTech(): Promise<boolean> {\n return (await (await this.edge()).getAttribute('aria-hidden')) === 'true';\n }\n\n /**\n * The projected content's text, trimmed.\n *\n * Read from `.wr-border-glow__inner` rather than the host, which is the assertion:\n * content that lands as a bare host child instead of inside the wrapper renders\n * underneath the interior bloom, and reading the host would report the same string\n * either way.\n */\n async getContentText(): Promise<string> {\n return (await this.locatorFor('.wr-border-glow__inner')()).text();\n }\n\n /** The host's inline declarations — the component's whole written output. */\n private async declarations(): Promise<Map<string, string>> {\n return parseInlineDeclarations((await (await this.host()).getAttribute('style')) ?? '');\n }\n\n /** One inline custom property, or `null` when the component has not written it. */\n private async readVar(name: string): Promise<string | null> {\n return (await this.declarations()).get(name) ?? null;\n }\n\n /** One inline custom property that is always written, as a number. */\n private async readNumberVar(name: string, method: string): Promise<number> {\n const raw = await this.readVar(name);\n const value = raw === null ? Number.NaN : Number.parseFloat(raw);\n if (Number.isNaN(value)) {\n throw new Error(\n `WrBorderGlowHarness.${method}(): \\`${name}\\` reads \"${raw ?? '(absent)'}\" on the host. The component ` +\n 'writes this one on every input change whatever the inputs say, so a missing or unreadable value means ' +\n 'the variable bag stopped reaching the element at all.'\n );\n }\n return value;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ import { 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
+ * Test harness for `<wr-calendar-heatmap>`.
11
+ *
12
+ * **Every square is `aria-hidden`, and that is the design.** The grid is one
13
+ * `role="img"` with a name; a year of individually announced days would be unusable
14
+ * with a screen reader. What each square carries instead is a `title` — its ISO date
15
+ * and value — which is the only text a spec can read, and what
16
+ * {@link getCells} parses.
17
+ *
18
+ * **Four of the seven weekday labels are blank on purpose**: seven do not fit beside
19
+ * a grid this dense. {@link getWeekdayLabels} returns them as they are, empties
20
+ * included, rather than filtering — a spec asserting seven entries with four blanks
21
+ * is pinning the real layout.
22
+ *
23
+ * @see https://ngwr.dev/guides/testing
24
+ */
25
+ class WrCalendarHeatmapHarness extends ComponentHarness {
26
+ static hostSelector = 'wr-calendar-heatmap';
27
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
28
+ static with(options = {}) {
29
+ return new HarnessPredicate(WrCalendarHeatmapHarness, options).addOption('ariaLabel', options.ariaLabel, (harness, label) => HarnessPredicate.stringMatches(harness.getAccessibleName(), label));
30
+ }
31
+ grid = this.locatorFor('.wr-calendar-heatmap__grid');
32
+ /** The role the grid announces — `img`, for the whole picture. */
33
+ async getRole() {
34
+ return (await this.grid()).getAttribute('role');
35
+ }
36
+ /** The grid's accessible name — the only name a reader gets. */
37
+ async getAccessibleName() {
38
+ return (await this.grid()).getAttribute('aria-label');
39
+ }
40
+ /** How many day squares were drawn. */
41
+ async getCellCount() {
42
+ return (await this.locatorForAll('.wr-calendar-heatmap__cell')()).length;
43
+ }
44
+ /**
45
+ * Every square, from its tooltip and its grid placement.
46
+ *
47
+ * The `title` is `"<iso>: <value>"`, which is the component's own format and the
48
+ * only text on a square — everything else about it is colour.
49
+ */
50
+ async getCells() {
51
+ const cells = await this.locatorForAll('.wr-calendar-heatmap__cell')();
52
+ return Promise.all(cells.map(async (cell) => {
53
+ const title = (await cell.getAttribute('title')) ?? '';
54
+ const [iso, value] = title.split(': ');
55
+ return {
56
+ iso: iso ?? '',
57
+ value: value ?? '',
58
+ // `grid-column` / `grid-row` as WRITTEN, not the `-start` longhands: the
59
+ // component sets the shorthand, and jsdom does not expand shorthands, so the
60
+ // longhand reads back empty and every square would land at NaN.
61
+ week: Number.parseInt(await cell.getCssValue('grid-column'), 10),
62
+ day: Number.parseInt(await cell.getCssValue('grid-row'), 10),
63
+ };
64
+ }));
65
+ }
66
+ /** The value a given ISO date's square reports, or `null` when that day is not drawn. */
67
+ async getValueFor(iso) {
68
+ for (const cell of await this.getCells()) {
69
+ if (cell.iso === iso)
70
+ return cell.value;
71
+ }
72
+ return null;
73
+ }
74
+ /** Whether the weekday and month labels are drawn (`showLabels`). */
75
+ async hasLabels() {
76
+ return (await this.locatorForOptional('.wr-calendar-heatmap__weekdays')()) !== null;
77
+ }
78
+ /** The weekday labels as drawn — seven entries, four of them deliberately empty. */
79
+ async getWeekdayLabels() {
80
+ const labels = await this.locatorForAll('.wr-calendar-heatmap__weekday')();
81
+ return Promise.all(labels.map(label => label.text()));
82
+ }
83
+ /** The month labels along the top, in order. */
84
+ async getMonthLabels() {
85
+ const labels = await this.locatorForAll('.wr-calendar-heatmap__month')();
86
+ return Promise.all(labels.map(label => label.text()));
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Generated bundle index. Do not edit.
92
+ */
93
+
94
+ export { WrCalendarHeatmapHarness };
95
+ //# sourceMappingURL=ngwr-calendar-heatmap-testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngwr-calendar-heatmap-testing.mjs","sources":["../../../projects/lib/calendar-heatmap/testing/wr-calendar-heatmap-harness.ts","../../../projects/lib/calendar-heatmap/testing/ngwr-calendar-heatmap-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrCalendarHeatmapCell, WrCalendarHeatmapHarnessFilters } from './interfaces';\n\n/**\n * Test harness for `<wr-calendar-heatmap>`.\n *\n * **Every square is `aria-hidden`, and that is the design.** The grid is one\n * `role=\"img\"` with a name; a year of individually announced days would be unusable\n * with a screen reader. What each square carries instead is a `title` — its ISO date\n * and value — which is the only text a spec can read, and what\n * {@link getCells} parses.\n *\n * **Four of the seven weekday labels are blank on purpose**: seven do not fit beside\n * a grid this dense. {@link getWeekdayLabels} returns them as they are, empties\n * included, rather than filtering — a spec asserting seven entries with four blanks\n * is pinning the real layout.\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrCalendarHeatmapHarness extends ComponentHarness {\n static hostSelector = 'wr-calendar-heatmap';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrCalendarHeatmapHarnessFilters = {}): HarnessPredicate<WrCalendarHeatmapHarness> {\n return new HarnessPredicate(WrCalendarHeatmapHarness, options).addOption(\n 'ariaLabel',\n options.ariaLabel,\n (harness, label) => HarnessPredicate.stringMatches(harness.getAccessibleName(), label)\n );\n }\n\n private readonly grid = this.locatorFor('.wr-calendar-heatmap__grid');\n\n /** The role the grid announces — `img`, for the whole picture. */\n async getRole(): Promise<string | null> {\n return (await this.grid()).getAttribute('role');\n }\n\n /** The grid's accessible name — the only name a reader gets. */\n async getAccessibleName(): Promise<string | null> {\n return (await this.grid()).getAttribute('aria-label');\n }\n\n /** How many day squares were drawn. */\n async getCellCount(): Promise<number> {\n return (await this.locatorForAll('.wr-calendar-heatmap__cell')()).length;\n }\n\n /**\n * Every square, from its tooltip and its grid placement.\n *\n * The `title` is `\"<iso>: <value>\"`, which is the component's own format and the\n * only text on a square — everything else about it is colour.\n */\n async getCells(): Promise<WrCalendarHeatmapCell[]> {\n const cells = await this.locatorForAll('.wr-calendar-heatmap__cell')();\n\n return Promise.all(\n cells.map(async cell => {\n const title = (await cell.getAttribute('title')) ?? '';\n const [iso, value] = title.split(': ');\n return {\n iso: iso ?? '',\n value: value ?? '',\n // `grid-column` / `grid-row` as WRITTEN, not the `-start` longhands: the\n // component sets the shorthand, and jsdom does not expand shorthands, so the\n // longhand reads back empty and every square would land at NaN.\n week: Number.parseInt(await cell.getCssValue('grid-column'), 10),\n day: Number.parseInt(await cell.getCssValue('grid-row'), 10),\n };\n })\n );\n }\n\n /** The value a given ISO date's square reports, or `null` when that day is not drawn. */\n async getValueFor(iso: string): Promise<string | null> {\n for (const cell of await this.getCells()) {\n if (cell.iso === iso) return cell.value;\n }\n return null;\n }\n\n /** Whether the weekday and month labels are drawn (`showLabels`). */\n async hasLabels(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-calendar-heatmap__weekdays')()) !== null;\n }\n\n /** The weekday labels as drawn — seven entries, four of them deliberately empty. */\n async getWeekdayLabels(): Promise<string[]> {\n const labels = await this.locatorForAll('.wr-calendar-heatmap__weekday')();\n return Promise.all(labels.map(label => label.text()));\n }\n\n /** The month labels along the top, in order. */\n async getMonthLabels(): Promise<string[]> {\n const labels = await this.locatorForAll('.wr-calendar-heatmap__month')();\n return Promise.all(labels.map(label => label.text()));\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}