@cdevhub/ngx-tw 0.5.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.
- package/README.md +78 -15
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs +16 -5
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs +26 -15
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-badge.mjs +17 -5
- package/fesm2022/cdevhub-ngx-tw-badge.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs +27 -6
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-button.mjs +23 -11
- package/fesm2022/cdevhub-ngx-tw-button.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs +5 -5
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs +3 -3
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs +427 -122
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-card.mjs +21 -11
- package/fesm2022/cdevhub-ngx-tw-card.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs +215 -96
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs +108 -64
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs +18 -6
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs +55 -30
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs +218 -0
- package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs +236 -211
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs +200 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs +34 -7
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-core.mjs +135 -15
- package/fesm2022/cdevhub-ngx-tw-core.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs +203 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs +160 -102
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs +209 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs +182 -107
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs +122 -0
- package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs +11 -2
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +1 -1
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs +149 -0
- package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-file-upload.mjs +57 -67
- package/fesm2022/cdevhub-ngx-tw-file-upload.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +42 -24
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs +114 -21
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-icon.mjs +16 -6
- package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-input.mjs +52 -61
- package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-item.mjs +18 -3
- package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs +168 -0
- package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-menu.mjs +58 -21
- package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-number-input.mjs +34 -1
- package/fesm2022/cdevhub-ngx-tw-number-input.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs +95 -45
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-popover.mjs +81 -32
- package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +1 -1
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-radio.mjs +131 -98
- package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +207 -42
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-select-testing.mjs +165 -0
- package/fesm2022/cdevhub-ngx-tw-select-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-select.mjs +386 -210
- package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -1
- package/fesm2022/{cdevhub-ngx-tw-sheet-sheet-renderer-ByyNluUd.mjs → cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs} +3 -3
- package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs +129 -0
- package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs +23 -7
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-slider.mjs +89 -54
- package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-sort.mjs +122 -17
- package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-split.mjs +72 -19
- package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-stat.mjs +63 -25
- package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs +35 -8
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-switch.mjs +54 -48
- package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +12 -15
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-table.mjs +74 -13
- package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs +84 -30
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs +172 -0
- package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tags-input.mjs +107 -71
- package/fesm2022/cdevhub-ngx-tw-tags-input.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs +41 -9
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-theme.mjs +257 -45
- package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs +201 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +184 -77
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs +110 -27
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -1
- package/fesm2022/{cdevhub-ngx-tw-toast-toast-renderer-DSu4YoTy.mjs → cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs} +107 -19
- package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs +21 -13
- package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +102 -15
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs +219 -0
- package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-transfer.mjs +88 -69
- package/fesm2022/cdevhub-ngx-tw-transfer.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tree.mjs +68 -19
- package/fesm2022/cdevhub-ngx-tw-tree.mjs.map +1 -1
- package/index.json +1 -1
- package/package.json +63 -2
- package/theme/_base.css +13 -0
- package/theme/_dark.css +110 -36
- package/theme/_high-contrast-dark.css +367 -0
- package/theme/_high-contrast.css +13 -2
- package/theme/_light.css +276 -0
- package/theme/_semantic.css +47 -12
- package/theme/_typography.css +17 -0
- package/theme/index.css +86 -7
- package/types/cdevhub-ngx-tw-accordion.d.ts +12 -4
- package/types/cdevhub-ngx-tw-avatar.d.ts +10 -3
- package/types/cdevhub-ngx-tw-button.d.ts +1 -0
- package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +2 -2
- package/types/cdevhub-ngx-tw-calendar-testing.d.ts +1 -1
- package/types/cdevhub-ngx-tw-calendar.d.ts +179 -73
- package/types/cdevhub-ngx-tw-card.d.ts +11 -3
- package/types/cdevhub-ngx-tw-carousel.d.ts +89 -20
- package/types/cdevhub-ngx-tw-checkbox.d.ts +40 -12
- package/types/cdevhub-ngx-tw-code-block.d.ts +11 -2
- package/types/cdevhub-ngx-tw-collapsible.d.ts +32 -5
- package/types/cdevhub-ngx-tw-combobox-testing.d.ts +157 -0
- package/types/cdevhub-ngx-tw-combobox.d.ts +94 -53
- package/types/cdevhub-ngx-tw-command-palette-testing.d.ts +136 -0
- package/types/cdevhub-ngx-tw-command-palette.d.ts +33 -5
- package/types/cdevhub-ngx-tw-core.d.ts +152 -21
- package/types/cdevhub-ngx-tw-date-picker-testing.d.ts +141 -0
- package/types/cdevhub-ngx-tw-date-picker.d.ts +37 -29
- package/types/cdevhub-ngx-tw-date-range-picker-testing.d.ts +148 -0
- package/types/cdevhub-ngx-tw-date-range-picker.d.ts +36 -29
- package/types/cdevhub-ngx-tw-dialog-testing.d.ts +98 -0
- package/types/cdevhub-ngx-tw-dialog.d.ts +12 -3
- package/types/cdevhub-ngx-tw-file-upload-testing.d.ts +120 -0
- package/types/cdevhub-ngx-tw-file-upload.d.ts +8 -10
- package/types/cdevhub-ngx-tw-flip-card.d.ts +18 -11
- package/types/cdevhub-ngx-tw-form-field.d.ts +19 -2
- package/types/cdevhub-ngx-tw-icon.d.ts +3 -3
- package/types/cdevhub-ngx-tw-input.d.ts +5 -8
- package/types/cdevhub-ngx-tw-item.d.ts +2 -0
- package/types/cdevhub-ngx-tw-menu-testing.d.ts +131 -0
- package/types/cdevhub-ngx-tw-menu.d.ts +2 -2
- package/types/cdevhub-ngx-tw-number-input.d.ts +20 -0
- package/types/cdevhub-ngx-tw-paginator.d.ts +58 -24
- package/types/cdevhub-ngx-tw-popover.d.ts +41 -10
- package/types/cdevhub-ngx-tw-progress-bar.d.ts +1 -1
- package/types/cdevhub-ngx-tw-radio.d.ts +68 -25
- package/types/cdevhub-ngx-tw-segmented-control.d.ts +79 -16
- package/types/cdevhub-ngx-tw-select-testing.d.ts +114 -0
- package/types/cdevhub-ngx-tw-select.d.ts +104 -33
- package/types/cdevhub-ngx-tw-sheet-testing.d.ts +104 -0
- package/types/cdevhub-ngx-tw-sheet.d.ts +19 -3
- package/types/cdevhub-ngx-tw-slider.d.ts +79 -25
- package/types/cdevhub-ngx-tw-sort.d.ts +45 -4
- package/types/cdevhub-ngx-tw-split.d.ts +32 -2
- package/types/cdevhub-ngx-tw-stat.d.ts +14 -4
- package/types/cdevhub-ngx-tw-stepper.d.ts +16 -2
- package/types/cdevhub-ngx-tw-switch.d.ts +35 -13
- package/types/cdevhub-ngx-tw-tab-nav.d.ts +7 -6
- package/types/cdevhub-ngx-tw-table.d.ts +53 -10
- package/types/cdevhub-ngx-tw-tabs.d.ts +41 -10
- package/types/cdevhub-ngx-tw-tags-input-testing.d.ts +135 -0
- package/types/cdevhub-ngx-tw-tags-input.d.ts +26 -11
- package/types/cdevhub-ngx-tw-textarea.d.ts +3 -2
- package/types/cdevhub-ngx-tw-theme.d.ts +245 -39
- package/types/cdevhub-ngx-tw-time-picker-testing.d.ts +129 -0
- package/types/cdevhub-ngx-tw-time-picker.d.ts +76 -19
- package/types/cdevhub-ngx-tw-timeline.d.ts +36 -12
- package/types/cdevhub-ngx-tw-toast.d.ts +20 -6
- package/types/cdevhub-ngx-tw-tooltip.d.ts +28 -6
- package/types/cdevhub-ngx-tw-transfer-testing.d.ts +152 -0
- package/types/cdevhub-ngx-tw-transfer.d.ts +21 -8
- package/types/cdevhub-ngx-tw-tree.d.ts +6 -1
- package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-ByyNluUd.mjs.map +0 -1
- 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
|
|
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.
|
|
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:
|
|
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
|