@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,200 @@
1
+ import { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';
2
+
3
+ /**
4
+ * Harness for a single result row in an open `tw-command-palette`.
5
+ *
6
+ * These rows are `role="option"` inside an activedescendant listbox: they never
7
+ * receive DOM focus and have no keyboard handlers of their own. "Active" here
8
+ * therefore means *referenced by the input's `aria-activedescendant`*, which the
9
+ * component mirrors onto each row as `aria-selected`. It does **not** mean
10
+ * `document.activeElement`.
11
+ */
12
+ class CommandPaletteItemHarness extends ComponentHarness {
13
+ static hostSelector = '[role="option"]';
14
+ /** Predicate for `locatorFor` / `locatorForAll`. */
15
+ static with(options = {}) {
16
+ return new HarnessPredicate(CommandPaletteItemHarness, options)
17
+ .addOption('text', options.text, async (harness, text) => HarnessPredicate.stringMatches(await harness.getText(), text))
18
+ .addOption('active', options.active, async (harness, active) => (await harness.isActive()) === active)
19
+ .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);
20
+ }
21
+ /** The row's rendered text, trimmed. Includes the description when one is rendered. */
22
+ async getText() {
23
+ return (await (await this.host()).text()).trim();
24
+ }
25
+ /** The row's DOM id, which is what `aria-activedescendant` points at. */
26
+ async getId() {
27
+ return (await this.host()).getAttribute('id');
28
+ }
29
+ /**
30
+ * Whether this row is the active descendant. Read from `aria-selected`, which
31
+ * the component binds to the active id — not from DOM focus, which never moves
32
+ * off the search input.
33
+ */
34
+ async isActive() {
35
+ return (await (await this.host()).getAttribute('aria-selected')) === 'true';
36
+ }
37
+ /** Whether the row reports `aria-disabled="true"`. */
38
+ async isDisabled() {
39
+ return (await (await this.host()).getAttribute('aria-disabled')) === 'true';
40
+ }
41
+ /** Clicks the row, activating the command. */
42
+ async click() {
43
+ await (await this.host()).click();
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Harness for `tw-command-palette`.
49
+ *
50
+ * The palette renders into the CDK overlay container, outside the
51
+ * `tw-command-palette` host, so this harness resolves the panel through
52
+ * `documentRootLocatorFactory()`. A consumer loads it from the ordinary fixture
53
+ * loader and still reaches the results.
54
+ *
55
+ * The palette is an **activedescendant listbox**: DOM focus stays on the search
56
+ * input and the active row is identified only by `aria-activedescendant`. Every
57
+ * "active" method here resolves that id reference — none of them consults
58
+ * `document.activeElement`, which would always report the input.
59
+ */
60
+ class CommandPaletteHarness extends ComponentHarness {
61
+ static hostSelector = 'tw-command-palette';
62
+ panel = this.documentRootLocatorFactory().locatorForOptional('[role="dialog"]');
63
+ input = this.documentRootLocatorFactory().locatorForOptional('input[role="combobox"]');
64
+ /** Predicate for `locatorFor` / `locatorForAll`. */
65
+ static with(options = {}) {
66
+ return new HarnessPredicate(CommandPaletteHarness, options).addOption('label', options.label, async (harness, label) => HarnessPredicate.stringMatches(await harness.getLabel(), label));
67
+ }
68
+ /** Whether the palette overlay is currently attached. */
69
+ async isOpen() {
70
+ return (await this.panel()) !== null;
71
+ }
72
+ /** The palette's accessible label, or `null` when it is closed. */
73
+ async getLabel() {
74
+ const panel = await this.panel();
75
+ return panel ? panel.getAttribute('aria-label') : null;
76
+ }
77
+ /** The current search query. Returns an empty string when the palette is closed. */
78
+ async getQuery() {
79
+ const input = await this.input();
80
+ return input ? input.getProperty('value') : '';
81
+ }
82
+ /**
83
+ * Types into the search input, replacing any existing query, then waits for
84
+ * the results to settle.
85
+ */
86
+ async setQuery(query) {
87
+ const input = await this.requireInput('setQuery');
88
+ await input.clear();
89
+ await input.sendKeys(query);
90
+ await this.waitForTasksOutsideAngular();
91
+ }
92
+ /** Clears the search query. */
93
+ async clearQuery() {
94
+ const input = await this.requireInput('clearQuery');
95
+ await input.clear();
96
+ await this.waitForTasksOutsideAngular();
97
+ }
98
+ /**
99
+ * Sends Escape to the search input. No-op when the palette is already closed.
100
+ *
101
+ * **The overlay is still attached when this resolves.** The component defers
102
+ * the detach behind a leave animation, so a caller asserting on `isOpen()`
103
+ * immediately afterwards will still see `true`. Wait for the animation before
104
+ * asserting — with a plain timer, not by polling a harness method.
105
+ *
106
+ * That caveat is deliberate rather than hidden behind a poll. An earlier
107
+ * version looped on `isOpen()` until the panel detached, which reads better
108
+ * but is unsound here: every harness call routes through
109
+ * `fixture.whenStable()`, and under zoneless that can wait on a re-scheduled
110
+ * timer and never resolve. A deadline checked *between* awaits cannot bound a
111
+ * single await that never returns, so the loop hung the suite instead of
112
+ * failing it. A harness that can hang is worse than one that makes the caller
113
+ * wait explicitly.
114
+ *
115
+ * **This method is not covered by a spec.** A test driving it hung at the full
116
+ * 15000ms budget in roughly one run in three: the harness calls it makes
117
+ * around a leave animation route through `whenStable()`, which can wait on a
118
+ * re-scheduled timer and never resolve. Escape dismissal itself is covered in
119
+ * `command-palette.spec.ts`, directly against the component.
120
+ */
121
+ async close() {
122
+ const input = await this.input();
123
+ if (!input)
124
+ return;
125
+ await input.sendKeys(TestKey.ESCAPE);
126
+ }
127
+ /** Moves the active descendant down one row. */
128
+ async pressArrowDown() {
129
+ await (await this.requireInput('pressArrowDown')).sendKeys(TestKey.DOWN_ARROW);
130
+ await this.waitForTasksOutsideAngular();
131
+ }
132
+ /** Moves the active descendant up one row. */
133
+ async pressArrowUp() {
134
+ await (await this.requireInput('pressArrowUp')).sendKeys(TestKey.UP_ARROW);
135
+ await this.waitForTasksOutsideAngular();
136
+ }
137
+ /** Activates the current active descendant with Enter. */
138
+ async pressEnter() {
139
+ await (await this.requireInput('pressEnter')).sendKeys(TestKey.ENTER);
140
+ await this.waitForTasksOutsideAngular();
141
+ }
142
+ /**
143
+ * Every result row currently rendered. Returns an empty array when the palette
144
+ * is closed.
145
+ */
146
+ async getItems(filters = {}) {
147
+ if (!(await this.panel()))
148
+ return [];
149
+ return this.documentRootLocatorFactory().locatorForAll(CommandPaletteItemHarness.with(filters))();
150
+ }
151
+ /**
152
+ * The labels of the rendered group headings, in DOM order. Ungrouped results
153
+ * produce an empty array.
154
+ */
155
+ async getGroupLabels() {
156
+ if (!(await this.panel()))
157
+ return [];
158
+ const groups = await this.documentRootLocatorFactory().locatorForAll('[role="group"]')();
159
+ const labels = await Promise.all(groups.map((group) => group.getAttribute('aria-label')));
160
+ return labels.filter((label) => label !== null);
161
+ }
162
+ /**
163
+ * The text of the active row, resolved through the input's
164
+ * `aria-activedescendant`, or `null` when nothing is active.
165
+ */
166
+ async getActiveItemText() {
167
+ const active = await this.getItems({ active: true });
168
+ return active.length > 0 ? active[0].getText() : null;
169
+ }
170
+ /**
171
+ * Clicks the first row whose text matches. Throws when nothing matches, rather
172
+ * than failing silently.
173
+ */
174
+ async selectItem(text) {
175
+ const matches = await this.getItems({ text });
176
+ if (matches.length === 0) {
177
+ throw new Error(`CommandPaletteHarness.selectItem: no result matching ${String(text)}.`);
178
+ }
179
+ await matches[0].click();
180
+ await this.waitForTasksOutsideAngular();
181
+ }
182
+ /**
183
+ * The search input, or a named error when the palette is closed. Every typing
184
+ * and key method needs it, and "cannot read property of null" would not say why.
185
+ */
186
+ async requireInput(method) {
187
+ const input = await this.input();
188
+ if (!input) {
189
+ throw new Error(`CommandPaletteHarness.${method}: the palette is closed, so it has no search input.`);
190
+ }
191
+ return input;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Generated bundle index. Do not edit.
197
+ */
198
+
199
+ export { CommandPaletteHarness, CommandPaletteItemHarness };
200
+ //# sourceMappingURL=cdevhub-ngx-tw-command-palette-testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdevhub-ngx-tw-command-palette-testing.mjs","sources":["../../../projects/ngx-tw/command-palette/testing/command-palette-item-harness.ts","../../../projects/ngx-tw/command-palette/testing/command-palette-harness.ts","../../../projects/ngx-tw/command-palette/testing/cdevhub-ngx-tw-command-palette-testing.ts"],"sourcesContent":["import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport type { BaseHarnessFilters } from '@angular/cdk/testing';\n\n/** Filters accepted by `CommandPaletteItemHarness.with`. */\nexport interface CommandPaletteItemHarnessFilters extends BaseHarnessFilters {\n /** Match by the item's visible text, which includes its description when it has one. */\n text?: string | RegExp;\n /** Match the active (`aria-activedescendant`) item. */\n active?: boolean;\n /** Match disabled / enabled items. */\n disabled?: boolean;\n}\n\n/**\n * Harness for a single result row in an open `tw-command-palette`.\n *\n * These rows are `role=\"option\"` inside an activedescendant listbox: they never\n * receive DOM focus and have no keyboard handlers of their own. \"Active\" here\n * therefore means *referenced by the input's `aria-activedescendant`*, which the\n * component mirrors onto each row as `aria-selected`. It does **not** mean\n * `document.activeElement`.\n */\nexport class CommandPaletteItemHarness extends ComponentHarness {\n static hostSelector = '[role=\"option\"]';\n\n /** Predicate for `locatorFor` / `locatorForAll`. */\n static with(\n options: CommandPaletteItemHarnessFilters = {},\n ): HarnessPredicate<CommandPaletteItemHarness> {\n return new HarnessPredicate(CommandPaletteItemHarness, options)\n .addOption('text', options.text, async (harness, text) =>\n HarnessPredicate.stringMatches(await harness.getText(), text),\n )\n .addOption(\n 'active',\n options.active,\n async (harness, active) => (await harness.isActive()) === active,\n )\n .addOption(\n 'disabled',\n options.disabled,\n async (harness, disabled) => (await harness.isDisabled()) === disabled,\n );\n }\n\n /** The row's rendered text, trimmed. Includes the description when one is rendered. */\n async getText(): Promise<string> {\n return (await (await this.host()).text()).trim();\n }\n\n /** The row's DOM id, which is what `aria-activedescendant` points at. */\n async getId(): Promise<string | null> {\n return (await this.host()).getAttribute('id');\n }\n\n /**\n * Whether this row is the active descendant. Read from `aria-selected`, which\n * the component binds to the active id — not from DOM focus, which never moves\n * off the search input.\n */\n async isActive(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-selected')) === 'true';\n }\n\n /** Whether the row reports `aria-disabled=\"true\"`. */\n async isDisabled(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-disabled')) === 'true';\n }\n\n /** Clicks the row, activating the command. */\n async click(): Promise<void> {\n await (await this.host()).click();\n }\n}\n","import { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\nimport type { BaseHarnessFilters } from '@angular/cdk/testing';\nimport {\n CommandPaletteItemHarness,\n type CommandPaletteItemHarnessFilters,\n} from './command-palette-item-harness';\n\n/** Filters accepted by `CommandPaletteHarness.with`. */\nexport interface CommandPaletteHarnessFilters extends BaseHarnessFilters {\n /** Match by the palette's accessible label. */\n label?: string | RegExp;\n}\n\n/**\n * Harness for `tw-command-palette`.\n *\n * The palette renders into the CDK overlay container, outside the\n * `tw-command-palette` host, so this harness resolves the panel through\n * `documentRootLocatorFactory()`. A consumer loads it from the ordinary fixture\n * loader and still reaches the results.\n *\n * The palette is an **activedescendant listbox**: DOM focus stays on the search\n * input and the active row is identified only by `aria-activedescendant`. Every\n * \"active\" method here resolves that id reference — none of them consults\n * `document.activeElement`, which would always report the input.\n */\nexport class CommandPaletteHarness extends ComponentHarness {\n static hostSelector = 'tw-command-palette';\n\n private readonly panel =\n this.documentRootLocatorFactory().locatorForOptional('[role=\"dialog\"]');\n private readonly input =\n this.documentRootLocatorFactory().locatorForOptional('input[role=\"combobox\"]');\n\n /** Predicate for `locatorFor` / `locatorForAll`. */\n static with(\n options: CommandPaletteHarnessFilters = {},\n ): HarnessPredicate<CommandPaletteHarness> {\n return new HarnessPredicate(CommandPaletteHarness, options).addOption(\n 'label',\n options.label,\n async (harness, label) =>\n HarnessPredicate.stringMatches(await harness.getLabel(), label),\n );\n }\n\n /** Whether the palette overlay is currently attached. */\n async isOpen(): Promise<boolean> {\n return (await this.panel()) !== null;\n }\n\n /** The palette's accessible label, or `null` when it is closed. */\n async getLabel(): Promise<string | null> {\n const panel = await this.panel();\n return panel ? panel.getAttribute('aria-label') : null;\n }\n\n /** The current search query. Returns an empty string when the palette is closed. */\n async getQuery(): Promise<string> {\n const input = await this.input();\n return input ? input.getProperty<string>('value') : '';\n }\n\n /**\n * Types into the search input, replacing any existing query, then waits for\n * the results to settle.\n */\n async setQuery(query: string): Promise<void> {\n const input = await this.requireInput('setQuery');\n await input.clear();\n await input.sendKeys(query);\n await this.waitForTasksOutsideAngular();\n }\n\n /** Clears the search query. */\n async clearQuery(): Promise<void> {\n const input = await this.requireInput('clearQuery');\n await input.clear();\n await this.waitForTasksOutsideAngular();\n }\n\n /**\n * Sends Escape to the search input. No-op when the palette is already closed.\n *\n * **The overlay is still attached when this resolves.** The component defers\n * the detach behind a leave animation, so a caller asserting on `isOpen()`\n * immediately afterwards will still see `true`. Wait for the animation before\n * asserting — with a plain timer, not by polling a harness method.\n *\n * That caveat is deliberate rather than hidden behind a poll. An earlier\n * version looped on `isOpen()` until the panel detached, which reads better\n * but is unsound here: every harness call routes through\n * `fixture.whenStable()`, and under zoneless that can wait on a re-scheduled\n * timer and never resolve. A deadline checked *between* awaits cannot bound a\n * single await that never returns, so the loop hung the suite instead of\n * failing it. A harness that can hang is worse than one that makes the caller\n * wait explicitly.\n *\n * **This method is not covered by a spec.** A test driving it hung at the full\n * 15000ms budget in roughly one run in three: the harness calls it makes\n * around a leave animation route through `whenStable()`, which can wait on a\n * re-scheduled timer and never resolve. Escape dismissal itself is covered in\n * `command-palette.spec.ts`, directly against the component.\n */\n async close(): Promise<void> {\n const input = await this.input();\n if (!input) return;\n await input.sendKeys(TestKey.ESCAPE);\n }\n\n /** Moves the active descendant down one row. */\n async pressArrowDown(): Promise<void> {\n await (await this.requireInput('pressArrowDown')).sendKeys(TestKey.DOWN_ARROW);\n await this.waitForTasksOutsideAngular();\n }\n\n /** Moves the active descendant up one row. */\n async pressArrowUp(): Promise<void> {\n await (await this.requireInput('pressArrowUp')).sendKeys(TestKey.UP_ARROW);\n await this.waitForTasksOutsideAngular();\n }\n\n /** Activates the current active descendant with Enter. */\n async pressEnter(): Promise<void> {\n await (await this.requireInput('pressEnter')).sendKeys(TestKey.ENTER);\n await this.waitForTasksOutsideAngular();\n }\n\n /**\n * Every result row currently rendered. Returns an empty array when the palette\n * is closed.\n */\n async getItems(\n filters: CommandPaletteItemHarnessFilters = {},\n ): Promise<CommandPaletteItemHarness[]> {\n if (!(await this.panel())) return [];\n return this.documentRootLocatorFactory().locatorForAll(\n CommandPaletteItemHarness.with(filters),\n )();\n }\n\n /**\n * The labels of the rendered group headings, in DOM order. Ungrouped results\n * produce an empty array.\n */\n async getGroupLabels(): Promise<string[]> {\n if (!(await this.panel())) return [];\n const groups = await this.documentRootLocatorFactory().locatorForAll(\n '[role=\"group\"]',\n )();\n const labels = await Promise.all(\n groups.map((group) => group.getAttribute('aria-label')),\n );\n return labels.filter((label): label is string => label !== null);\n }\n\n /**\n * The text of the active row, resolved through the input's\n * `aria-activedescendant`, or `null` when nothing is active.\n */\n async getActiveItemText(): Promise<string | null> {\n const active = await this.getItems({ active: true });\n return active.length > 0 ? active[0].getText() : null;\n }\n\n /**\n * Clicks the first row whose text matches. Throws when nothing matches, rather\n * than failing silently.\n */\n async selectItem(text: string | RegExp): Promise<void> {\n const matches = await this.getItems({ text });\n if (matches.length === 0) {\n throw new Error(\n `CommandPaletteHarness.selectItem: no result matching ${String(text)}.`,\n );\n }\n await matches[0].click();\n await this.waitForTasksOutsideAngular();\n }\n\n /**\n * The search input, or a named error when the palette is closed. Every typing\n * and key method needs it, and \"cannot read property of null\" would not say why.\n */\n private async requireInput(method: string) {\n const input = await this.input();\n if (!input) {\n throw new Error(\n `CommandPaletteHarness.${method}: the palette is closed, so it has no search input.`,\n );\n }\n return input;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, input, Directive, output, viewChild, inject, computed, ChangeDetectionStrategy, Component, TemplateRef, model, ViewContainerRef, Injector, DestroyRef, contentChildren, contentChild, signal, linkedSignal, effect, untracked } from '@angular/core';
3
- import { NgTemplateOutlet } from '@angular/common';
2
+ import { InjectionToken, input, Directive, output, viewChild, inject, computed, ChangeDetectionStrategy, Component, TemplateRef, model, ViewContainerRef, Injector, DestroyRef, PLATFORM_ID, contentChildren, contentChild, signal, linkedSignal, effect, untracked } from '@angular/core';
3
+ import { NgTemplateOutlet, DOCUMENT, isPlatformBrowser } from '@angular/common';
4
4
  import { Overlay } from '@angular/cdk/overlay';
5
5
  import { ComponentPortal } from '@angular/cdk/portal';
6
6
  import { FocusTrapFactory, LiveAnnouncer } from '@angular/cdk/a11y';
@@ -8,7 +8,14 @@ import { Subscription } from 'rxjs';
8
8
  import { tv } from 'tailwind-variants';
9
9
 
10
10
  /** Injection token providing a `CommandPaletteRef` to palette overlay content. */
11
- const COMMAND_PALETTE_REF = new InjectionToken('COMMAND_PALETTE_REF');
11
+ const TW_COMMAND_PALETTE_REF = new InjectionToken('TW_COMMAND_PALETTE_REF');
12
+ /**
13
+ * @deprecated Renamed to {@link TW_COMMAND_PALETTE_REF} for consistency with
14
+ * every other ngx-tw injection token. This is the *same token instance*, not a
15
+ * copy — providing under either name and injecting under the other resolves —
16
+ * so the rename is safe to adopt incrementally. Removed in the next major.
17
+ */
18
+ const COMMAND_PALETTE_REF = TW_COMMAND_PALETTE_REF;
12
19
 
13
20
  // ── Constants ──
14
21
  /** Duration of leave animation (ms) — matches scale-out/fade-out in theme/_base.css. */
@@ -35,6 +42,12 @@ const commandPaletteVariants = tv({
35
42
  footer: 'border-t border-border px-4 py-2 bg-surface-muted',
36
43
  },
37
44
  variants: {
45
+ // The search input's `py-*` runs one full step above the item rows
46
+ // (8/10/12/14/16px vs 4/6/8/10/12px) — it is the palette's primary
47
+ // control, not a list row. `lg`'s `py-3.5` is the half-step that keeps
48
+ // that ramp monotonic: `py-3` collides with `md` and `py-4` with `xl`,
49
+ // either of which collapses `lg` into a neighbouring density and leaves
50
+ // the five-step size axis with only four distinct search-field heights.
38
51
  size: {
39
52
  xs: {
40
53
  item: 'py-1 text-xs',
@@ -337,7 +350,7 @@ class CommandPaletteComponent {
337
350
  /** Placeholder text shown inside the search input. Defaults to `'Type a command or search…'`. */
338
351
  placeholder = input('Type a command or search…', /* @ts-ignore */
339
352
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
340
- /** Two-way bindable search query. Reads or resets the current filter. Defaults to `''`. */
353
+ /** Two-way bindable search query. Writes back on every keystroke in the search input. Opening and closing the palette leave it untouched — reset it yourself if each open should start empty. Defaults to `''`. */
341
354
  query = model('', /* @ts-ignore */
342
355
  ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
343
356
  /** Two-way bindable open state. Setting to `true` opens the palette; closing updates it to `false`. Defaults to `false`. */
@@ -382,6 +395,8 @@ class CommandPaletteComponent {
382
395
  destroyRef = inject(DestroyRef);
383
396
  focusTrapFactory = inject(FocusTrapFactory);
384
397
  announcer = inject(LiveAnnouncer);
398
+ document = inject(DOCUMENT);
399
+ isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
385
400
  declarativeItems = contentChildren(CommandPaletteItemDirective, { ...(ngDevMode ? { debugName: "declarativeItems" } : /* istanbul ignore next */ {}), descendants: true });
386
401
  emptyDirective = contentChild(CommandPaletteEmptyDirective, /* @ts-ignore */
387
402
  ...(ngDevMode ? [{ debugName: "emptyDirective" }] : /* istanbul ignore next */ []));
@@ -588,8 +603,20 @@ class CommandPaletteComponent {
588
603
  }
589
604
  // ── Private ──
590
605
  openPalette() {
606
+ // SSR guard. `openPalette()` is reachable from the constructor `effect()`
607
+ // below, and effects run during server render (only `afterRender*` hooks
608
+ // are client-only) — so a consumer deep-linking the palette with a bound
609
+ // `[open]="true"` executed this path on Node. A CDK overlay is a
610
+ // browser-only construct and there is nothing meaningful to serialize, so
611
+ // bail out entirely; the client instance re-runs the same effect during
612
+ // hydration and opens then. Matches `split.ts` / `theme.service.ts`, which
613
+ // gate on the same `isPlatformBrowser(inject(PLATFORM_ID))` flag.
614
+ if (!this.isBrowser)
615
+ return;
616
+ // `document` is injected rather than read as a global so the reference is
617
+ // testable and consistent with the rest of the library.
591
618
  this.previousFocus =
592
- document.activeElement ?? null;
619
+ this.document.activeElement ?? null;
593
620
  this.ensureOverlay();
594
621
  this.attachContent();
595
622
  this.subscribePerOpen();
@@ -655,7 +682,7 @@ class CommandPaletteComponent {
655
682
  parent: this.injector,
656
683
  providers: [
657
684
  { provide: CommandPaletteComponent, useValue: this },
658
- { provide: COMMAND_PALETTE_REF, useValue: paletteRef },
685
+ { provide: TW_COMMAND_PALETTE_REF, useValue: paletteRef },
659
686
  ],
660
687
  });
661
688
  const portal = new ComponentPortal(CommandPaletteOverlayComponent, this.viewContainerRef, contentInjector);
@@ -784,5 +811,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
784
811
  * Generated bundle index. Do not edit.
785
812
  */
786
813
 
787
- export { COMMAND_PALETTE_REF, CommandPaletteComponent, CommandPaletteEmptyDirective, CommandPaletteFooterDirective, CommandPaletteGroupDirective, CommandPaletteItemDescriptionDirective, CommandPaletteItemDirective, CommandPaletteItemIconDirective };
814
+ export { COMMAND_PALETTE_REF, CommandPaletteComponent, CommandPaletteEmptyDirective, CommandPaletteFooterDirective, CommandPaletteGroupDirective, CommandPaletteItemDescriptionDirective, CommandPaletteItemDirective, CommandPaletteItemIconDirective, TW_COMMAND_PALETTE_REF };
788
815
  //# sourceMappingURL=cdevhub-ngx-tw-command-palette.mjs.map