@cdevhub/ngx-tw 0.5.0 → 0.6.1

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 (209) hide show
  1. package/README.md +78 -15
  2. package/fesm2022/cdevhub-ngx-tw-accordion.mjs +16 -5
  3. package/fesm2022/cdevhub-ngx-tw-accordion.mjs.map +1 -1
  4. package/fesm2022/cdevhub-ngx-tw-avatar.mjs +26 -15
  5. package/fesm2022/cdevhub-ngx-tw-avatar.mjs.map +1 -1
  6. package/fesm2022/cdevhub-ngx-tw-badge.mjs +17 -5
  7. package/fesm2022/cdevhub-ngx-tw-badge.mjs.map +1 -1
  8. package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs +27 -6
  9. package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs.map +1 -1
  10. package/fesm2022/cdevhub-ngx-tw-button.mjs +23 -11
  11. package/fesm2022/cdevhub-ngx-tw-button.mjs.map +1 -1
  12. package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs +5 -5
  13. package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs.map +1 -1
  14. package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs +3 -3
  15. package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs.map +1 -1
  16. package/fesm2022/cdevhub-ngx-tw-calendar.mjs +427 -122
  17. package/fesm2022/cdevhub-ngx-tw-calendar.mjs.map +1 -1
  18. package/fesm2022/cdevhub-ngx-tw-card.mjs +21 -11
  19. package/fesm2022/cdevhub-ngx-tw-card.mjs.map +1 -1
  20. package/fesm2022/cdevhub-ngx-tw-carousel.mjs +215 -96
  21. package/fesm2022/cdevhub-ngx-tw-carousel.mjs.map +1 -1
  22. package/fesm2022/cdevhub-ngx-tw-checkbox.mjs +108 -64
  23. package/fesm2022/cdevhub-ngx-tw-checkbox.mjs.map +1 -1
  24. package/fesm2022/cdevhub-ngx-tw-code-block.mjs +18 -6
  25. package/fesm2022/cdevhub-ngx-tw-code-block.mjs.map +1 -1
  26. package/fesm2022/cdevhub-ngx-tw-collapsible.mjs +55 -30
  27. package/fesm2022/cdevhub-ngx-tw-collapsible.mjs.map +1 -1
  28. package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs +218 -0
  29. package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs.map +1 -0
  30. package/fesm2022/cdevhub-ngx-tw-combobox.mjs +236 -211
  31. package/fesm2022/cdevhub-ngx-tw-combobox.mjs.map +1 -1
  32. package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs +200 -0
  33. package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs.map +1 -0
  34. package/fesm2022/cdevhub-ngx-tw-command-palette.mjs +34 -7
  35. package/fesm2022/cdevhub-ngx-tw-command-palette.mjs.map +1 -1
  36. package/fesm2022/cdevhub-ngx-tw-core.mjs +135 -15
  37. package/fesm2022/cdevhub-ngx-tw-core.mjs.map +1 -1
  38. package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs +203 -0
  39. package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs.map +1 -0
  40. package/fesm2022/cdevhub-ngx-tw-date-picker.mjs +160 -102
  41. package/fesm2022/cdevhub-ngx-tw-date-picker.mjs.map +1 -1
  42. package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs +209 -0
  43. package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs.map +1 -0
  44. package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs +182 -107
  45. package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs.map +1 -1
  46. package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs +122 -0
  47. package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs.map +1 -0
  48. package/fesm2022/cdevhub-ngx-tw-dialog.mjs +11 -2
  49. package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -1
  50. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +1 -1
  51. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -1
  52. package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs +149 -0
  53. package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs.map +1 -0
  54. package/fesm2022/cdevhub-ngx-tw-file-upload.mjs +57 -67
  55. package/fesm2022/cdevhub-ngx-tw-file-upload.mjs.map +1 -1
  56. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +42 -24
  57. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -1
  58. package/fesm2022/cdevhub-ngx-tw-form-field.mjs +114 -21
  59. package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -1
  60. package/fesm2022/cdevhub-ngx-tw-icon.mjs +16 -6
  61. package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -1
  62. package/fesm2022/cdevhub-ngx-tw-input.mjs +52 -61
  63. package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -1
  64. package/fesm2022/cdevhub-ngx-tw-item.mjs +28 -4
  65. package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -1
  66. package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs +168 -0
  67. package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs.map +1 -0
  68. package/fesm2022/cdevhub-ngx-tw-menu.mjs +58 -21
  69. package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -1
  70. package/fesm2022/cdevhub-ngx-tw-number-input.mjs +34 -1
  71. package/fesm2022/cdevhub-ngx-tw-number-input.mjs.map +1 -1
  72. package/fesm2022/cdevhub-ngx-tw-paginator.mjs +95 -45
  73. package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -1
  74. package/fesm2022/cdevhub-ngx-tw-popover.mjs +81 -32
  75. package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -1
  76. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +1 -1
  77. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -1
  78. package/fesm2022/cdevhub-ngx-tw-radio.mjs +131 -98
  79. package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -1
  80. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +207 -42
  81. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -1
  82. package/fesm2022/cdevhub-ngx-tw-select-testing.mjs +165 -0
  83. package/fesm2022/cdevhub-ngx-tw-select-testing.mjs.map +1 -0
  84. package/fesm2022/cdevhub-ngx-tw-select.mjs +386 -210
  85. package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -1
  86. package/fesm2022/{cdevhub-ngx-tw-sheet-sheet-renderer-ByyNluUd.mjs → cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs} +3 -3
  87. package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs.map +1 -0
  88. package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs +129 -0
  89. package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs.map +1 -0
  90. package/fesm2022/cdevhub-ngx-tw-sheet.mjs +23 -7
  91. package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -1
  92. package/fesm2022/cdevhub-ngx-tw-slider.mjs +89 -54
  93. package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -1
  94. package/fesm2022/cdevhub-ngx-tw-sort.mjs +122 -17
  95. package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -1
  96. package/fesm2022/cdevhub-ngx-tw-split.mjs +72 -19
  97. package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -1
  98. package/fesm2022/cdevhub-ngx-tw-stat.mjs +63 -25
  99. package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -1
  100. package/fesm2022/cdevhub-ngx-tw-stepper.mjs +35 -8
  101. package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -1
  102. package/fesm2022/cdevhub-ngx-tw-switch.mjs +54 -48
  103. package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -1
  104. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +12 -15
  105. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -1
  106. package/fesm2022/cdevhub-ngx-tw-table.mjs +74 -13
  107. package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -1
  108. package/fesm2022/cdevhub-ngx-tw-tabs.mjs +84 -30
  109. package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -1
  110. package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs +172 -0
  111. package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs.map +1 -0
  112. package/fesm2022/cdevhub-ngx-tw-tags-input.mjs +107 -71
  113. package/fesm2022/cdevhub-ngx-tw-tags-input.mjs.map +1 -1
  114. package/fesm2022/cdevhub-ngx-tw-textarea.mjs +41 -9
  115. package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -1
  116. package/fesm2022/cdevhub-ngx-tw-theme.mjs +257 -45
  117. package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -1
  118. package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs +201 -0
  119. package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs.map +1 -0
  120. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +184 -77
  121. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -1
  122. package/fesm2022/cdevhub-ngx-tw-timeline.mjs +110 -27
  123. package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -1
  124. package/fesm2022/{cdevhub-ngx-tw-toast-toast-renderer-DSu4YoTy.mjs → cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs} +107 -19
  125. package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs.map +1 -0
  126. package/fesm2022/cdevhub-ngx-tw-toast.mjs +21 -13
  127. package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -1
  128. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +102 -15
  129. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -1
  130. package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs +219 -0
  131. package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs.map +1 -0
  132. package/fesm2022/cdevhub-ngx-tw-transfer.mjs +88 -69
  133. package/fesm2022/cdevhub-ngx-tw-transfer.mjs.map +1 -1
  134. package/fesm2022/cdevhub-ngx-tw-tree.mjs +68 -19
  135. package/fesm2022/cdevhub-ngx-tw-tree.mjs.map +1 -1
  136. package/index.json +1 -1
  137. package/package.json +63 -2
  138. package/theme/_base.css +13 -0
  139. package/theme/_dark.css +111 -36
  140. package/theme/_high-contrast-dark.css +367 -0
  141. package/theme/_high-contrast.css +13 -2
  142. package/theme/_light.css +332 -0
  143. package/theme/_semantic.css +60 -26
  144. package/theme/_typography.css +17 -0
  145. package/theme/index.css +86 -7
  146. package/types/cdevhub-ngx-tw-accordion.d.ts +12 -4
  147. package/types/cdevhub-ngx-tw-avatar.d.ts +10 -3
  148. package/types/cdevhub-ngx-tw-button.d.ts +1 -0
  149. package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +2 -2
  150. package/types/cdevhub-ngx-tw-calendar-testing.d.ts +1 -1
  151. package/types/cdevhub-ngx-tw-calendar.d.ts +179 -73
  152. package/types/cdevhub-ngx-tw-card.d.ts +11 -3
  153. package/types/cdevhub-ngx-tw-carousel.d.ts +89 -20
  154. package/types/cdevhub-ngx-tw-checkbox.d.ts +40 -12
  155. package/types/cdevhub-ngx-tw-code-block.d.ts +11 -2
  156. package/types/cdevhub-ngx-tw-collapsible.d.ts +32 -5
  157. package/types/cdevhub-ngx-tw-combobox-testing.d.ts +157 -0
  158. package/types/cdevhub-ngx-tw-combobox.d.ts +94 -53
  159. package/types/cdevhub-ngx-tw-command-palette-testing.d.ts +136 -0
  160. package/types/cdevhub-ngx-tw-command-palette.d.ts +33 -5
  161. package/types/cdevhub-ngx-tw-core.d.ts +152 -21
  162. package/types/cdevhub-ngx-tw-date-picker-testing.d.ts +141 -0
  163. package/types/cdevhub-ngx-tw-date-picker.d.ts +37 -29
  164. package/types/cdevhub-ngx-tw-date-range-picker-testing.d.ts +148 -0
  165. package/types/cdevhub-ngx-tw-date-range-picker.d.ts +36 -29
  166. package/types/cdevhub-ngx-tw-dialog-testing.d.ts +98 -0
  167. package/types/cdevhub-ngx-tw-dialog.d.ts +12 -3
  168. package/types/cdevhub-ngx-tw-file-upload-testing.d.ts +120 -0
  169. package/types/cdevhub-ngx-tw-file-upload.d.ts +8 -10
  170. package/types/cdevhub-ngx-tw-flip-card.d.ts +18 -11
  171. package/types/cdevhub-ngx-tw-form-field.d.ts +19 -2
  172. package/types/cdevhub-ngx-tw-icon.d.ts +3 -3
  173. package/types/cdevhub-ngx-tw-input.d.ts +5 -8
  174. package/types/cdevhub-ngx-tw-item.d.ts +2 -0
  175. package/types/cdevhub-ngx-tw-menu-testing.d.ts +131 -0
  176. package/types/cdevhub-ngx-tw-menu.d.ts +2 -2
  177. package/types/cdevhub-ngx-tw-number-input.d.ts +20 -0
  178. package/types/cdevhub-ngx-tw-paginator.d.ts +58 -24
  179. package/types/cdevhub-ngx-tw-popover.d.ts +41 -10
  180. package/types/cdevhub-ngx-tw-progress-bar.d.ts +1 -1
  181. package/types/cdevhub-ngx-tw-radio.d.ts +68 -25
  182. package/types/cdevhub-ngx-tw-segmented-control.d.ts +79 -16
  183. package/types/cdevhub-ngx-tw-select-testing.d.ts +114 -0
  184. package/types/cdevhub-ngx-tw-select.d.ts +104 -33
  185. package/types/cdevhub-ngx-tw-sheet-testing.d.ts +104 -0
  186. package/types/cdevhub-ngx-tw-sheet.d.ts +19 -3
  187. package/types/cdevhub-ngx-tw-slider.d.ts +79 -25
  188. package/types/cdevhub-ngx-tw-sort.d.ts +45 -4
  189. package/types/cdevhub-ngx-tw-split.d.ts +32 -2
  190. package/types/cdevhub-ngx-tw-stat.d.ts +14 -4
  191. package/types/cdevhub-ngx-tw-stepper.d.ts +16 -2
  192. package/types/cdevhub-ngx-tw-switch.d.ts +35 -13
  193. package/types/cdevhub-ngx-tw-tab-nav.d.ts +7 -6
  194. package/types/cdevhub-ngx-tw-table.d.ts +53 -10
  195. package/types/cdevhub-ngx-tw-tabs.d.ts +41 -10
  196. package/types/cdevhub-ngx-tw-tags-input-testing.d.ts +135 -0
  197. package/types/cdevhub-ngx-tw-tags-input.d.ts +26 -11
  198. package/types/cdevhub-ngx-tw-textarea.d.ts +3 -2
  199. package/types/cdevhub-ngx-tw-theme.d.ts +245 -39
  200. package/types/cdevhub-ngx-tw-time-picker-testing.d.ts +129 -0
  201. package/types/cdevhub-ngx-tw-time-picker.d.ts +76 -19
  202. package/types/cdevhub-ngx-tw-timeline.d.ts +36 -12
  203. package/types/cdevhub-ngx-tw-toast.d.ts +20 -6
  204. package/types/cdevhub-ngx-tw-tooltip.d.ts +28 -6
  205. package/types/cdevhub-ngx-tw-transfer-testing.d.ts +152 -0
  206. package/types/cdevhub-ngx-tw-transfer.d.ts +21 -8
  207. package/types/cdevhub-ngx-tw-tree.d.ts +6 -1
  208. package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-ByyNluUd.mjs.map +0 -1
  209. package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-DSu4YoTy.mjs.map +0 -1
