@truenas/ui-components 0.1.39 → 0.1.41
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.
|
@@ -8071,6 +8071,207 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
8071
8071
|
}, template: "<div class=\"tn-date-input-container\">\n <div #wrapper ixInput class=\"tn-date-input-wrapper\" style=\"padding-right: 40px;\">\n <!-- Date segments MM/DD/YYYY -->\n <div class=\"tn-date-segment-group\">\n <input\n #monthInput\n type=\"text\"\n class=\"tn-date-segment tn-date-segment-month\"\n placeholder=\"MM\"\n maxlength=\"2\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onSegmentFocus('month')\"\n (blur)=\"onSegmentBlur('month')\"\n (keydown)=\"onSegmentKeydown($event, 'month')\">\n <span class=\"tn-date-segment-separator\">/</span>\n <input\n #dayInput\n type=\"text\"\n class=\"tn-date-segment tn-date-segment-day\"\n placeholder=\"DD\"\n maxlength=\"2\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onSegmentFocus('day')\"\n (blur)=\"onSegmentBlur('day')\"\n (keydown)=\"onSegmentKeydown($event, 'day')\">\n <span class=\"tn-date-segment-separator\">/</span>\n <input\n #yearInput\n type=\"text\"\n class=\"tn-date-segment tn-date-segment-year\"\n placeholder=\"YYYY\"\n maxlength=\"4\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onSegmentFocus('year')\"\n (blur)=\"onSegmentBlur('year')\"\n (keydown)=\"onSegmentKeydown($event, 'year')\">\n </div>\n\n <button\n type=\"button\"\n class=\"tn-date-input-toggle\"\n aria-label=\"Open calendar\"\n [disabled]=\"isDisabled()\"\n (click)=\"openDatepicker()\">\n <span aria-hidden=\"true\">\uD83D\uDCC5</span>\n </button>\n </div>\n\n <ng-template #calendarTemplate>\n <tn-calendar\n class=\"tn-calendar\"\n [startView]=\"'month'\"\n [rangeMode]=\"false\"\n [selected]=\"value()\"\n (selectedChange)=\"onDateSelected($event)\" />\n </ng-template>\n</div>\n", styles: [":host{display:block;width:100%}.tn-date-input-container{position:relative;display:flex;align-items:center}.tn-date-input-wrapper{display:flex;align-items:center;width:100%;position:relative}.tn-date-segment-group{display:flex;align-items:center}.tn-date-segment{background:transparent;border:none;outline:none;font:inherit;color:inherit;padding:0;min-width:0;text-align:center;width:2.6ch}.tn-date-segment::placeholder{color:var(--tn-alt-fg1, #999);opacity:1}.tn-date-segment:focus{outline:none;background:var(--tn-bg2, rgba(0, 0, 0, .05));border-radius:2px}.tn-date-segment:focus::placeholder{opacity:0}.tn-date-segment.tn-date-segment-year{width:4ch}.tn-date-segment-separator{padding:0 2px;-webkit-user-select:none;user-select:none;color:var(--tn-alt-fg1, #999)}.tn-date-input-toggle{position:absolute;right:8px;z-index:2;pointer-events:auto;background:transparent;border:none;cursor:pointer;padding:4px;font-size:16px}.tn-date-input-toggle:hover{background:var(--tn-bg2, #f0f0f0);border-radius:4px}.tn-date-input-toggle:disabled{cursor:not-allowed;opacity:.5}:host ::ng-deep .tn-datepicker-overlay .tn-calendar{background:var(--tn-bg1, white);border:1px solid var(--tn-lines, #e0e0e0);border-radius:8px;box-shadow:0 4px 12px #00000026;padding:24px;min-width:380px;--calendar-cell-size: 48px;--calendar-header-height: 44px;--calendar-cell-font-size: 16px;--calendar-header-font-size: 14px}:host ::ng-deep .tn-datepicker-overlay .tn-calendar .tn-calendar-content{padding:0}\n"] }]
|
|
8072
8072
|
}], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], monthRef: [{ type: i0.ViewChild, args: ['monthInput', { isSignal: true }] }], dayRef: [{ type: i0.ViewChild, args: ['dayInput', { isSignal: true }] }], yearRef: [{ type: i0.ViewChild, args: ['yearInput', { isSignal: true }] }], calendarTemplate: [{ type: i0.ViewChild, args: ['calendarTemplate', { isSignal: true }] }], calendar: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TnCalendarComponent), { isSignal: true }] }], wrapperEl: [{ type: i0.ViewChild, args: ['wrapper', { isSignal: true }] }] } });
|
|
8073
8073
|
|
|
8074
|
+
/**
|
|
8075
|
+
* Formats a Date into zero-padded month, day, and year strings.
|
|
8076
|
+
*
|
|
8077
|
+
* @param date The date to format.
|
|
8078
|
+
* @returns An object with `month` (MM), `day` (DD), and `year` (YYYY) strings.
|
|
8079
|
+
*/
|
|
8080
|
+
function formatDateParts(date) {
|
|
8081
|
+
return {
|
|
8082
|
+
month: (date.getMonth() + 1).toString().padStart(2, '0'),
|
|
8083
|
+
day: date.getDate().toString().padStart(2, '0'),
|
|
8084
|
+
year: date.getFullYear().toString(),
|
|
8085
|
+
};
|
|
8086
|
+
}
|
|
8087
|
+
/**
|
|
8088
|
+
* Reads the current value from an input element.
|
|
8089
|
+
*
|
|
8090
|
+
* @param el The test element to read from.
|
|
8091
|
+
* @returns The input's value, or an empty string if unset.
|
|
8092
|
+
*/
|
|
8093
|
+
async function getInputValue(el) {
|
|
8094
|
+
return (await el.getProperty('value')) || '';
|
|
8095
|
+
}
|
|
8096
|
+
/**
|
|
8097
|
+
* Clears an input element and types a new value, then blurs to trigger validation.
|
|
8098
|
+
*
|
|
8099
|
+
* @param el The test element to write to.
|
|
8100
|
+
* @param value The value to type. Pass empty string to clear only.
|
|
8101
|
+
*/
|
|
8102
|
+
async function setInputValue(el, value) {
|
|
8103
|
+
await el.clear();
|
|
8104
|
+
if (value) {
|
|
8105
|
+
await el.sendKeys(value);
|
|
8106
|
+
}
|
|
8107
|
+
await el.blur();
|
|
8108
|
+
}
|
|
8109
|
+
const MONTHS = [
|
|
8110
|
+
'JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN',
|
|
8111
|
+
'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC',
|
|
8112
|
+
];
|
|
8113
|
+
/**
|
|
8114
|
+
* Navigates the calendar overlay to the target month/year, then clicks the day cell.
|
|
8115
|
+
* Assumes the calendar popup is already open.
|
|
8116
|
+
*
|
|
8117
|
+
* @param rootLocator The document root locator factory (for finding overlay elements).
|
|
8118
|
+
* @param date The target date to select.
|
|
8119
|
+
*/
|
|
8120
|
+
async function selectCalendarDate(rootLocator, date) {
|
|
8121
|
+
await navigateCalendarTo(rootLocator, date);
|
|
8122
|
+
const dayStr = date.getDate().toString();
|
|
8123
|
+
const cells = await rootLocator.locatorForAll('.tn-calendar-body-cell:not([disabled])')();
|
|
8124
|
+
for (const cell of cells) {
|
|
8125
|
+
if ((await cell.text()).trim() === dayStr) {
|
|
8126
|
+
await cell.click();
|
|
8127
|
+
return;
|
|
8128
|
+
}
|
|
8129
|
+
}
|
|
8130
|
+
throw new Error(`Could not find enabled calendar cell for day ${dayStr}`);
|
|
8131
|
+
}
|
|
8132
|
+
/**
|
|
8133
|
+
* Navigates the calendar to show the month/year of the target date by
|
|
8134
|
+
* clicking previous/next buttons as needed.
|
|
8135
|
+
*/
|
|
8136
|
+
async function navigateCalendarTo(rootLocator, date) {
|
|
8137
|
+
const targetLabel = `${MONTHS[date.getMonth()]} ${date.getFullYear()}`;
|
|
8138
|
+
// Safety limit to prevent infinite loops
|
|
8139
|
+
for (let i = 0; i < 120; i++) {
|
|
8140
|
+
const periodButton = await rootLocator.locatorFor('.tn-calendar-period-button')();
|
|
8141
|
+
const currentLabel = (await periodButton.text()).trim();
|
|
8142
|
+
if (currentLabel === targetLabel) {
|
|
8143
|
+
return;
|
|
8144
|
+
}
|
|
8145
|
+
const currentDate = parseCalendarLabel(currentLabel);
|
|
8146
|
+
const targetTime = date.getFullYear() * 12 + date.getMonth();
|
|
8147
|
+
const currentTime = currentDate.year * 12 + currentDate.month;
|
|
8148
|
+
const navSelector = targetTime > currentTime
|
|
8149
|
+
? '.tn-calendar-next-button'
|
|
8150
|
+
: '.tn-calendar-previous-button';
|
|
8151
|
+
const navButton = await rootLocator.locatorFor(navSelector)();
|
|
8152
|
+
await navButton.click();
|
|
8153
|
+
}
|
|
8154
|
+
throw new Error(`Could not navigate calendar to ${targetLabel}`);
|
|
8155
|
+
}
|
|
8156
|
+
function parseCalendarLabel(label) {
|
|
8157
|
+
const parts = label.split(' ');
|
|
8158
|
+
const monthIndex = MONTHS.indexOf(parts[0]);
|
|
8159
|
+
const year = parseInt(parts[1], 10);
|
|
8160
|
+
return { year, month: monthIndex >= 0 ? monthIndex : 0 };
|
|
8161
|
+
}
|
|
8162
|
+
|
|
8163
|
+
/**
|
|
8164
|
+
* Harness for interacting with `tn-date-input` in tests.
|
|
8165
|
+
* Provides methods for getting/setting the date value, opening the calendar,
|
|
8166
|
+
* and querying disabled state.
|
|
8167
|
+
*
|
|
8168
|
+
* The calendar popup is portaled to the CDK overlay, so use
|
|
8169
|
+
* `TestbedHarnessEnvironment.documentRootLoader(fixture)` if you need to
|
|
8170
|
+
* interact with the calendar cells directly.
|
|
8171
|
+
*
|
|
8172
|
+
* @example
|
|
8173
|
+
* ```typescript
|
|
8174
|
+
* const dateInput = await loader.getHarness(TnDateInputHarness);
|
|
8175
|
+
* await dateInput.setValue(new Date(2026, 3, 15));
|
|
8176
|
+
* expect(await dateInput.getDisplayText()).toBe('04/15/2026');
|
|
8177
|
+
*
|
|
8178
|
+
* await dateInput.openCalendar();
|
|
8179
|
+
* expect(await dateInput.isCalendarOpen()).toBe(true);
|
|
8180
|
+
* ```
|
|
8181
|
+
*/
|
|
8182
|
+
class TnDateInputHarness extends ComponentHarness {
|
|
8183
|
+
static hostSelector = 'tn-date-input';
|
|
8184
|
+
_monthInput = this.locatorFor('.tn-date-segment-month');
|
|
8185
|
+
_dayInput = this.locatorFor('.tn-date-segment-day');
|
|
8186
|
+
_yearInput = this.locatorFor('.tn-date-segment-year');
|
|
8187
|
+
_toggle = this.locatorFor('.tn-date-input-toggle');
|
|
8188
|
+
/**
|
|
8189
|
+
* Gets a `HarnessPredicate` that can be used to search for a date input
|
|
8190
|
+
* with specific attributes.
|
|
8191
|
+
*
|
|
8192
|
+
* @param options Options for filtering which instances are considered a match.
|
|
8193
|
+
* @returns A `HarnessPredicate` configured with the given options.
|
|
8194
|
+
*/
|
|
8195
|
+
static with(options = {}) {
|
|
8196
|
+
return new HarnessPredicate(TnDateInputHarness, options);
|
|
8197
|
+
}
|
|
8198
|
+
/**
|
|
8199
|
+
* Gets the displayed date text as "MM/DD/YYYY".
|
|
8200
|
+
* Returns placeholder segments if no date is set.
|
|
8201
|
+
*
|
|
8202
|
+
* @returns Promise resolving to the formatted display string.
|
|
8203
|
+
*/
|
|
8204
|
+
async getDisplayText() {
|
|
8205
|
+
const month = await getInputValue(await this._monthInput());
|
|
8206
|
+
const day = await getInputValue(await this._dayInput());
|
|
8207
|
+
const year = await getInputValue(await this._yearInput());
|
|
8208
|
+
return `${month || 'MM'}/${day || 'DD'}/${year || 'YYYY'}`;
|
|
8209
|
+
}
|
|
8210
|
+
/**
|
|
8211
|
+
* Sets the date by typing into the segment inputs.
|
|
8212
|
+
*
|
|
8213
|
+
* @param date The date to set.
|
|
8214
|
+
*/
|
|
8215
|
+
async setValue(date) {
|
|
8216
|
+
const { month, day, year } = formatDateParts(date);
|
|
8217
|
+
await setInputValue(await this._monthInput(), month);
|
|
8218
|
+
await setInputValue(await this._dayInput(), day);
|
|
8219
|
+
await setInputValue(await this._yearInput(), year);
|
|
8220
|
+
}
|
|
8221
|
+
/**
|
|
8222
|
+
* Clears all date segments.
|
|
8223
|
+
*/
|
|
8224
|
+
async clear() {
|
|
8225
|
+
await setInputValue(await this._monthInput(), '');
|
|
8226
|
+
await setInputValue(await this._dayInput(), '');
|
|
8227
|
+
await setInputValue(await this._yearInput(), '');
|
|
8228
|
+
}
|
|
8229
|
+
/**
|
|
8230
|
+
* Checks whether the input is disabled.
|
|
8231
|
+
*
|
|
8232
|
+
* @returns Promise resolving to true if the input is disabled.
|
|
8233
|
+
*/
|
|
8234
|
+
async isDisabled() {
|
|
8235
|
+
const month = await this._monthInput();
|
|
8236
|
+
return (await month.getProperty('disabled')) ?? false;
|
|
8237
|
+
}
|
|
8238
|
+
/**
|
|
8239
|
+
* Opens the calendar popup by clicking the toggle button.
|
|
8240
|
+
*/
|
|
8241
|
+
async openCalendar() {
|
|
8242
|
+
const toggle = await this._toggle();
|
|
8243
|
+
await toggle.click();
|
|
8244
|
+
}
|
|
8245
|
+
/**
|
|
8246
|
+
* Whether the calendar popup is currently open.
|
|
8247
|
+
*
|
|
8248
|
+
* @returns Promise resolving to true if the calendar overlay is visible.
|
|
8249
|
+
*/
|
|
8250
|
+
async isCalendarOpen() {
|
|
8251
|
+
const overlay = await this.documentRootLocatorFactory().locatorForOptional('.tn-datepicker-overlay')();
|
|
8252
|
+
return overlay !== null;
|
|
8253
|
+
}
|
|
8254
|
+
/**
|
|
8255
|
+
* Selects a date via the calendar popup. Opens the calendar if not already
|
|
8256
|
+
* open, navigates to the target month, and clicks the day cell.
|
|
8257
|
+
*
|
|
8258
|
+
* @param date The date to select.
|
|
8259
|
+
*
|
|
8260
|
+
* @example
|
|
8261
|
+
* ```typescript
|
|
8262
|
+
* const dateInput = await loader.getHarness(TnDateInputHarness);
|
|
8263
|
+
* await dateInput.selectDate(new Date(2026, 3, 15));
|
|
8264
|
+
* expect(await dateInput.getDisplayText()).toBe('04/15/2026');
|
|
8265
|
+
* ```
|
|
8266
|
+
*/
|
|
8267
|
+
async selectDate(date) {
|
|
8268
|
+
if (!(await this.isCalendarOpen())) {
|
|
8269
|
+
await this.openCalendar();
|
|
8270
|
+
}
|
|
8271
|
+
await selectCalendarDate(this.documentRootLocatorFactory(), date);
|
|
8272
|
+
}
|
|
8273
|
+
}
|
|
8274
|
+
|
|
8074
8275
|
class TnDateRangeInputComponent {
|
|
8075
8276
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
8076
8277
|
placeholder = input('Select date range', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
@@ -8430,6 +8631,150 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
8430
8631
|
}, template: "<div class=\"tn-date-range-container\">\n <div #wrapper ixInput class=\"tn-date-range-wrapper\" style=\"padding-right: 40px;\">\n <!-- Start date segments -->\n <div class=\"tn-date-segment-group\">\n <input\n #startMonthInput\n type=\"text\"\n class=\"tn-date-segment tn-date-segment-month\"\n placeholder=\"MM\"\n maxlength=\"2\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onSegmentFocus('start', 'month')\"\n (blur)=\"onSegmentBlur('start', 'month')\"\n (keydown)=\"onSegmentKeydown($event, 'start', 'month')\">\n <span class=\"tn-date-segment-separator\">/</span>\n <input\n #startDayInput\n type=\"text\"\n class=\"tn-date-segment tn-date-segment-day\"\n placeholder=\"DD\"\n maxlength=\"2\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onSegmentFocus('start', 'day')\"\n (blur)=\"onSegmentBlur('start', 'day')\"\n (keydown)=\"onSegmentKeydown($event, 'start', 'day')\">\n <span class=\"tn-date-segment-separator\">/</span>\n <input\n #startYearInput\n type=\"text\"\n class=\"tn-date-segment tn-date-segment-year\"\n placeholder=\"YYYY\"\n maxlength=\"4\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onSegmentFocus('start', 'year')\"\n (blur)=\"onSegmentBlur('start', 'year')\"\n (keydown)=\"onSegmentKeydown($event, 'start', 'year')\">\n </div>\n\n <span class=\"tn-date-range-separator\">\u2013</span>\n\n <!-- End date segments -->\n <div class=\"tn-date-segment-group\">\n <input\n #endMonthInput\n type=\"text\"\n class=\"tn-date-segment tn-date-segment-month\"\n placeholder=\"MM\"\n maxlength=\"2\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onSegmentFocus('end', 'month')\"\n (blur)=\"onSegmentBlur('end', 'month')\"\n (keydown)=\"onSegmentKeydown($event, 'end', 'month')\">\n <span class=\"tn-date-segment-separator\">/</span>\n <input\n #endDayInput\n type=\"text\"\n class=\"tn-date-segment tn-date-segment-day\"\n placeholder=\"DD\"\n maxlength=\"2\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onSegmentFocus('end', 'day')\"\n (blur)=\"onSegmentBlur('end', 'day')\"\n (keydown)=\"onSegmentKeydown($event, 'end', 'day')\">\n <span class=\"tn-date-segment-separator\">/</span>\n <input\n #endYearInput\n type=\"text\"\n class=\"tn-date-segment tn-date-segment-year\"\n placeholder=\"YYYY\"\n maxlength=\"4\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onSegmentFocus('end', 'year')\"\n (blur)=\"onSegmentBlur('end', 'year')\"\n (keydown)=\"onSegmentKeydown($event, 'end', 'year')\">\n </div>\n\n <button\n type=\"button\"\n class=\"tn-date-range-toggle\"\n aria-label=\"Open calendar\"\n [disabled]=\"isDisabled()\"\n (click)=\"openDatepicker()\">\n <span aria-hidden=\"true\">\uD83D\uDCC5</span>\n </button>\n </div>\n\n <ng-template #calendarTemplate>\n <tn-calendar\n class=\"tn-calendar\"\n [startView]=\"'month'\"\n [rangeMode]=\"true\"\n [selectedRange]=\"initialRange()\"\n (selectedRangeChange)=\"onRangeSelected($event)\" />\n </ng-template>\n</div>\n", styles: [":host{display:block;width:100%}.tn-date-range-container{position:relative;display:flex;align-items:center}.tn-date-range-wrapper{display:flex;align-items:center;width:100%;position:relative}.tn-date-segment-group{display:flex;align-items:center}.tn-date-segment{background:transparent;border:none;outline:none;font:inherit;color:inherit;padding:0;min-width:0;text-align:center;width:2.6ch}.tn-date-segment::placeholder{color:var(--tn-alt-fg1, #999);opacity:1}.tn-date-segment:focus{outline:none;background:var(--tn-bg2, rgba(0, 0, 0, .05));border-radius:2px}.tn-date-segment:focus::placeholder{opacity:0}.tn-date-segment.tn-date-segment-year{width:4ch}.tn-date-segment-separator{padding:0 2px;-webkit-user-select:none;user-select:none;color:var(--tn-alt-fg1, #999)}.tn-date-range-separator{padding:0 .25em;-webkit-user-select:none;user-select:none;color:var(--tn-fg2, #666);flex-shrink:0}.tn-date-range-toggle{position:absolute;right:8px;z-index:2;pointer-events:auto;background:transparent;border:none;cursor:pointer;padding:4px;font-size:16px}.tn-date-range-toggle:hover{background:var(--tn-bg2, #f0f0f0);border-radius:4px}.tn-date-range-toggle:disabled{cursor:not-allowed;opacity:.5}:host ::ng-deep .tn-datepicker-overlay .tn-calendar{background:var(--tn-bg1, white);border:1px solid var(--tn-lines, #e0e0e0);border-radius:8px;box-shadow:0 4px 12px #00000026;padding:24px;min-width:380px;--calendar-cell-size: 48px;--calendar-header-height: 44px;--calendar-cell-font-size: 16px;--calendar-header-font-size: 14px}:host ::ng-deep .tn-datepicker-overlay .tn-calendar .tn-calendar-content{padding:0}\n"] }]
|
|
8431
8632
|
}], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], startMonthRef: [{ type: i0.ViewChild, args: ['startMonthInput', { isSignal: true }] }], startDayRef: [{ type: i0.ViewChild, args: ['startDayInput', { isSignal: true }] }], startYearRef: [{ type: i0.ViewChild, args: ['startYearInput', { isSignal: true }] }], endMonthRef: [{ type: i0.ViewChild, args: ['endMonthInput', { isSignal: true }] }], endDayRef: [{ type: i0.ViewChild, args: ['endDayInput', { isSignal: true }] }], endYearRef: [{ type: i0.ViewChild, args: ['endYearInput', { isSignal: true }] }], calendarTemplate: [{ type: i0.ViewChild, args: ['calendarTemplate', { isSignal: true }] }], calendar: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TnCalendarComponent), { isSignal: true }] }], wrapperEl: [{ type: i0.ViewChild, args: ['wrapper', { isSignal: true }] }] } });
|
|
8432
8633
|
|
|
8634
|
+
/**
|
|
8635
|
+
* Harness for interacting with `tn-date-range-input` in tests.
|
|
8636
|
+
* Provides methods for getting/setting the date range, opening the calendar,
|
|
8637
|
+
* and querying disabled state.
|
|
8638
|
+
*
|
|
8639
|
+
* The calendar popup is portaled to the CDK overlay, so use
|
|
8640
|
+
* `TestbedHarnessEnvironment.documentRootLoader(fixture)` if you need to
|
|
8641
|
+
* interact with the calendar cells directly.
|
|
8642
|
+
*
|
|
8643
|
+
* @example
|
|
8644
|
+
* ```typescript
|
|
8645
|
+
* const rangeInput = await loader.getHarness(TnDateRangeInputHarness);
|
|
8646
|
+
* await rangeInput.setStartDate(new Date(2026, 0, 1));
|
|
8647
|
+
* await rangeInput.setEndDate(new Date(2026, 0, 31));
|
|
8648
|
+
* expect(await rangeInput.getStartText()).toBe('01/01/2026');
|
|
8649
|
+
* expect(await rangeInput.getEndText()).toBe('01/31/2026');
|
|
8650
|
+
* ```
|
|
8651
|
+
*/
|
|
8652
|
+
class TnDateRangeInputHarness extends ComponentHarness {
|
|
8653
|
+
static hostSelector = 'tn-date-range-input';
|
|
8654
|
+
_startMonth = this.locatorFor('.tn-date-segment-group:first-of-type .tn-date-segment-month');
|
|
8655
|
+
_startDay = this.locatorFor('.tn-date-segment-group:first-of-type .tn-date-segment-day');
|
|
8656
|
+
_startYear = this.locatorFor('.tn-date-segment-group:first-of-type .tn-date-segment-year');
|
|
8657
|
+
_endMonth = this.locatorFor('.tn-date-segment-group:last-of-type .tn-date-segment-month');
|
|
8658
|
+
_endDay = this.locatorFor('.tn-date-segment-group:last-of-type .tn-date-segment-day');
|
|
8659
|
+
_endYear = this.locatorFor('.tn-date-segment-group:last-of-type .tn-date-segment-year');
|
|
8660
|
+
_toggle = this.locatorFor('.tn-date-range-toggle');
|
|
8661
|
+
/**
|
|
8662
|
+
* Gets a `HarnessPredicate` that can be used to search for a date range input
|
|
8663
|
+
* with specific attributes.
|
|
8664
|
+
*
|
|
8665
|
+
* @param options Options for filtering which instances are considered a match.
|
|
8666
|
+
* @returns A `HarnessPredicate` configured with the given options.
|
|
8667
|
+
*/
|
|
8668
|
+
static with(options = {}) {
|
|
8669
|
+
return new HarnessPredicate(TnDateRangeInputHarness, options);
|
|
8670
|
+
}
|
|
8671
|
+
/**
|
|
8672
|
+
* Gets the start date display text as "MM/DD/YYYY".
|
|
8673
|
+
*
|
|
8674
|
+
* @returns Promise resolving to the formatted start date string.
|
|
8675
|
+
*/
|
|
8676
|
+
async getStartText() {
|
|
8677
|
+
const month = await getInputValue(await this._startMonth());
|
|
8678
|
+
const day = await getInputValue(await this._startDay());
|
|
8679
|
+
const year = await getInputValue(await this._startYear());
|
|
8680
|
+
return `${month || 'MM'}/${day || 'DD'}/${year || 'YYYY'}`;
|
|
8681
|
+
}
|
|
8682
|
+
/**
|
|
8683
|
+
* Gets the end date display text as "MM/DD/YYYY".
|
|
8684
|
+
*
|
|
8685
|
+
* @returns Promise resolving to the formatted end date string.
|
|
8686
|
+
*/
|
|
8687
|
+
async getEndText() {
|
|
8688
|
+
const month = await getInputValue(await this._endMonth());
|
|
8689
|
+
const day = await getInputValue(await this._endDay());
|
|
8690
|
+
const year = await getInputValue(await this._endYear());
|
|
8691
|
+
return `${month || 'MM'}/${day || 'DD'}/${year || 'YYYY'}`;
|
|
8692
|
+
}
|
|
8693
|
+
/**
|
|
8694
|
+
* Sets the start date by typing into the segment inputs.
|
|
8695
|
+
*
|
|
8696
|
+
* @param date The start date to set.
|
|
8697
|
+
*/
|
|
8698
|
+
async setStartDate(date) {
|
|
8699
|
+
const { month, day, year } = formatDateParts(date);
|
|
8700
|
+
await setInputValue(await this._startMonth(), month);
|
|
8701
|
+
await setInputValue(await this._startDay(), day);
|
|
8702
|
+
await setInputValue(await this._startYear(), year);
|
|
8703
|
+
}
|
|
8704
|
+
/**
|
|
8705
|
+
* Sets the end date by typing into the segment inputs.
|
|
8706
|
+
*
|
|
8707
|
+
* @param date The end date to set.
|
|
8708
|
+
*/
|
|
8709
|
+
async setEndDate(date) {
|
|
8710
|
+
const { month, day, year } = formatDateParts(date);
|
|
8711
|
+
await setInputValue(await this._endMonth(), month);
|
|
8712
|
+
await setInputValue(await this._endDay(), day);
|
|
8713
|
+
await setInputValue(await this._endYear(), year);
|
|
8714
|
+
}
|
|
8715
|
+
/**
|
|
8716
|
+
* Clears both start and end date segments.
|
|
8717
|
+
*/
|
|
8718
|
+
async clear() {
|
|
8719
|
+
await setInputValue(await this._startMonth(), '');
|
|
8720
|
+
await setInputValue(await this._startDay(), '');
|
|
8721
|
+
await setInputValue(await this._startYear(), '');
|
|
8722
|
+
await setInputValue(await this._endMonth(), '');
|
|
8723
|
+
await setInputValue(await this._endDay(), '');
|
|
8724
|
+
await setInputValue(await this._endYear(), '');
|
|
8725
|
+
}
|
|
8726
|
+
/**
|
|
8727
|
+
* Checks whether the input is disabled.
|
|
8728
|
+
*
|
|
8729
|
+
* @returns Promise resolving to true if the input is disabled.
|
|
8730
|
+
*/
|
|
8731
|
+
async isDisabled() {
|
|
8732
|
+
const month = await this._startMonth();
|
|
8733
|
+
return (await month.getProperty('disabled')) ?? false;
|
|
8734
|
+
}
|
|
8735
|
+
/**
|
|
8736
|
+
* Opens the calendar popup by clicking the toggle button.
|
|
8737
|
+
*/
|
|
8738
|
+
async openCalendar() {
|
|
8739
|
+
const toggle = await this._toggle();
|
|
8740
|
+
await toggle.click();
|
|
8741
|
+
}
|
|
8742
|
+
/**
|
|
8743
|
+
* Whether the calendar popup is currently open.
|
|
8744
|
+
*
|
|
8745
|
+
* @returns Promise resolving to true if the calendar overlay is visible.
|
|
8746
|
+
*/
|
|
8747
|
+
async isCalendarOpen() {
|
|
8748
|
+
const overlay = await this.documentRootLocatorFactory().locatorForOptional('.tn-datepicker-overlay')();
|
|
8749
|
+
return overlay !== null;
|
|
8750
|
+
}
|
|
8751
|
+
/**
|
|
8752
|
+
* Selects a date range via the calendar popup. Opens the calendar if not
|
|
8753
|
+
* already open, navigates to each target month, and clicks the day cells.
|
|
8754
|
+
*
|
|
8755
|
+
* @param range An object with `start` and `end` dates.
|
|
8756
|
+
*
|
|
8757
|
+
* @example
|
|
8758
|
+
* ```typescript
|
|
8759
|
+
* const rangeInput = await loader.getHarness(TnDateRangeInputHarness);
|
|
8760
|
+
* await rangeInput.selectRange({
|
|
8761
|
+
* start: new Date(2026, 0, 1),
|
|
8762
|
+
* end: new Date(2026, 0, 31),
|
|
8763
|
+
* });
|
|
8764
|
+
* expect(await rangeInput.getStartText()).toBe('01/01/2026');
|
|
8765
|
+
* expect(await rangeInput.getEndText()).toBe('01/31/2026');
|
|
8766
|
+
* ```
|
|
8767
|
+
*/
|
|
8768
|
+
async selectRange(range) {
|
|
8769
|
+
if (!(await this.isCalendarOpen())) {
|
|
8770
|
+
await this.openCalendar();
|
|
8771
|
+
}
|
|
8772
|
+
const rootLocator = this.documentRootLocatorFactory();
|
|
8773
|
+
await selectCalendarDate(rootLocator, range.start);
|
|
8774
|
+
await selectCalendarDate(rootLocator, range.end);
|
|
8775
|
+
}
|
|
8776
|
+
}
|
|
8777
|
+
|
|
8433
8778
|
class TnTimeInputComponent {
|
|
8434
8779
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
8435
8780
|
format = input('12h', ...(ngDevMode ? [{ debugName: "format" }] : []));
|
|
@@ -12099,5 +12444,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
12099
12444
|
* Generated bundle index. Do not edit.
|
|
12100
12445
|
*/
|
|
12101
12446
|
|
|
12102
|
-
export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, TN_THEME_DEFINITIONS, TnAutocompleteComponent, TnAutocompleteHarness, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnButtonToggleGroupHarness, TnButtonToggleHarness, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCellDefDirective, TnCheckboxComponent, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnConfirmDialogComponent, TnDateInputComponent, TnDateRangeInputComponent, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnFormFieldHarness, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnInputHarness, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuActivateHoverDirective, TnMenuComponent, TnMenuHarness, TnMenuTesting, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioHarness, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTabsComponent, TnTabsHarness, TnTheme, TnThemeService, TnTimeInputComponent, TnToastComponent, TnToastMock, TnToastPosition, TnToastRef, TnToastService, TnToastTesting, TnToastType, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
|
|
12447
|
+
export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, TN_THEME_DEFINITIONS, TnAutocompleteComponent, TnAutocompleteHarness, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnButtonToggleGroupHarness, TnButtonToggleHarness, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCellDefDirective, TnCheckboxComponent, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnFormFieldHarness, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnInputHarness, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuActivateHoverDirective, TnMenuComponent, TnMenuHarness, TnMenuTesting, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioHarness, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTabsComponent, TnTabsHarness, TnTheme, TnThemeService, TnTimeInputComponent, TnToastComponent, TnToastMock, TnToastPosition, TnToastRef, TnToastService, TnToastTesting, TnToastType, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
|
|
12103
12448
|
//# sourceMappingURL=truenas-ui-components.mjs.map
|