@cdevhub/ngx-tw 0.4.0 → 0.6.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 (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-dialog-renderer-DoIhoV3d.mjs +198 -0
  47. package/fesm2022/cdevhub-ngx-tw-dialog-dialog-renderer-DoIhoV3d.mjs.map +1 -0
  48. package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs +122 -0
  49. package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs.map +1 -0
  50. package/fesm2022/cdevhub-ngx-tw-dialog.mjs +199 -220
  51. package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -1
  52. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +1 -1
  53. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -1
  54. package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs +149 -0
  55. package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs.map +1 -0
  56. package/fesm2022/cdevhub-ngx-tw-file-upload.mjs +57 -67
  57. package/fesm2022/cdevhub-ngx-tw-file-upload.mjs.map +1 -1
  58. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +42 -24
  59. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -1
  60. package/fesm2022/cdevhub-ngx-tw-form-field.mjs +114 -21
  61. package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -1
  62. package/fesm2022/cdevhub-ngx-tw-icon.mjs +16 -6
  63. package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -1
  64. package/fesm2022/cdevhub-ngx-tw-input.mjs +52 -61
  65. package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -1
  66. package/fesm2022/cdevhub-ngx-tw-item.mjs +18 -3
  67. package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -1
  68. package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs +168 -0
  69. package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs.map +1 -0
  70. package/fesm2022/cdevhub-ngx-tw-menu.mjs +58 -21
  71. package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -1
  72. package/fesm2022/cdevhub-ngx-tw-number-input.mjs +34 -1
  73. package/fesm2022/cdevhub-ngx-tw-number-input.mjs.map +1 -1
  74. package/fesm2022/cdevhub-ngx-tw-paginator.mjs +95 -45
  75. package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -1
  76. package/fesm2022/cdevhub-ngx-tw-popover.mjs +81 -32
  77. package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -1
  78. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +1 -1
  79. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -1
  80. package/fesm2022/cdevhub-ngx-tw-radio.mjs +131 -98
  81. package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -1
  82. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +207 -42
  83. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -1
  84. package/fesm2022/cdevhub-ngx-tw-select-testing.mjs +165 -0
  85. package/fesm2022/cdevhub-ngx-tw-select-testing.mjs.map +1 -0
  86. package/fesm2022/cdevhub-ngx-tw-select.mjs +386 -210
  87. package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -1
  88. package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs +256 -0
  89. package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs.map +1 -0
  90. package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs +129 -0
  91. package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs.map +1 -0
  92. package/fesm2022/cdevhub-ngx-tw-sheet.mjs +197 -278
  93. package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -1
  94. package/fesm2022/cdevhub-ngx-tw-slider.mjs +89 -54
  95. package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -1
  96. package/fesm2022/cdevhub-ngx-tw-sort.mjs +122 -17
  97. package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -1
  98. package/fesm2022/cdevhub-ngx-tw-split.mjs +72 -19
  99. package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -1
  100. package/fesm2022/cdevhub-ngx-tw-stat.mjs +63 -25
  101. package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -1
  102. package/fesm2022/cdevhub-ngx-tw-stepper.mjs +35 -8
  103. package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -1
  104. package/fesm2022/cdevhub-ngx-tw-switch.mjs +54 -48
  105. package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -1
  106. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +12 -15
  107. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -1
  108. package/fesm2022/cdevhub-ngx-tw-table.mjs +74 -13
  109. package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -1
  110. package/fesm2022/cdevhub-ngx-tw-tabs.mjs +84 -30
  111. package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -1
  112. package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs +172 -0
  113. package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs.map +1 -0
  114. package/fesm2022/cdevhub-ngx-tw-tags-input.mjs +107 -71
  115. package/fesm2022/cdevhub-ngx-tw-tags-input.mjs.map +1 -1
  116. package/fesm2022/cdevhub-ngx-tw-textarea.mjs +41 -9
  117. package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -1
  118. package/fesm2022/cdevhub-ngx-tw-theme.mjs +257 -45
  119. package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -1
  120. package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs +201 -0
  121. package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs.map +1 -0
  122. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +184 -77
  123. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -1
  124. package/fesm2022/cdevhub-ngx-tw-timeline.mjs +110 -27
  125. package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -1
  126. package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs +616 -0
  127. package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs.map +1 -0
  128. package/fesm2022/cdevhub-ngx-tw-toast.mjs +556 -945
  129. package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -1
  130. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +102 -15
  131. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -1
  132. package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs +219 -0
  133. package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs.map +1 -0
  134. package/fesm2022/cdevhub-ngx-tw-transfer.mjs +88 -69
  135. package/fesm2022/cdevhub-ngx-tw-transfer.mjs.map +1 -1
  136. package/fesm2022/cdevhub-ngx-tw-tree.mjs +68 -19
  137. package/fesm2022/cdevhub-ngx-tw-tree.mjs.map +1 -1
  138. package/index.json +1 -1
  139. package/package.json +63 -2
  140. package/theme/_base.css +13 -0
  141. package/theme/_dark.css +110 -36
  142. package/theme/_high-contrast-dark.css +367 -0
  143. package/theme/_high-contrast.css +13 -2
  144. package/theme/_light.css +276 -0
  145. package/theme/_semantic.css +47 -12
  146. package/theme/_typography.css +17 -0
  147. package/theme/index.css +86 -7
  148. package/types/cdevhub-ngx-tw-accordion.d.ts +12 -4
  149. package/types/cdevhub-ngx-tw-avatar.d.ts +10 -3
  150. package/types/cdevhub-ngx-tw-button.d.ts +1 -0
  151. package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +2 -2
  152. package/types/cdevhub-ngx-tw-calendar-testing.d.ts +1 -1
  153. package/types/cdevhub-ngx-tw-calendar.d.ts +179 -73
  154. package/types/cdevhub-ngx-tw-card.d.ts +11 -3
  155. package/types/cdevhub-ngx-tw-carousel.d.ts +89 -20
  156. package/types/cdevhub-ngx-tw-checkbox.d.ts +40 -12
  157. package/types/cdevhub-ngx-tw-code-block.d.ts +11 -2
  158. package/types/cdevhub-ngx-tw-collapsible.d.ts +32 -5
  159. package/types/cdevhub-ngx-tw-combobox-testing.d.ts +157 -0
  160. package/types/cdevhub-ngx-tw-combobox.d.ts +94 -53
  161. package/types/cdevhub-ngx-tw-command-palette-testing.d.ts +136 -0
  162. package/types/cdevhub-ngx-tw-command-palette.d.ts +33 -5
  163. package/types/cdevhub-ngx-tw-core.d.ts +152 -21
  164. package/types/cdevhub-ngx-tw-date-picker-testing.d.ts +141 -0
  165. package/types/cdevhub-ngx-tw-date-picker.d.ts +37 -29
  166. package/types/cdevhub-ngx-tw-date-range-picker-testing.d.ts +148 -0
  167. package/types/cdevhub-ngx-tw-date-range-picker.d.ts +36 -29
  168. package/types/cdevhub-ngx-tw-dialog-testing.d.ts +98 -0
  169. package/types/cdevhub-ngx-tw-dialog.d.ts +89 -19
  170. package/types/cdevhub-ngx-tw-file-upload-testing.d.ts +120 -0
  171. package/types/cdevhub-ngx-tw-file-upload.d.ts +8 -10
  172. package/types/cdevhub-ngx-tw-flip-card.d.ts +18 -11
  173. package/types/cdevhub-ngx-tw-form-field.d.ts +19 -2
  174. package/types/cdevhub-ngx-tw-icon.d.ts +3 -3
  175. package/types/cdevhub-ngx-tw-input.d.ts +5 -8
  176. package/types/cdevhub-ngx-tw-item.d.ts +2 -0
  177. package/types/cdevhub-ngx-tw-menu-testing.d.ts +131 -0
  178. package/types/cdevhub-ngx-tw-menu.d.ts +2 -2
  179. package/types/cdevhub-ngx-tw-number-input.d.ts +20 -0
  180. package/types/cdevhub-ngx-tw-paginator.d.ts +58 -24
  181. package/types/cdevhub-ngx-tw-popover.d.ts +41 -10
  182. package/types/cdevhub-ngx-tw-progress-bar.d.ts +1 -1
  183. package/types/cdevhub-ngx-tw-radio.d.ts +68 -25
  184. package/types/cdevhub-ngx-tw-segmented-control.d.ts +79 -16
  185. package/types/cdevhub-ngx-tw-select-testing.d.ts +114 -0
  186. package/types/cdevhub-ngx-tw-select.d.ts +104 -33
  187. package/types/cdevhub-ngx-tw-sheet-testing.d.ts +104 -0
  188. package/types/cdevhub-ngx-tw-sheet.d.ts +96 -21
  189. package/types/cdevhub-ngx-tw-slider.d.ts +79 -25
  190. package/types/cdevhub-ngx-tw-sort.d.ts +45 -4
  191. package/types/cdevhub-ngx-tw-split.d.ts +32 -2
  192. package/types/cdevhub-ngx-tw-stat.d.ts +14 -4
  193. package/types/cdevhub-ngx-tw-stepper.d.ts +16 -2
  194. package/types/cdevhub-ngx-tw-switch.d.ts +35 -13
  195. package/types/cdevhub-ngx-tw-tab-nav.d.ts +7 -6
  196. package/types/cdevhub-ngx-tw-table.d.ts +53 -10
  197. package/types/cdevhub-ngx-tw-tabs.d.ts +41 -10
  198. package/types/cdevhub-ngx-tw-tags-input-testing.d.ts +135 -0
  199. package/types/cdevhub-ngx-tw-tags-input.d.ts +26 -11
  200. package/types/cdevhub-ngx-tw-textarea.d.ts +3 -2
  201. package/types/cdevhub-ngx-tw-theme.d.ts +245 -39
  202. package/types/cdevhub-ngx-tw-time-picker-testing.d.ts +129 -0
  203. package/types/cdevhub-ngx-tw-time-picker.d.ts +76 -19
  204. package/types/cdevhub-ngx-tw-timeline.d.ts +36 -12
  205. package/types/cdevhub-ngx-tw-toast.d.ts +61 -12
  206. package/types/cdevhub-ngx-tw-tooltip.d.ts +28 -6
  207. package/types/cdevhub-ngx-tw-transfer-testing.d.ts +152 -0
  208. package/types/cdevhub-ngx-tw-transfer.d.ts +21 -8
  209. package/types/cdevhub-ngx-tw-tree.d.ts +6 -1
@@ -0,0 +1,114 @@
1
+ import { BaseHarnessFilters, ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
2
+
3
+ /** Filters accepted by `SelectOptionHarness.with`. */
4
+ interface SelectOptionHarnessFilters extends BaseHarnessFilters {
5
+ /** Match by the option's visible text. */
6
+ text?: string | RegExp;
7
+ /** Match selected / unselected options. */
8
+ selected?: boolean;
9
+ /** Match disabled / enabled options. */
10
+ disabled?: boolean;
11
+ }
12
+ /**
13
+ * Harness for a single option inside an open `tw-select` panel.
14
+ *
15
+ * Options render into the CDK overlay, not inside `tw-select`, so they are
16
+ * reached through {@link SelectHarness} rather than located directly against a
17
+ * fixture.
18
+ */
19
+ declare class SelectOptionHarness extends ComponentHarness {
20
+ static hostSelector: string;
21
+ /** Predicate for `locatorFor` / `locatorForAll`. */
22
+ static with(options?: SelectOptionHarnessFilters): HarnessPredicate<SelectOptionHarness>;
23
+ /** The option's rendered text, trimmed. */
24
+ getText(): Promise<string>;
25
+ /** Whether the option reports `aria-selected="true"`. */
26
+ isSelected(): Promise<boolean>;
27
+ /** Whether the option reports `aria-disabled="true"`. */
28
+ isDisabled(): Promise<boolean>;
29
+ /** Clicks the option. A disabled option ignores the click, as in the browser. */
30
+ click(): Promise<void>;
31
+ }
32
+
33
+ /** Filters accepted by `SelectHarness.with`. */
34
+ interface SelectHarnessFilters extends BaseHarnessFilters {
35
+ /** Match by the trigger's accessible name. */
36
+ label?: string | RegExp;
37
+ /** Match by the text currently displayed in the trigger. */
38
+ triggerText?: string | RegExp;
39
+ /** Match disabled / enabled selects. */
40
+ disabled?: boolean;
41
+ }
42
+ /**
43
+ * Harness for `tw-select`.
44
+ *
45
+ * The options panel renders into the CDK overlay container, outside the
46
+ * `tw-select` host, so this harness resolves it through
47
+ * `documentRootLocatorFactory()`. A consumer can therefore load this harness
48
+ * from the ordinary fixture loader and still reach the options — no
49
+ * `documentRootLoader` ceremony required.
50
+ *
51
+ * The panel is *disposed* on close rather than merely detached, so option
52
+ * harnesses must be re-read after each `open()`. Do not cache them across a
53
+ * close.
54
+ */
55
+ declare class SelectHarness extends ComponentHarness {
56
+ static hostSelector: string;
57
+ private readonly trigger;
58
+ private readonly clearButton;
59
+ /** Resolves the overlay panel, which lives outside this harness's host. */
60
+ private readonly panel;
61
+ /** Predicate for `locatorFor` / `locatorForAll`. */
62
+ static with(options?: SelectHarnessFilters): HarnessPredicate<SelectHarness>;
63
+ /** The trigger's accessible name, from `aria-label` when one is set. */
64
+ getLabel(): Promise<string | null>;
65
+ /**
66
+ * The text currently rendered in the trigger, trimmed. This is the
67
+ * placeholder when the select holds no value.
68
+ */
69
+ getTriggerText(): Promise<string>;
70
+ /** Whether the panel is open, read from the trigger's `aria-expanded`. */
71
+ isOpen(): Promise<boolean>;
72
+ /** Whether the trigger reports `aria-disabled="true"`. */
73
+ isDisabled(): Promise<boolean>;
74
+ /** Whether the trigger reports `aria-invalid="true"`. */
75
+ isInvalid(): Promise<boolean>;
76
+ /** Whether the trigger reports `aria-required="true"`. */
77
+ isRequired(): Promise<boolean>;
78
+ /** Opens the panel. No-op when already open. */
79
+ open(): Promise<void>;
80
+ /** Closes the panel with Escape. No-op when already closed. */
81
+ close(): Promise<void>;
82
+ /**
83
+ * Every option in the open panel. Returns an empty array when the panel is
84
+ * closed, because the overlay is disposed rather than hidden.
85
+ */
86
+ getOptions(filters?: SelectOptionHarnessFilters): Promise<SelectOptionHarness[]>;
87
+ /**
88
+ * Opens the panel if needed and clicks the first option whose text matches.
89
+ * Throws when nothing matches, rather than failing silently.
90
+ */
91
+ selectOption(text: string | RegExp): Promise<void>;
92
+ /**
93
+ * The text of every currently selected option. Opens the panel if needed,
94
+ * because selection state is exposed on the options, which only exist while
95
+ * the panel is attached.
96
+ */
97
+ getSelectedOptionTexts(): Promise<string[]>;
98
+ /**
99
+ * Whether a clear control is rendered. `tw-select` has no `clearable` input —
100
+ * the control appears whenever the select is enabled and holds a value.
101
+ */
102
+ hasClearButton(): Promise<boolean>;
103
+ /**
104
+ * Clicks the clear control. Throws when none is rendered.
105
+ *
106
+ * The control is a native `<button>` and a *sibling* of the trigger, not a
107
+ * descendant — HTML's content model forbids interactive content inside a
108
+ * `<button>`.
109
+ */
110
+ clear(): Promise<void>;
111
+ }
112
+
113
+ export { SelectHarness, SelectOptionHarness };
114
+ export type { SelectHarnessFilters, SelectOptionHarnessFilters };
@@ -143,13 +143,13 @@ declare class SelectFooterTemplateDirective {
143
143
  declare class SelectComponent<T = unknown> implements ControlValueAccessor, FormFieldControl<T | readonly T[]>, OnInit {
144
144
  /** Array of options to render in the panel. Accepts either `TwSelectOption<T>` objects or arbitrary records read via the accessor inputs. Defaults to an empty array. */
145
145
  readonly options: _angular_core.InputSignal<readonly unknown[]>;
146
- /** Accessor returning the visible label for an option. Override when passing arbitrary objects. */
146
+ /** Accessor returning the visible label for an option. Override when passing arbitrary objects. Defaults to reading `.label`, falling back to `String(.value)`. */
147
147
  readonly optionLabel: _angular_core.InputSignal<(option: unknown) => string>;
148
- /** Accessor returning the value for an option. The result is what `value` / `valueChange` emit. */
148
+ /** Accessor returning the value for an option. The result is what `value` / `valueChange` emit. Defaults to reading `.value`. */
149
149
  readonly optionValue: _angular_core.InputSignal<(option: unknown) => T>;
150
150
  /** Accessor returning the disabled state for an option. Defaults to reading `.disabled`. */
151
151
  readonly optionDisabled: _angular_core.InputSignal<(option: unknown) => boolean>;
152
- /** Accessor returning the group name for an option. Options sharing a group render under a labelled `role="group"` region. */
152
+ /** Accessor returning the group name for an option. Options sharing a group render under a labelled `role="group"` region. Defaults to reading `.group`. */
153
153
  readonly optionGroup: _angular_core.InputSignal<(option: unknown) => string | undefined>;
154
154
  /** When true, enables multi-selection. The `value` model becomes a `T[]` and the panel renders checkable options. Defaults to `false`. */
155
155
  readonly multiple: _angular_core.InputSignal<boolean>;
@@ -157,11 +157,11 @@ declare class SelectComponent<T = unknown> implements ControlValueAccessor, Form
157
157
  readonly searchable: _angular_core.InputSignal<boolean>;
158
158
  /** Custom filter function for the search input. Defaults to a case-insensitive substring match on the option label. */
159
159
  readonly filterPredicate: _angular_core.InputSignal<(option: unknown, search: string) => boolean>;
160
- /** Placeholder text shown in the trigger when no value is selected. */
160
+ /** Placeholder text shown in the trigger when no value is selected. Defaults to `undefined`. */
161
161
  readonly placeholder: _angular_core.InputSignal<string | undefined>;
162
- /** When true, the trigger cannot be activated and the panel cannot open. Defaults to `false`. */
162
+ /** When true, the trigger cannot be activated and the panel cannot open. Defaults to `false`. Alias: `disabled`. */
163
163
  readonly disabledInput: _angular_core.InputSignal<boolean>;
164
- /** When true, exposes `aria-required="true"` on the trigger. Defaults to `false`. */
164
+ /** When true, exposes `aria-required="true"` on the trigger and the `*` marker on a wrapping `tw-form-field`. Also inferred from `Validators.required` on a bound control, so a reactive/template-driven form does not have to state it twice. Defaults to `false`. */
165
165
  readonly requiredInput: _angular_core.InputSignal<boolean>;
166
166
  /** Controls trigger padding, font size, and panel option density. Defaults to `'md'`. */
167
167
  readonly size: _angular_core.InputSignal<TwSize>;
@@ -171,7 +171,7 @@ declare class SelectComponent<T = unknown> implements ControlValueAccessor, Form
171
171
  readonly variant: _angular_core.InputSignal<SelectVariant | undefined>;
172
172
  /** Overlay panel width. `'trigger'` matches the trigger's measured width; `'auto'` lets content decide; a number is applied as pixels; a string is passed through as a CSS length. Defaults to `'trigger'`. */
173
173
  readonly panelWidth: _angular_core.InputSignal<string | number>;
174
- /** Extra class(es) applied to the overlay panel element. */
174
+ /** Extra class(es) applied to the overlay panel element. Defaults to an empty string. */
175
175
  readonly panelClass: _angular_core.InputSignal<string | readonly string[]>;
176
176
  /** Maximum height of the listbox scroll region in pixels. Defaults to `256`. */
177
177
  readonly panelMaxHeight: _angular_core.InputSignal<number>;
@@ -181,27 +181,27 @@ declare class SelectComponent<T = unknown> implements ControlValueAccessor, Form
181
181
  readonly scrollStrategy: _angular_core.InputSignal<"reposition" | "close" | "block">;
182
182
  /** Pixel distance between trigger and panel. Defaults to `4`. */
183
183
  readonly offset: _angular_core.InputSignal<number>;
184
- /** Fallback message rendered when the filter yields no options and no `*twSelectEmpty` template is provided. */
184
+ /** Fallback message rendered when the filter yields no options and no `*twSelectEmpty` template is provided. Defaults to `'No results'`. */
185
185
  readonly emptyMessage: _angular_core.InputSignal<string>;
186
- /** Accessible name for the combobox trigger. */
186
+ /** Accessible name for the combobox trigger. Defaults to `undefined`. Alias: `aria-label`. */
187
187
  readonly ariaLabel: _angular_core.InputSignal<string | undefined>;
188
- /** ID of an external element that labels the combobox. */
188
+ /** ID of an external element that labels the combobox. Defaults to `undefined`. Alias: `aria-labelledby`. */
189
189
  readonly ariaLabelledby: _angular_core.InputSignal<string | undefined>;
190
- /** ID of an external element that describes the combobox. */
190
+ /** ID of an external element that describes the combobox. Defaults to `undefined`. Alias: `aria-describedby`. */
191
191
  readonly ariaDescribedby: _angular_core.InputSignal<string | undefined>;
192
192
  /** Per-instance override of the {@link ErrorStateMatcher}. When omitted, the select uses the `TW_ERROR_STATE_MATCHER` token's value. */
193
193
  readonly errorStateMatcher: _angular_core.InputSignal<ErrorStateMatcher | undefined>;
194
194
  /** Equality comparator for option values. Defaults to `Object.is`. */
195
195
  readonly compareWith: _angular_core.InputSignal<(a: T, b: T) => boolean>;
196
- /** Two-way bound selected value(s). Single-select: `T | null`. Multi-select: `T[]`. */
196
+ /** Two-way bound selected value(s). Single-select: `T | null`. Multi-select: `T[]`. Defaults to `null`. */
197
197
  readonly value: _angular_core.ModelSignal<T | readonly T[] | null>;
198
- /** Two-way bound open state of the panel. */
198
+ /** Two-way bound open state of the panel. Defaults to `false`. */
199
199
  readonly open: _angular_core.ModelSignal<boolean>;
200
- /** Fires when the panel's visibility finishes changing. */
200
+ /** Fires when the panel's visibility finishes changing. Payload carries the new `open` state and the trigger element the overlay is anchored to. */
201
201
  readonly openedChange: _angular_core.OutputEmitterRef<TwSelectOpenedEvent>;
202
202
  /** Fires after any selection change, with `added`, `removed`, `previousValue`, and a `source` discriminator. */
203
203
  readonly selectionChange: _angular_core.OutputEmitterRef<TwSelectSelectionChangeEvent<T>>;
204
- /** Fires whenever the search input changes (only when `searchable` is true). */
204
+ /** Fires whenever the search input changes (only when `searchable` is true). Payload carries the raw, untrimmed query and the post-filter `visibleCount`. Emitted immediately, not debounced. */
205
205
  readonly searchChange: _angular_core.OutputEmitterRef<TwSelectSearchEvent>;
206
206
  /** @internal */
207
207
  readonly triggerTemplateChild: Signal<SelectTriggerTemplateDirective<any, any> | undefined>;
@@ -213,7 +213,10 @@ declare class SelectComponent<T = unknown> implements ControlValueAccessor, Form
213
213
  readonly headerTemplateChild: Signal<SelectHeaderTemplateDirective | undefined>;
214
214
  /** @internal */
215
215
  readonly footerTemplateChild: Signal<SelectFooterTemplateDirective | undefined>;
216
+ /** @internal The combobox trigger — focus target after the clear control unmounts. */
217
+ private readonly triggerButtonRef;
216
218
  private readonly overlayService;
219
+ private readonly coordinator;
217
220
  private readonly elementRef;
218
221
  private readonly viewContainerRef;
219
222
  private readonly injector;
@@ -223,9 +226,6 @@ declare class SelectComponent<T = unknown> implements ControlValueAccessor, Form
223
226
  private readonly platform;
224
227
  private readonly formField;
225
228
  private readonly ngControl;
226
- private readonly parentForm;
227
- private readonly parentFormGroup;
228
- private readonly defaultMatcher;
229
229
  private readonly uid;
230
230
  /** @internal */
231
231
  readonly hostId: string;
@@ -244,19 +244,22 @@ declare class SelectComponent<T = unknown> implements ControlValueAccessor, Form
244
244
  /** @internal */
245
245
  readonly focusedSignal: _angular_core.WritableSignal<boolean>;
246
246
  private readonly cvaDisabled;
247
- private readonly _ngControlRev;
248
- private readonly _formSubmitRev;
247
+ /** @internal Shared `errorState` / `required` / `errors` derivation — see `wireErrorState`. */
248
+ private readonly errorWiring;
249
249
  private readonly describedByIdsSignal;
250
250
  private readonly labelledByIdsSignal;
251
- private readonly closingSignal;
252
251
  private onChange;
253
252
  private onTouched;
254
- private overlayRef;
255
- private readonly overlayInstanceSignal;
256
- private perOpenSubs;
257
- private closeTimer;
253
+ /**
254
+ * Watches the host for size changes so `panelWidth="trigger"` keeps tracking
255
+ * while the panel is up. Minted per open and disconnected on close, because
256
+ * the `OverlayRef` it feeds is disposed on close (see `openOverlay`).
257
+ */
258
258
  private resizeObserver;
259
- private get overlayInstance();
259
+ private overlayInstance;
260
+ private closing;
261
+ /** Flips true once the overlay component is attached; the sole trigger for the state-push effect. */
262
+ private readonly isAttached;
260
263
  /** @internal */
261
264
  readonly isDisabled: Signal<boolean>;
262
265
  /** @internal Effective variant — auto-naked when wrapped in tw-form-field. */
@@ -275,6 +278,16 @@ declare class SelectComponent<T = unknown> implements ControlValueAccessor, Form
275
278
  readonly showClearButton: Signal<boolean>;
276
279
  /** @internal */
277
280
  readonly activeDescendantId: Signal<string | null>;
281
+ /**
282
+ * @internal `aria-activedescendant` for the trigger button.
283
+ *
284
+ * Null while a searchable panel is open: `openOverlay()` moves DOM focus to
285
+ * the overlay's search input, and `aria-activedescendant` is only honoured
286
+ * on the element that actually holds focus. Leaving it on the blurred
287
+ * trigger made arrow-key navigation silent to assistive tech. The overlay's
288
+ * search input carries it instead in that mode.
289
+ */
290
+ readonly triggerActiveDescendantId: Signal<string | null>;
278
291
  /** @internal */
279
292
  readonly describedBy: Signal<string>;
280
293
  /** @internal Merged `aria-labelledby`: form-field-pushed label ids + consumer-supplied `aria-labelledby`. */
@@ -295,7 +308,9 @@ declare class SelectComponent<T = unknown> implements ControlValueAccessor, Form
295
308
  /** @internal */
296
309
  readonly clearButtonClasses: Signal<string>;
297
310
  /** @internal */
298
- readonly optionSizeClass: Signal<"px-3 py-1.5 text-sm" | "px-2 py-1 text-xs gap-1.5" | "px-3 py-2 text-sm" | "px-4 py-2.5 text-base" | "px-4 py-3 text-base">;
311
+ readonly clearSpacerClasses: Signal<string>;
312
+ /** @internal */
313
+ readonly optionSizeClass: Signal<"px-2 py-1 text-xs gap-1.5" | "px-3 py-1.5 text-sm" | "px-3 py-2 text-sm" | "px-4 py-2.5 text-base" | "px-4 py-3 text-base">;
299
314
  /** @internal */
300
315
  readonly checkmarkColorClass: Signal<string>;
301
316
  /** @internal Class string applied to each option row. */
@@ -308,8 +323,18 @@ declare class SelectComponent<T = unknown> implements ControlValueAccessor, Form
308
323
  readonly empty: Signal<boolean>;
309
324
  /** @internal */
310
325
  readonly disabled: Signal<boolean>;
311
- /** @internal */
326
+ /**
327
+ * @internal Resolved required state: the `required` input OR'd with
328
+ * `Validators.required` on a bound `NgControl`. Without the validator arm the
329
+ * form-field `*` marker (`FormFieldComponent.isRequired`) and the trigger's
330
+ * `aria-required` silently vanish under reactive/template-driven forms, while
331
+ * signal forms shows them — `cvaControlCreate` writes the `required` *input*
332
+ * directly from the field state and never consults validators. The OR keeps
333
+ * both branches true at once.
334
+ */
312
335
  readonly required: Signal<boolean>;
336
+ /** @internal Active validation errors map from the bound `NgControl`. Drives `[twError match="…"]` inside a wrapping `tw-form-field`; without it the form-field's key set is permanently empty and every `match`ed error stays hidden. */
337
+ readonly errors: Signal<Record<string, unknown> | null>;
313
338
  /** @internal Error-state per the configured `ErrorStateMatcher`. Reads the bound `NgControl.invalid` through the matcher. */
314
339
  readonly errorState: Signal<boolean>;
315
340
  /** @internal */
@@ -336,10 +361,38 @@ declare class SelectComponent<T = unknown> implements ControlValueAccessor, Form
336
361
  selectByVisibleIndex(index: number, source?: TwSelectSelectionSource): void;
337
362
  /** @internal */
338
363
  onTriggerClick(): void;
339
- /** @internal */
364
+ /**
365
+ * @internal
366
+ *
367
+ * `stopPropagation` + `preventDefault` are load-bearing even now that the
368
+ * control is a sibling of the trigger rather than nested inside it: inside a
369
+ * `tw-form-field` the click still bubbles to the field's control wrapper,
370
+ * which calls `onContainerClick()` and would open the panel on a clear.
371
+ */
340
372
  onClearClick(event: MouseEvent): void;
341
- /** @internal */
373
+ /**
374
+ * @internal
375
+ *
376
+ * A native `button` already activates on Enter and Space in a browser, so
377
+ * this looks redundant — it is not. `preventDefault()` here suppresses that
378
+ * native activation, so the two paths cannot both fire, and jsdom never
379
+ * synthesises a click from a key event at all, which makes this the only
380
+ * keyboard path a unit spec can exercise.
381
+ */
342
382
  onClearKeydown(event: KeyboardEvent): void;
383
+ /**
384
+ * Clearing removes the clear control from the DOM (`showClearButton()` goes
385
+ * false), so whatever had focus is destroyed under the user. Hand focus back
386
+ * to the trigger rather than letting it fall to `<body>` (SC 2.4.3).
387
+ *
388
+ * The disabled guard is not redundant. `[disabled]` on a native button makes
389
+ * `.focus()` a silent no-op (the same trap `calendar-cell` hit), and today
390
+ * the only reason the path cannot be reached is that `showClearButton()`
391
+ * happens to include `!isDisabled()`. That is ordering, not a guarantee: a
392
+ * `setDisabledState(true)` landing between the keydown and this call would
393
+ * drop focus to `<body>` with nothing to show for it.
394
+ */
395
+ private focusTrigger;
343
396
  /** @internal */
344
397
  onTriggerKeydown(event: KeyboardEvent): void;
345
398
  /** @internal Handles keyboard events from the trigger AND the search input inside the overlay. */
@@ -354,15 +407,33 @@ declare class SelectComponent<T = unknown> implements ControlValueAccessor, Form
354
407
  initActiveIndexOnOpen(): void;
355
408
  private scrollActiveIntoView;
356
409
  private commitSelection;
410
+ /**
411
+ * Builds and attaches the panel through {@link PickerOverlayCoordinator}.
412
+ *
413
+ * The coordinator disposes the `OverlayRef` on close, so every value read
414
+ * here — `offset`, `scrollStrategy`, `panelClass`, and the position
415
+ * strategy's origin — is re-read on each open rather than frozen at the
416
+ * first one. That replaces the previous `refreshPositionConfig()` /
417
+ * `applyScrollStrategy()` pair, which could only refresh the two inputs it
418
+ * knew about on a reused ref.
419
+ *
420
+ * `focusTrap: false` is load-bearing: this is an `aria-activedescendant`
421
+ * listbox, so DOM focus stays on the trigger (or the panel's search input)
422
+ * and a trap's anchor elements would inject tab stops around the panel.
423
+ */
357
424
  private openOverlay;
358
425
  private closeOverlay;
359
- private ensureOverlay;
360
426
  private installResizeObserver;
361
427
  private updateOverlaySize;
362
- private attachOverlayComponent;
428
+ /**
429
+ * Registers the backdrop-click and Escape listeners for one open.
430
+ *
431
+ * Both streams come from the coordinator, which completes them when the
432
+ * overlay closes — so there is no per-open `Subscription` aggregate to keep
433
+ * and no way for listeners to accumulate across opens.
434
+ */
363
435
  private subscribePerOpen;
364
436
  private resolvePanelClass;
365
- private clearCloseTimer;
366
437
  /** @internal */
367
438
  buildTriggerContext(): TwSelectTriggerContext<T>;
368
439
  writeValue(value: T | readonly T[] | null): void;
@@ -0,0 +1,104 @@
1
+ import { ComponentHarness, BaseHarnessFilters, HarnessPredicate } from '@angular/cdk/testing';
2
+ import { SheetSide } from '@cdevhub/ngx-tw/sheet';
3
+
4
+ /** Filters accepted by `SheetHarness.with`. */
5
+ interface SheetHarnessFilters extends BaseHarnessFilters {
6
+ /** Match by the sheet's id — the same value as `SheetRef.id`. */
7
+ id?: string | RegExp;
8
+ /** Match by the text of the projected `[twSheetTitle]` element. */
9
+ title?: string | RegExp;
10
+ /** Match by the edge the sheet is anchored to. */
11
+ side?: SheetSide;
12
+ }
13
+ /**
14
+ * Harness for a sheet opened with `Sheet.open()`.
15
+ *
16
+ * ## Loading it
17
+ *
18
+ * A sheet is **service-opened**, not template-declared: nothing in the test
19
+ * fixture's own DOM represents it, and the container renders into the CDK
20
+ * overlay container on `document.body`. This harness's host *is* that container,
21
+ * so it must be loaded from
22
+ * `TestbedHarnessEnvironment.documentRootLoader(fixture)` — the ordinary
23
+ * `loader(fixture)` searches inside `fixture.nativeElement` and will never find
24
+ * it. Overlay panels belonging to template-declared components (`tw-select`,
25
+ * `tw-popover-overlay`, `tw-menu`) are the opposite case: those harnesses host
26
+ * on the in-fixture trigger and reach their panel internally, so the plain
27
+ * fixture loader is correct there.
28
+ *
29
+ * ## Waiting for it
30
+ *
31
+ * `Sheet` loads its render layer through a dynamic `import()`, so the container
32
+ * does **not** exist synchronously after `open()`. Await the ref's public
33
+ * {@link SheetRef.whenComponentReady} before asking for the harness — it
34
+ * resolves once the chunk has landed and the overlay is attached, for template
35
+ * sheets (with `null`) as well as component sheets.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const ref = sheet.open(FiltersSheet, { side: 'left' });
40
+ * await ref.whenComponentReady();
41
+ * const rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture);
42
+ * const harness = await rootLoader.getHarness(SheetHarness);
43
+ * expect(await harness.getSide()).toBe('left');
44
+ * ```
45
+ */
46
+ declare class SheetHarness extends ComponentHarness {
47
+ static hostSelector: string;
48
+ private readonly title;
49
+ private readonly content;
50
+ /** Any descendant of the container that currently holds DOM focus. */
51
+ private readonly focusedDescendant;
52
+ /**
53
+ * Overlay backdrops, which are siblings of the sheet's overlay pane rather
54
+ * than descendants of the container — hence the document-root lookup.
55
+ */
56
+ private readonly backdrops;
57
+ /** Predicate for `locatorFor` / `locatorForAll`. */
58
+ static with(options?: SheetHarnessFilters): HarnessPredicate<SheetHarness>;
59
+ /** The sheet's id. Matches the `id` on the `SheetRef` returned by `open()`. */
60
+ getId(): Promise<string | null>;
61
+ /** The sheet's ARIA role — `'dialog'`, or `'alertdialog'` when so configured. */
62
+ getRole(): Promise<string | null>;
63
+ /**
64
+ * The edge the sheet is anchored to, read from the container's `data-side`
65
+ * attribute. This is the one axis a sheet has that a dialog does not.
66
+ */
67
+ getSide(): Promise<SheetSide | null>;
68
+ /**
69
+ * Text of the projected `[twSheetTitle]` element, trimmed, or `null` when the
70
+ * sheet's content does not use the title directive.
71
+ */
72
+ getTitleText(): Promise<string | null>;
73
+ /**
74
+ * Text of the projected `[twSheetContent]` region, trimmed, or `null` when
75
+ * the sheet's content does not use the content directive.
76
+ */
77
+ getContentText(): Promise<string | null>;
78
+ /**
79
+ * Whether DOM focus currently sits on the sheet container or anywhere inside
80
+ * it — the observable consequence of the CDK focus trap. CDK moves focus
81
+ * asynchronously, so let the fixture settle after opening before asserting.
82
+ */
83
+ containsFocus(): Promise<boolean>;
84
+ /** Whether the sheet rendered a backdrop. */
85
+ hasBackdrop(): Promise<boolean>;
86
+ /**
87
+ * Clicks the backdrop — the pointer dismiss gesture. Does nothing observable
88
+ * when the sheet was opened with `disableClose: true` or
89
+ * `closeOnBackdropClick: false`, which is how a test asserts those flags.
90
+ *
91
+ * Throws when no backdrop is rendered. With several overlays stacked, the
92
+ * last backdrop in the overlay container is used, i.e. the topmost one.
93
+ */
94
+ clickBackdrop(): Promise<void>;
95
+ /**
96
+ * Sends Escape to the sheet — the keyboard dismiss gesture. Deliberately a
97
+ * no-op when the sheet was opened with `disableClose: true` or
98
+ * `closeOnEscape: false`.
99
+ */
100
+ pressEscape(): Promise<void>;
101
+ }
102
+
103
+ export { SheetHarness };
104
+ export type { SheetHarnessFilters };