@@ -0,0 +1,201 @@
1
+ import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
2
+
3
+ /** Position of each field in the rendered `role="spinbutton"` sequence. */
4
+ const FIELD_ORDER = ['hour', 'minute', 'second'];
5
+ /**
6
+ * Harness for `tw-time-picker`.
7
+ *
8
+ * Unlike every other picker in the library this component has **no CDK
9
+ * overlay** — it renders three `input[role="spinbutton"]` fields inline inside
10
+ * a `role="group"`, plus an optional stepper pair, an optional AM/PM
11
+ * radiogroup, and an optional clear button. Nothing here reaches outside the
12
+ * host, so no `documentRootLocatorFactory()` is involved.
13
+ *
14
+ * Fields are addressed by position, not by `aria-label`: every visible string
15
+ * on this component comes from the injectable `TimePickerIntl` and a localized
16
+ * app would break a label-based selector.
17
+ */
18
+ class TimePickerHarness extends ComponentHarness {
19
+ static hostSelector = 'tw-time-picker';
20
+ group = this.locatorFor('[role="group"]');
21
+ fields = this.locatorForAll('input[role="spinbutton"]');
22
+ /**
23
+ * The two stepper buttons, up then down. They are the only buttons inside a
24
+ * plain (role-less) wrapper div — the meridiem buttons live in a
25
+ * `role="radiogroup"` and the clear button is a direct child of the host.
26
+ */
27
+ steppers = this.locatorForAll(':scope > div:not([role]) > button');
28
+ /** The AM then PM radio, present only while `format` is `'12h'`. */
29
+ meridiemButtons = this.locatorForAll('[role="radiogroup"] button[role="radio"]');
30
+ /** Predicate for `locatorFor` / `locatorForAll`. */
31
+ static with(options = {}) {
32
+ return new HarnessPredicate(TimePickerHarness, options)
33
+ .addOption('label', options.label, async (h, label) => HarnessPredicate.stringMatches(await h.getLabel(), label))
34
+ .addOption('disabled', options.disabled, async (h, disabled) => (await h.isDisabled()) === disabled);
35
+ }
36
+ /**
37
+ * The field group's accessible name, from `aria-label` on the
38
+ * `role="group"` wrapper. `null` when the group is named by reference
39
+ * (`aria-labelledby`) instead — a `<tw-form-field>` label does exactly that.
40
+ */
41
+ async getLabel() {
42
+ return (await this.group()).getAttribute('aria-label');
43
+ }
44
+ /** Whether the seconds field is rendered (`showSeconds`). */
45
+ async hasSeconds() {
46
+ return (await this.fields()).length > 2;
47
+ }
48
+ /** Whether the stepper buttons are rendered (`showSteppers`, suppressed at `xs` density). */
49
+ async hasSteppers() {
50
+ return (await this.steppers()).length > 0;
51
+ }
52
+ /**
53
+ * The numeric value of one field, read from its `aria-valuenow`. `null` when
54
+ * the field is blank. In `12h` format the hour is 1–12 and the meridiem is
55
+ * read separately via {@link getMeridiem}.
56
+ */
57
+ async getValue(field) {
58
+ const raw = await (await this.field(field)).getAttribute('aria-valuenow');
59
+ return raw === null || raw === '' ? null : Number(raw);
60
+ }
61
+ /**
62
+ * The spoken text of one field, from `aria-valuetext` — `'Empty'` for a blank
63
+ * field, and the hour additionally carries the meridiem in `12h` format.
64
+ * Exposed because it is what a screen reader announces, which no other read
65
+ * on this harness reproduces.
66
+ */
67
+ async getValueText(field) {
68
+ return (await this.field(field)).getAttribute('aria-valuetext');
69
+ }
70
+ /**
71
+ * Types `value` into one field as two digits, exactly as a user would.
72
+ *
73
+ * The component drives entry from `beforeinput`, buffering one digit at a
74
+ * time, so the first digit lands on its own before the second replaces the
75
+ * buffer. That means a *partial* time may be committed in between — the same
76
+ * intermediate emission a real keystroke pair produces. Pass values inside
77
+ * the field's range (hour 0–23, or 1–12 in `12h` format; minute/second 0–59)
78
+ * or the component will reject the digit that overflows.
79
+ */
80
+ async setValue(field, value) {
81
+ const el = await this.field(field);
82
+ await el.focus();
83
+ const digits = value < 10 ? `0${value}` : `${value}`;
84
+ for (const digit of digits) {
85
+ await el.dispatchEvent('beforeinput', {
86
+ inputType: 'insertText',
87
+ data: digit,
88
+ });
89
+ }
90
+ await this.waitForTasksOutsideAngular();
91
+ }
92
+ /** Clears one field's text, leaving the time incomplete. */
93
+ async clearValue(field) {
94
+ const el = await this.field(field);
95
+ await el.focus();
96
+ await el.setInputValue('');
97
+ await el.dispatchEvent('input', { inputType: 'deleteContentBackward' });
98
+ await this.waitForTasksOutsideAngular();
99
+ }
100
+ /**
101
+ * The current meridiem, or `null` in `24h` format where no radiogroup is
102
+ * rendered.
103
+ */
104
+ async getMeridiem() {
105
+ const buttons = await this.meridiemButtons();
106
+ if (buttons.length === 0)
107
+ return null;
108
+ return (await buttons[0].getAttribute('aria-checked')) === 'true' ? 'AM' : 'PM';
109
+ }
110
+ /**
111
+ * Clicks the AM or PM radio. Throws in `24h` format, where the control does
112
+ * not exist. Selecting the already-selected meridiem is a no-op, as it is for
113
+ * a user.
114
+ */
115
+ async setMeridiem(meridiem) {
116
+ const buttons = await this.meridiemButtons();
117
+ if (buttons.length === 0) {
118
+ throw new Error('TimePickerHarness.setMeridiem: no AM/PM control is rendered. It exists only while `format` is "12h".');
119
+ }
120
+ await buttons[meridiem === 'AM' ? 0 : 1].click();
121
+ await this.waitForTasksOutsideAngular();
122
+ }
123
+ /**
124
+ * Focuses `field` and clicks the up stepper, which steps whichever field
125
+ * holds focus by that field's configured step, wrapping at its bounds.
126
+ * Throws when the steppers are not rendered.
127
+ */
128
+ async stepUp(field = 'hour') {
129
+ await this.clickStepper(field, 0);
130
+ }
131
+ /** Focuses `field` and clicks the down stepper. Throws when the steppers are not rendered. */
132
+ async stepDown(field = 'hour') {
133
+ await this.clickStepper(field, 1);
134
+ }
135
+ /** Whether every field reports `aria-disabled="true"`. */
136
+ async isDisabled() {
137
+ return (await (await this.field('hour')).getAttribute('aria-disabled')) === 'true';
138
+ }
139
+ /** Whether the fields report `aria-invalid="true"`. */
140
+ async isInvalid() {
141
+ return (await (await this.field('hour')).getAttribute('aria-invalid')) === 'true';
142
+ }
143
+ /**
144
+ * Whether the fields report `aria-required="true"`. Reflects the `required`
145
+ * input *and* a `Validators.required` on a bound control.
146
+ */
147
+ async isRequired() {
148
+ return (await (await this.field('hour')).getAttribute('aria-required')) === 'true';
149
+ }
150
+ /**
151
+ * Whether the clear control is rendered. It appears only while `showClear` is
152
+ * on, the picker is enabled and not readonly, and a value is set.
153
+ */
154
+ async hasClearButton() {
155
+ return (await this.clearButton()) !== null;
156
+ }
157
+ /** Clicks the clear control. Throws when none is rendered. */
158
+ async clear() {
159
+ const button = await this.clearButton();
160
+ if (!button) {
161
+ throw new Error('TimePickerHarness.clear: no clear control is rendered. It appears only while `showClear` is on, the picker is enabled and not readonly, and a value is set.');
162
+ }
163
+ await button.click();
164
+ await this.waitForTasksOutsideAngular();
165
+ }
166
+ /** @internal Resolves one field by its position in the spinbutton sequence. */
167
+ async field(field) {
168
+ const elements = await this.fields();
169
+ const index = FIELD_ORDER.indexOf(field);
170
+ const element = elements[index];
171
+ if (!element) {
172
+ throw new Error(`TimePickerHarness: the "${field}" field is not rendered. The seconds field requires \`showSeconds\`.`);
173
+ }
174
+ return element;
175
+ }
176
+ /**
177
+ * @internal The clear button — the only `<button>` that is a direct child of
178
+ * the host, the steppers and meridiem radios both sitting inside wrapper
179
+ * divs.
180
+ */
181
+ async clearButton() {
182
+ return this.locatorForOptional(':scope > button')();
183
+ }
184
+ /** @internal Focuses a field, then clicks the stepper at `index` (0 = up, 1 = down). */
185
+ async clickStepper(field, index) {
186
+ const buttons = await this.steppers();
187
+ if (buttons.length < 2) {
188
+ throw new Error('TimePickerHarness: the steppers are not rendered. They require `showSteppers` and a density above `xs`.');
189
+ }
190
+ await (await this.field(field)).focus();
191
+ await buttons[index].click();
192
+ await this.waitForTasksOutsideAngular();
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Generated bundle index. Do not edit.
198
+ */
199
+
200
+ export { TimePickerHarness };
201
+ //# sourceMappingURL=cdevhub-ngx-tw-time-picker-testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdevhub-ngx-tw-time-picker-testing.mjs","sources":["../../../projects/ngx-tw/time-picker/testing/time-picker-harness.ts","../../../projects/ngx-tw/time-picker/testing/cdevhub-ngx-tw-time-picker-testing.ts"],"sourcesContent":["import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport type { BaseHarnessFilters, TestElement } from '@angular/cdk/testing';\n\n/**\n * The editable numeric fields, in render order. Mirrored here rather than\n * imported from `@cdevhub/ngx-tw/time-picker` so a test file does not have to\n * pull in the component entry point just to name a field.\n */\nexport type TimePickerHarnessField = 'hour' | 'minute' | 'second';\n\n/** The two meridiem options of the 12h format. */\nexport type TimePickerHarnessMeridiem = 'AM' | 'PM';\n\n/** Filters accepted by `TimePickerHarness.with`. */\nexport interface TimePickerHarnessFilters extends BaseHarnessFilters {\n /** Match by the field group's accessible name. */\n label?: string | RegExp;\n /** Match disabled / enabled time pickers. */\n disabled?: boolean;\n}\n\n/** Position of each field in the rendered `role=\"spinbutton\"` sequence. */\nconst FIELD_ORDER: readonly TimePickerHarnessField[] = ['hour', 'minute', 'second'];\n\n/**\n * Harness for `tw-time-picker`.\n *\n * Unlike every other picker in the library this component has **no CDK\n * overlay** — it renders three `input[role=\"spinbutton\"]` fields inline inside\n * a `role=\"group\"`, plus an optional stepper pair, an optional AM/PM\n * radiogroup, and an optional clear button. Nothing here reaches outside the\n * host, so no `documentRootLocatorFactory()` is involved.\n *\n * Fields are addressed by position, not by `aria-label`: every visible string\n * on this component comes from the injectable `TimePickerIntl` and a localized\n * app would break a label-based selector.\n */\nexport class TimePickerHarness extends ComponentHarness {\n static hostSelector = 'tw-time-picker';\n\n private readonly group = this.locatorFor('[role=\"group\"]');\n private readonly fields = this.locatorForAll('input[role=\"spinbutton\"]');\n /**\n * The two stepper buttons, up then down. They are the only buttons inside a\n * plain (role-less) wrapper div — the meridiem buttons live in a\n * `role=\"radiogroup\"` and the clear button is a direct child of the host.\n */\n private readonly steppers = this.locatorForAll(':scope > div:not([role]) > button');\n /** The AM then PM radio, present only while `format` is `'12h'`. */\n private readonly meridiemButtons = this.locatorForAll(\n '[role=\"radiogroup\"] button[role=\"radio\"]',\n );\n\n /** Predicate for `locatorFor` / `locatorForAll`. */\n static with(options: TimePickerHarnessFilters = {}): HarnessPredicate<TimePickerHarness> {\n return new HarnessPredicate(TimePickerHarness, options)\n .addOption('label', options.label, async (h, label) =>\n HarnessPredicate.stringMatches(await h.getLabel(), label),\n )\n .addOption(\n 'disabled',\n options.disabled,\n async (h, disabled) => (await h.isDisabled()) === disabled,\n );\n }\n\n /**\n * The field group's accessible name, from `aria-label` on the\n * `role=\"group\"` wrapper. `null` when the group is named by reference\n * (`aria-labelledby`) instead — a `<tw-form-field>` label does exactly that.\n */\n async getLabel(): Promise<string | null> {\n return (await this.group()).getAttribute('aria-label');\n }\n\n /** Whether the seconds field is rendered (`showSeconds`). */\n async hasSeconds(): Promise<boolean> {\n return (await this.fields()).length > 2;\n }\n\n /** Whether the stepper buttons are rendered (`showSteppers`, suppressed at `xs` density). */\n async hasSteppers(): Promise<boolean> {\n return (await this.steppers()).length > 0;\n }\n\n /**\n * The numeric value of one field, read from its `aria-valuenow`. `null` when\n * the field is blank. In `12h` format the hour is 1–12 and the meridiem is\n * read separately via {@link getMeridiem}.\n */\n async getValue(field: TimePickerHarnessField): Promise<number | null> {\n const raw = await (await this.field(field)).getAttribute('aria-valuenow');\n return raw === null || raw === '' ? null : Number(raw);\n }\n\n /**\n * The spoken text of one field, from `aria-valuetext` — `'Empty'` for a blank\n * field, and the hour additionally carries the meridiem in `12h` format.\n * Exposed because it is what a screen reader announces, which no other read\n * on this harness reproduces.\n */\n async getValueText(field: TimePickerHarnessField): Promise<string | null> {\n return (await this.field(field)).getAttribute('aria-valuetext');\n }\n\n /**\n * Types `value` into one field as two digits, exactly as a user would.\n *\n * The component drives entry from `beforeinput`, buffering one digit at a\n * time, so the first digit lands on its own before the second replaces the\n * buffer. That means a *partial* time may be committed in between — the same\n * intermediate emission a real keystroke pair produces. Pass values inside\n * the field's range (hour 0–23, or 1–12 in `12h` format; minute/second 0–59)\n * or the component will reject the digit that overflows.\n */\n async setValue(field: TimePickerHarnessField, value: number): Promise<void> {\n const el = await this.field(field);\n await el.focus();\n const digits = value < 10 ? `0${value}` : `${value}`;\n for (const digit of digits) {\n await el.dispatchEvent('beforeinput', {\n inputType: 'insertText',\n data: digit,\n });\n }\n await this.waitForTasksOutsideAngular();\n }\n\n /** Clears one field's text, leaving the time incomplete. */\n async clearValue(field: TimePickerHarnessField): Promise<void> {\n const el = await this.field(field);\n await el.focus();\n await el.setInputValue('');\n await el.dispatchEvent('input', { inputType: 'deleteContentBackward' });\n await this.waitForTasksOutsideAngular();\n }\n\n /**\n * The current meridiem, or `null` in `24h` format where no radiogroup is\n * rendered.\n */\n async getMeridiem(): Promise<TimePickerHarnessMeridiem | null> {\n const buttons = await this.meridiemButtons();\n if (buttons.length === 0) return null;\n return (await buttons[0].getAttribute('aria-checked')) === 'true' ? 'AM' : 'PM';\n }\n\n /**\n * Clicks the AM or PM radio. Throws in `24h` format, where the control does\n * not exist. Selecting the already-selected meridiem is a no-op, as it is for\n * a user.\n */\n async setMeridiem(meridiem: TimePickerHarnessMeridiem): Promise<void> {\n const buttons = await this.meridiemButtons();\n if (buttons.length === 0) {\n throw new Error(\n 'TimePickerHarness.setMeridiem: no AM/PM control is rendered. It exists only while `format` is \"12h\".',\n );\n }\n await buttons[meridiem === 'AM' ? 0 : 1].click();\n await this.waitForTasksOutsideAngular();\n }\n\n /**\n * Focuses `field` and clicks the up stepper, which steps whichever field\n * holds focus by that field's configured step, wrapping at its bounds.\n * Throws when the steppers are not rendered.\n */\n async stepUp(field: TimePickerHarnessField = 'hour'): Promise<void> {\n await this.clickStepper(field, 0);\n }\n\n /** Focuses `field` and clicks the down stepper. Throws when the steppers are not rendered. */\n async stepDown(field: TimePickerHarnessField = 'hour'): Promise<void> {\n await this.clickStepper(field, 1);\n }\n\n /** Whether every field reports `aria-disabled=\"true\"`. */\n async isDisabled(): Promise<boolean> {\n return (await (await this.field('hour')).getAttribute('aria-disabled')) === 'true';\n }\n\n /** Whether the fields report `aria-invalid=\"true\"`. */\n async isInvalid(): Promise<boolean> {\n return (await (await this.field('hour')).getAttribute('aria-invalid')) === 'true';\n }\n\n /**\n * Whether the fields report `aria-required=\"true\"`. Reflects the `required`\n * input *and* a `Validators.required` on a bound control.\n */\n async isRequired(): Promise<boolean> {\n return (await (await this.field('hour')).getAttribute('aria-required')) === 'true';\n }\n\n /**\n * Whether the clear control is rendered. It appears only while `showClear` is\n * on, the picker is enabled and not readonly, and a value is set.\n */\n async hasClearButton(): Promise<boolean> {\n return (await this.clearButton()) !== null;\n }\n\n /** Clicks the clear control. Throws when none is rendered. */\n async clear(): Promise<void> {\n const button = await this.clearButton();\n if (!button) {\n throw new Error(\n 'TimePickerHarness.clear: no clear control is rendered. It appears only while `showClear` is on, the picker is enabled and not readonly, and a value is set.',\n );\n }\n await button.click();\n await this.waitForTasksOutsideAngular();\n }\n\n /** @internal Resolves one field by its position in the spinbutton sequence. */\n private async field(field: TimePickerHarnessField): Promise<TestElement> {\n const elements = await this.fields();\n const index = FIELD_ORDER.indexOf(field);\n const element = elements[index];\n if (!element) {\n throw new Error(\n `TimePickerHarness: the \"${field}\" field is not rendered. The seconds field requires \\`showSeconds\\`.`,\n );\n }\n return element;\n }\n\n /**\n * @internal The clear button — the only `<button>` that is a direct child of\n * the host, the steppers and meridiem radios both sitting inside wrapper\n * divs.\n */\n private async clearButton(): Promise<TestElement | null> {\n return this.locatorForOptional(':scope > button')();\n }\n\n /** @internal Focuses a field, then clicks the stepper at `index` (0 = up, 1 = down). */\n private async clickStepper(\n field: TimePickerHarnessField,\n index: 0 | 1,\n ): Promise<void> {\n const buttons = await this.steppers();\n if (buttons.length < 2) {\n throw new Error(\n 'TimePickerHarness: the steppers are not rendered. They require `showSteppers` and a density above `xs`.',\n );\n }\n await (await this.field(field)).focus();\n await buttons[index].click();\n await this.waitForTasksOutsideAngular();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}