@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.
- 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-dialog-renderer-DoIhoV3d.mjs +198 -0
- package/fesm2022/cdevhub-ngx-tw-dialog-dialog-renderer-DoIhoV3d.mjs.map +1 -0
- 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 +199 -220
- 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-CqVJwgmp.mjs +256 -0
- 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 +197 -278
- 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-BlDxemsU.mjs +616 -0
- package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs +556 -945
- 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 +89 -19
- 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 +96 -21
- 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 +61 -12
- 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
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
2
|
+
import { CalendarHarness } from '@cdevhub/ngx-tw/calendar/testing';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Harness for `tw-date-range-picker`.
|
|
6
|
+
*
|
|
7
|
+
* Unlike `tw-date-picker`, this component has **no text input**: its trigger is
|
|
8
|
+
* a `button[role="combobox"]` that renders the start label, a separator and the
|
|
9
|
+
* end label as three sibling spans (or a single placeholder span when empty).
|
|
10
|
+
* A range is therefore read, never typed — {@link getStartText} /
|
|
11
|
+
* {@link getEndText} read it, and {@link selectRange} sets it through the
|
|
12
|
+
* calendar.
|
|
13
|
+
*
|
|
14
|
+
* The calendar renders into the CDK overlay, outside the
|
|
15
|
+
* `tw-date-range-picker` host, so it is resolved through
|
|
16
|
+
* `documentRootLocatorFactory()`. A consumer loads this harness from the
|
|
17
|
+
* ordinary fixture loader — no `documentRootLoader` ceremony required.
|
|
18
|
+
*
|
|
19
|
+
* Day-grid navigation is not reimplemented here: {@link getCalendar} returns
|
|
20
|
+
* the `CalendarHarness` from `@cdevhub/ngx-tw/calendar/testing`, which already
|
|
21
|
+
* owns cells, view switching and header paging.
|
|
22
|
+
*/
|
|
23
|
+
class DateRangePickerHarness extends ComponentHarness {
|
|
24
|
+
static hostSelector = 'tw-date-range-picker';
|
|
25
|
+
trigger = this.locatorFor('button[role="combobox"]');
|
|
26
|
+
/**
|
|
27
|
+
* The start / separator / end spans, or a single placeholder span when the
|
|
28
|
+
* range is empty. They carry no distinguishing attribute, so position inside
|
|
29
|
+
* the trigger is the only handle — see {@link getStartText}.
|
|
30
|
+
*/
|
|
31
|
+
triggerSpans = this.locatorForAll('button[role="combobox"] span > span');
|
|
32
|
+
/**
|
|
33
|
+
* The inline clear control — the only button in the host that is not the
|
|
34
|
+
* trigger.
|
|
35
|
+
*/
|
|
36
|
+
clearButton = this.locatorForOptional('button:not([role="combobox"])');
|
|
37
|
+
/** Resolves the overlay's calendar, which lives outside this harness's host. */
|
|
38
|
+
calendar = this.documentRootLocatorFactory().locatorForOptional(CalendarHarness);
|
|
39
|
+
/** Predicate for `locatorFor` / `locatorForAll`. */
|
|
40
|
+
static with(options = {}) {
|
|
41
|
+
return new HarnessPredicate(DateRangePickerHarness, options)
|
|
42
|
+
.addOption('label', options.label, async (h, label) => HarnessPredicate.stringMatches(await h.getLabel(), label))
|
|
43
|
+
.addOption('triggerText', options.triggerText, async (h, text) => HarnessPredicate.stringMatches(await h.getTriggerText(), text))
|
|
44
|
+
.addOption('disabled', options.disabled, async (h, disabled) => (await h.isDisabled()) === disabled);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The trigger's accessible name.
|
|
48
|
+
*
|
|
49
|
+
* Note this is *composed*, not the bare `aria-label`: once a complete range
|
|
50
|
+
* is set the component appends the range to it
|
|
51
|
+
* (`"Report period. Current range: Apr 10 to Apr 15."`). Match with a RegExp
|
|
52
|
+
* in {@link with} if you need it to survive a selection.
|
|
53
|
+
*/
|
|
54
|
+
async getLabel() {
|
|
55
|
+
return (await this.trigger()).getAttribute('aria-label');
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The whole text rendered in the trigger, trimmed — start, separator and end
|
|
59
|
+
* run together, or the placeholder when no range is set.
|
|
60
|
+
*/
|
|
61
|
+
async getTriggerText() {
|
|
62
|
+
return (await (await this.trigger()).text()).trim();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The formatted start date, or `null` when no range is set (the trigger then
|
|
66
|
+
* renders a single placeholder span instead of the start/separator/end
|
|
67
|
+
* triple).
|
|
68
|
+
*/
|
|
69
|
+
async getStartText() {
|
|
70
|
+
return this.rangeSpanText(0);
|
|
71
|
+
}
|
|
72
|
+
/** The formatted end date, or `null` when no range is set. */
|
|
73
|
+
async getEndText() {
|
|
74
|
+
return this.rangeSpanText(2);
|
|
75
|
+
}
|
|
76
|
+
/** Whether the calendar overlay is open, read from the trigger's `aria-expanded`. */
|
|
77
|
+
async isOpen() {
|
|
78
|
+
return (await (await this.trigger()).getAttribute('aria-expanded')) === 'true';
|
|
79
|
+
}
|
|
80
|
+
/** Whether the trigger reports `aria-disabled="true"`. */
|
|
81
|
+
async isDisabled() {
|
|
82
|
+
return (await (await this.trigger()).getAttribute('aria-disabled')) === 'true';
|
|
83
|
+
}
|
|
84
|
+
/** Whether the trigger reports `aria-invalid="true"`. */
|
|
85
|
+
async isInvalid() {
|
|
86
|
+
return (await (await this.trigger()).getAttribute('aria-invalid')) === 'true';
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Whether the trigger reports `aria-required="true"`. Reflects the `required`
|
|
90
|
+
* input *and* a `Validators.required` on a bound control.
|
|
91
|
+
*/
|
|
92
|
+
async isRequired() {
|
|
93
|
+
return (await (await this.trigger()).getAttribute('aria-required')) === 'true';
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Opens the calendar overlay by clicking the trigger. No-op when already
|
|
97
|
+
* open; a disabled picker ignores it, as it does in the browser.
|
|
98
|
+
*/
|
|
99
|
+
async open() {
|
|
100
|
+
if (await this.isOpen())
|
|
101
|
+
return;
|
|
102
|
+
await (await this.trigger()).click();
|
|
103
|
+
await this.waitForTasksOutsideAngular();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Closes the calendar overlay by clicking the trigger again. No-op when
|
|
107
|
+
* already closed.
|
|
108
|
+
*
|
|
109
|
+
* Deliberately not Escape: `tw-date-range-picker` treats Escape as *cancel*
|
|
110
|
+
* and restores the range the overlay opened with, which would make `close()`
|
|
111
|
+
* silently undo a selection. The trigger toggles without that side effect.
|
|
112
|
+
*
|
|
113
|
+
* The overlay runs a leave animation before it detaches, so `isOpen()` flips
|
|
114
|
+
* to `false` immediately while the panel element lingers for a few more
|
|
115
|
+
* frames. Two consequences worth knowing:
|
|
116
|
+
*
|
|
117
|
+
* - Do not assert `getCalendar()` is `null` on the tick after a close; the
|
|
118
|
+
* old panel is still mounted.
|
|
119
|
+
* - An `open()` inside that window does **not** attach a fresh panel — the
|
|
120
|
+
* component sees an overlay ref it has not disposed yet and silently
|
|
121
|
+
* keeps the outgoing one. Let the animation finish before reopening.
|
|
122
|
+
*/
|
|
123
|
+
async close() {
|
|
124
|
+
if (!(await this.isOpen()))
|
|
125
|
+
return;
|
|
126
|
+
await (await this.trigger()).click();
|
|
127
|
+
await this.waitForTasksOutsideAngular();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The `CalendarHarness` for the open overlay, or `null` when no overlay is
|
|
131
|
+
* attached. One `tw-calendar` renders all `numberOfMonths` months under a
|
|
132
|
+
* single header, so this is a single harness whatever that input is set to.
|
|
133
|
+
*/
|
|
134
|
+
async getCalendar() {
|
|
135
|
+
return this.calendar();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Opens the overlay if needed and clicks the two cells whose text matches
|
|
139
|
+
* `start` then `end` exactly (`'10'`, not `'April 10'`). Throws when either
|
|
140
|
+
* cell is not visible in the current view.
|
|
141
|
+
*
|
|
142
|
+
* With more than one month rendered a day number appears once per month grid;
|
|
143
|
+
* the first match wins, which is the earliest visible month. Page the
|
|
144
|
+
* calendar via {@link getCalendar} first if you need a later one.
|
|
145
|
+
*
|
|
146
|
+
* With `showActions` off this commits and closes on the second click; with it
|
|
147
|
+
* on the range stays pending until {@link clickAction} confirms it.
|
|
148
|
+
*/
|
|
149
|
+
async selectRange(start, end) {
|
|
150
|
+
await this.open();
|
|
151
|
+
const calendar = await this.getCalendar();
|
|
152
|
+
if (!calendar) {
|
|
153
|
+
throw new Error('DateRangePickerHarness.selectRange: the calendar overlay is not attached.');
|
|
154
|
+
}
|
|
155
|
+
await calendar.selectRange(start, end);
|
|
156
|
+
await this.waitForTasksOutsideAngular();
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Clicks a button in the overlay's action bar (`Today`, `Clear`, `Cancel`,
|
|
160
|
+
* `Apply`) by its visible text. Matching on text rather than position is what
|
|
161
|
+
* keeps this usable when a consumer relabels the actions via `todayLabel` and
|
|
162
|
+
* friends — pass whatever label you configured.
|
|
163
|
+
*
|
|
164
|
+
* Preset entries are excluded: they are `role="option"`, not action buttons.
|
|
165
|
+
* Throws when the overlay is closed or nothing matches.
|
|
166
|
+
*/
|
|
167
|
+
async clickAction(label) {
|
|
168
|
+
const buttons = await this.documentRootLocatorFactory().locatorForAll('tw-date-range-picker-overlay button:not([role="option"])')();
|
|
169
|
+
for (const button of buttons) {
|
|
170
|
+
if (await HarnessPredicate.stringMatches((await button.text()).trim(), label)) {
|
|
171
|
+
await button.click();
|
|
172
|
+
await this.waitForTasksOutsideAngular();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
throw new Error(`DateRangePickerHarness.clickAction: no overlay action button matching ${String(label)}. Is the overlay open and \`showActions\` on?`);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Whether the inline clear control is rendered. It appears only while
|
|
180
|
+
* `showClear` is on, the picker is enabled, and a range is set.
|
|
181
|
+
*/
|
|
182
|
+
async hasClearButton() {
|
|
183
|
+
return (await this.clearButton()) !== null;
|
|
184
|
+
}
|
|
185
|
+
/** Clicks the inline clear control. Throws when none is rendered. */
|
|
186
|
+
async clear() {
|
|
187
|
+
const button = await this.clearButton();
|
|
188
|
+
if (!button) {
|
|
189
|
+
throw new Error('DateRangePickerHarness.clear: no clear control is rendered. It appears only while `showClear` is on, the picker is enabled, and a range is set.');
|
|
190
|
+
}
|
|
191
|
+
await button.click();
|
|
192
|
+
await this.waitForTasksOutsideAngular();
|
|
193
|
+
}
|
|
194
|
+
/** @internal Text of one of the start/separator/end spans, or `null` when the trigger is empty. */
|
|
195
|
+
async rangeSpanText(index) {
|
|
196
|
+
const spans = await this.triggerSpans();
|
|
197
|
+
// One span means the placeholder branch rendered — no range is set.
|
|
198
|
+
if (spans.length < 3)
|
|
199
|
+
return null;
|
|
200
|
+
return (await spans[index].text()).trim();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Generated bundle index. Do not edit.
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
export { DateRangePickerHarness };
|
|
209
|
+
//# sourceMappingURL=cdevhub-ngx-tw-date-range-picker-testing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdevhub-ngx-tw-date-range-picker-testing.mjs","sources":["../../../projects/ngx-tw/date-range-picker/testing/date-range-picker-harness.ts","../../../projects/ngx-tw/date-range-picker/testing/cdevhub-ngx-tw-date-range-picker-testing.ts"],"sourcesContent":["import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport type { BaseHarnessFilters, TestElement } from '@angular/cdk/testing';\nimport { CalendarHarness } from '@cdevhub/ngx-tw/calendar/testing';\n\n/** Filters accepted by `DateRangePickerHarness.with`. */\nexport interface DateRangePickerHarnessFilters extends BaseHarnessFilters {\n /** Match by the trigger's accessible name. */\n label?: string | RegExp;\n /** Match by the whole text currently rendered in the trigger. */\n triggerText?: string | RegExp;\n /** Match disabled / enabled pickers. */\n disabled?: boolean;\n}\n\n/**\n * Harness for `tw-date-range-picker`.\n *\n * Unlike `tw-date-picker`, this component has **no text input**: its trigger is\n * a `button[role=\"combobox\"]` that renders the start label, a separator and the\n * end label as three sibling spans (or a single placeholder span when empty).\n * A range is therefore read, never typed — {@link getStartText} /\n * {@link getEndText} read it, and {@link selectRange} sets it through the\n * calendar.\n *\n * The calendar renders into the CDK overlay, outside the\n * `tw-date-range-picker` host, so it is resolved through\n * `documentRootLocatorFactory()`. A consumer loads this harness from the\n * ordinary fixture loader — no `documentRootLoader` ceremony required.\n *\n * Day-grid navigation is not reimplemented here: {@link getCalendar} returns\n * the `CalendarHarness` from `@cdevhub/ngx-tw/calendar/testing`, which already\n * owns cells, view switching and header paging.\n */\nexport class DateRangePickerHarness extends ComponentHarness {\n static hostSelector = 'tw-date-range-picker';\n\n private readonly trigger = this.locatorFor('button[role=\"combobox\"]');\n /**\n * The start / separator / end spans, or a single placeholder span when the\n * range is empty. They carry no distinguishing attribute, so position inside\n * the trigger is the only handle — see {@link getStartText}.\n */\n private readonly triggerSpans = this.locatorForAll(\n 'button[role=\"combobox\"] span > span',\n );\n /**\n * The inline clear control — the only button in the host that is not the\n * trigger.\n */\n private readonly clearButton = this.locatorForOptional(\n 'button:not([role=\"combobox\"])',\n );\n /** Resolves the overlay's calendar, which lives outside this harness's host. */\n private readonly calendar =\n this.documentRootLocatorFactory().locatorForOptional(CalendarHarness);\n\n /** Predicate for `locatorFor` / `locatorForAll`. */\n static with(\n options: DateRangePickerHarnessFilters = {},\n ): HarnessPredicate<DateRangePickerHarness> {\n return new HarnessPredicate(DateRangePickerHarness, options)\n .addOption('label', options.label, async (h, label) =>\n HarnessPredicate.stringMatches(await h.getLabel(), label),\n )\n .addOption('triggerText', options.triggerText, async (h, text) =>\n HarnessPredicate.stringMatches(await h.getTriggerText(), text),\n )\n .addOption(\n 'disabled',\n options.disabled,\n async (h, disabled) => (await h.isDisabled()) === disabled,\n );\n }\n\n /**\n * The trigger's accessible name.\n *\n * Note this is *composed*, not the bare `aria-label`: once a complete range\n * is set the component appends the range to it\n * (`\"Report period. Current range: Apr 10 to Apr 15.\"`). Match with a RegExp\n * in {@link with} if you need it to survive a selection.\n */\n async getLabel(): Promise<string | null> {\n return (await this.trigger()).getAttribute('aria-label');\n }\n\n /**\n * The whole text rendered in the trigger, trimmed — start, separator and end\n * run together, or the placeholder when no range is set.\n */\n async getTriggerText(): Promise<string> {\n return (await (await this.trigger()).text()).trim();\n }\n\n /**\n * The formatted start date, or `null` when no range is set (the trigger then\n * renders a single placeholder span instead of the start/separator/end\n * triple).\n */\n async getStartText(): Promise<string | null> {\n return this.rangeSpanText(0);\n }\n\n /** The formatted end date, or `null` when no range is set. */\n async getEndText(): Promise<string | null> {\n return this.rangeSpanText(2);\n }\n\n /** Whether the calendar overlay is open, read from the trigger's `aria-expanded`. */\n async isOpen(): Promise<boolean> {\n return (await (await this.trigger()).getAttribute('aria-expanded')) === 'true';\n }\n\n /** Whether the trigger reports `aria-disabled=\"true\"`. */\n async isDisabled(): Promise<boolean> {\n return (await (await this.trigger()).getAttribute('aria-disabled')) === 'true';\n }\n\n /** Whether the trigger reports `aria-invalid=\"true\"`. */\n async isInvalid(): Promise<boolean> {\n return (await (await this.trigger()).getAttribute('aria-invalid')) === 'true';\n }\n\n /**\n * Whether the trigger reports `aria-required=\"true\"`. Reflects the `required`\n * input *and* a `Validators.required` on a bound control.\n */\n async isRequired(): Promise<boolean> {\n return (await (await this.trigger()).getAttribute('aria-required')) === 'true';\n }\n\n /**\n * Opens the calendar overlay by clicking the trigger. No-op when already\n * open; a disabled picker ignores it, as it does in the browser.\n */\n async open(): Promise<void> {\n if (await this.isOpen()) return;\n await (await this.trigger()).click();\n await this.waitForTasksOutsideAngular();\n }\n\n /**\n * Closes the calendar overlay by clicking the trigger again. No-op when\n * already closed.\n *\n * Deliberately not Escape: `tw-date-range-picker` treats Escape as *cancel*\n * and restores the range the overlay opened with, which would make `close()`\n * silently undo a selection. The trigger toggles without that side effect.\n *\n * The overlay runs a leave animation before it detaches, so `isOpen()` flips\n * to `false` immediately while the panel element lingers for a few more\n * frames. Two consequences worth knowing:\n *\n * - Do not assert `getCalendar()` is `null` on the tick after a close; the\n * old panel is still mounted.\n * - An `open()` inside that window does **not** attach a fresh panel — the\n * component sees an overlay ref it has not disposed yet and silently\n * keeps the outgoing one. Let the animation finish before reopening.\n */\n async close(): Promise<void> {\n if (!(await this.isOpen())) return;\n await (await this.trigger()).click();\n await this.waitForTasksOutsideAngular();\n }\n\n /**\n * The `CalendarHarness` for the open overlay, or `null` when no overlay is\n * attached. One `tw-calendar` renders all `numberOfMonths` months under a\n * single header, so this is a single harness whatever that input is set to.\n */\n async getCalendar(): Promise<CalendarHarness | null> {\n return this.calendar();\n }\n\n /**\n * Opens the overlay if needed and clicks the two cells whose text matches\n * `start` then `end` exactly (`'10'`, not `'April 10'`). Throws when either\n * cell is not visible in the current view.\n *\n * With more than one month rendered a day number appears once per month grid;\n * the first match wins, which is the earliest visible month. Page the\n * calendar via {@link getCalendar} first if you need a later one.\n *\n * With `showActions` off this commits and closes on the second click; with it\n * on the range stays pending until {@link clickAction} confirms it.\n */\n async selectRange(start: string, end: string): Promise<void> {\n await this.open();\n const calendar = await this.getCalendar();\n if (!calendar) {\n throw new Error(\n 'DateRangePickerHarness.selectRange: the calendar overlay is not attached.',\n );\n }\n await calendar.selectRange(start, end);\n await this.waitForTasksOutsideAngular();\n }\n\n /**\n * Clicks a button in the overlay's action bar (`Today`, `Clear`, `Cancel`,\n * `Apply`) by its visible text. Matching on text rather than position is what\n * keeps this usable when a consumer relabels the actions via `todayLabel` and\n * friends — pass whatever label you configured.\n *\n * Preset entries are excluded: they are `role=\"option\"`, not action buttons.\n * Throws when the overlay is closed or nothing matches.\n */\n async clickAction(label: string | RegExp): Promise<void> {\n const buttons = await this.documentRootLocatorFactory().locatorForAll(\n 'tw-date-range-picker-overlay button:not([role=\"option\"])',\n )();\n for (const button of buttons) {\n if (await HarnessPredicate.stringMatches((await button.text()).trim(), label)) {\n await button.click();\n await this.waitForTasksOutsideAngular();\n return;\n }\n }\n throw new Error(\n `DateRangePickerHarness.clickAction: no overlay action button matching ${String(label)}. Is the overlay open and \\`showActions\\` on?`,\n );\n }\n\n /**\n * Whether the inline clear control is rendered. It appears only while\n * `showClear` is on, the picker is enabled, and a range is set.\n */\n async hasClearButton(): Promise<boolean> {\n return (await this.clearButton()) !== null;\n }\n\n /** Clicks the inline clear control. Throws when none is rendered. */\n async clear(): Promise<void> {\n const button: TestElement | null = await this.clearButton();\n if (!button) {\n throw new Error(\n 'DateRangePickerHarness.clear: no clear control is rendered. It appears only while `showClear` is on, the picker is enabled, and a range is set.',\n );\n }\n await button.click();\n await this.waitForTasksOutsideAngular();\n }\n\n /** @internal Text of one of the start/separator/end spans, or `null` when the trigger is empty. */\n private async rangeSpanText(index: number): Promise<string | null> {\n const spans = await this.triggerSpans();\n // One span means the placeholder branch rendered — no range is set.\n if (spans.length < 3) return null;\n return (await spans[index].text()).trim();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAcA;;;;;;;;;;;;;;;;;;AAkBG;AACG,MAAO,sBAAuB,SAAQ,gBAAgB,CAAA;AAC1D,IAAA,OAAO,YAAY,GAAG,sBAAsB;AAE3B,IAAA,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,yBAAyB,CAAC;AACrE;;;;AAIG;AACc,IAAA,YAAY,GAAG,IAAI,CAAC,aAAa,CAChD,qCAAqC,CACtC;AACD;;;AAGG;AACc,IAAA,WAAW,GAAG,IAAI,CAAC,kBAAkB,CACpD,+BAA+B,CAChC;;IAEgB,QAAQ,GACvB,IAAI,CAAC,0BAA0B,EAAE,CAAC,kBAAkB,CAAC,eAAe,CAAC;;AAGvE,IAAA,OAAO,IAAI,CACT,OAAA,GAAyC,EAAE,EAAA;AAE3C,QAAA,OAAO,IAAI,gBAAgB,CAAC,sBAAsB,EAAE,OAAO;aACxD,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,KAAK,KAChD,gBAAgB,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC;aAE1D,SAAS,CAAC,aAAa,EAAE,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE,IAAI,KAC3D,gBAAgB,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC;aAE/D,SAAS,CACR,UAAU,EACV,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,EAAE,QAAQ,KAAK,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,MAAM,QAAQ,CAC3D;IACL;AAEA;;;;;;;AAOG;AACH,IAAA,MAAM,QAAQ,GAAA;AACZ,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,YAAY,CAAC;IAC1D;AAEA;;;AAGG;AACH,IAAA,MAAM,cAAc,GAAA;AAClB,QAAA,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE;IACrD;AAEA;;;;AAIG;AACH,IAAA,MAAM,YAAY,GAAA;AAChB,QAAA,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;IAC9B;;AAGA,IAAA,MAAM,UAAU,GAAA;AACd,QAAA,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;IAC9B;;AAGA,IAAA,MAAM,MAAM,GAAA;AACV,QAAA,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,eAAe,CAAC,MAAM,MAAM;IAChF;;AAGA,IAAA,MAAM,UAAU,GAAA;AACd,QAAA,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,eAAe,CAAC,MAAM,MAAM;IAChF;;AAGA,IAAA,MAAM,SAAS,GAAA;AACb,QAAA,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,cAAc,CAAC,MAAM,MAAM;IAC/E;AAEA;;;AAGG;AACH,IAAA,MAAM,UAAU,GAAA;AACd,QAAA,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,eAAe,CAAC,MAAM,MAAM;IAChF;AAEA;;;AAGG;AACH,IAAA,MAAM,IAAI,GAAA;AACR,QAAA,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE;YAAE;QACzB,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE;AACpC,QAAA,MAAM,IAAI,CAAC,0BAA0B,EAAE;IACzC;AAEA;;;;;;;;;;;;;;;;;AAiBG;AACH,IAAA,MAAM,KAAK,GAAA;AACT,QAAA,IAAI,EAAE,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YAAE;QAC5B,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE;AACpC,QAAA,MAAM,IAAI,CAAC,0BAA0B,EAAE;IACzC;AAEA;;;;AAIG;AACH,IAAA,MAAM,WAAW,GAAA;AACf,QAAA,OAAO,IAAI,CAAC,QAAQ,EAAE;IACxB;AAEA;;;;;;;;;;;AAWG;AACH,IAAA,MAAM,WAAW,CAAC,KAAa,EAAE,GAAW,EAAA;AAC1C,QAAA,MAAM,IAAI,CAAC,IAAI,EAAE;AACjB,QAAA,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE;QACzC,IAAI,CAAC,QAAQ,EAAE;AACb,YAAA,MAAM,IAAI,KAAK,CACb,2EAA2E,CAC5E;QACH;QACA,MAAM,QAAQ,CAAC,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC;AACtC,QAAA,MAAM,IAAI,CAAC,0BAA0B,EAAE;IACzC;AAEA;;;;;;;;AAQG;IACH,MAAM,WAAW,CAAC,KAAsB,EAAA;AACtC,QAAA,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,0BAA0B,EAAE,CAAC,aAAa,CACnE,0DAA0D,CAC3D,EAAE;AACH,QAAA,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE;AAC5B,YAAA,IAAI,MAAM,gBAAgB,CAAC,aAAa,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,CAAC,EAAE;AAC7E,gBAAA,MAAM,MAAM,CAAC,KAAK,EAAE;AACpB,gBAAA,MAAM,IAAI,CAAC,0BAA0B,EAAE;gBACvC;YACF;QACF;QACA,MAAM,IAAI,KAAK,CACb,CAAA,sEAAA,EAAyE,MAAM,CAAC,KAAK,CAAC,CAAA,6CAAA,CAA+C,CACtI;IACH;AAEA;;;AAGG;AACH,IAAA,MAAM,cAAc,GAAA;QAClB,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,MAAM,IAAI;IAC5C;;AAGA,IAAA,MAAM,KAAK,GAAA;AACT,QAAA,MAAM,MAAM,GAAuB,MAAM,IAAI,CAAC,WAAW,EAAE;QAC3D,IAAI,CAAC,MAAM,EAAE;AACX,YAAA,MAAM,IAAI,KAAK,CACb,iJAAiJ,CAClJ;QACH;AACA,QAAA,MAAM,MAAM,CAAC,KAAK,EAAE;AACpB,QAAA,MAAM,IAAI,CAAC,0BAA0B,EAAE;IACzC;;IAGQ,MAAM,aAAa,CAAC,KAAa,EAAA;AACvC,QAAA,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE;;AAEvC,QAAA,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;AAAE,YAAA,OAAO,IAAI;AACjC,QAAA,OAAO,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE;IAC3C;;;ACzPF;;AAEG;;;;"}
|