@truenas/ui-components 0.1.32 → 0.1.34

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.
@@ -17,13 +17,13 @@ import { TemplatePortal, PortalModule, ComponentPortal } from '@angular/cdk/port
17
17
  import { CdkMenu, CdkMenuItem, CdkMenuTrigger } from '@angular/cdk/menu';
18
18
  import { trigger, state, transition, style, animate } from '@angular/animations';
19
19
  import { SPACE, ENTER, END, HOME, DOWN_ARROW, UP_ARROW, RIGHT_ARROW, LEFT_ARROW } from '@angular/cdk/keycodes';
20
+ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
20
21
  import { DataSource } from '@angular/cdk/collections';
21
22
  import * as i1$3 from '@angular/cdk/tree';
22
23
  import { CdkTree, CdkTreeModule, CdkTreeNode, CDK_TREE_NODE_OUTLET_NODE, CdkTreeNodeOutlet, CdkNestedTreeNode } from '@angular/cdk/tree';
23
24
  export { FlatTreeControl } from '@angular/cdk/tree';
24
25
  import { map } from 'rxjs/operators';
25
26
  import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
26
- import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
27
27
  import { ScrollingModule } from '@angular/cdk/scrolling';
28
28
 
29
29
  let nextId = 0;
@@ -3242,6 +3242,108 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
3242
3242
  ], template: "<div [ngClass]=\"classes()\">\n <label class=\"tn-radio__label\" [for]=\"id\">\n <input\n #radioEl\n type=\"radio\"\n class=\"tn-radio__input\"\n [id]=\"id\"\n [name]=\"name()\"\n [value]=\"value()\"\n [checked]=\"checked\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [attr.data-testid]=\"testId()\"\n [attr.aria-describedby]=\"error() ? id + '-error' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n (change)=\"onRadioChange($event)\"\n />\n <span class=\"tn-radio__checkmark\"></span>\n <span class=\"tn-radio__text\">{{ label() }}</span>\n </label>\n\n @if (error()) {\n <div\n class=\"tn-radio__error\"\n role=\"alert\"\n aria-live=\"polite\"\n [id]=\"id + '-error'\"\n >\n {{ error() }}\n </div>\n }\n</div>", styles: [".tn-radio{display:inline-block;margin-bottom:.5rem}.tn-radio__label{display:flex;align-items:center;cursor:pointer;-webkit-user-select:none;user-select:none;gap:.5rem}.tn-radio__label:hover .tn-radio__checkmark{border-color:var(--tn-primary, #007cba)}.tn-radio__input{position:absolute;opacity:0;cursor:pointer;height:0;width:0}.tn-radio__input:focus+.tn-radio__checkmark{outline:2px solid var(--tn-primary, #007cba);outline-offset:2px}.tn-radio__input:checked+.tn-radio__checkmark{border-color:var(--tn-primary, #007cba);background-color:var(--tn-primary, #007cba)}.tn-radio__input:checked+.tn-radio__checkmark:after{display:block}.tn-radio__input:disabled+.tn-radio__checkmark{border-color:var(--tn-lines, #e5e7eb);background-color:var(--tn-alt-bg1, #f8f9fa);cursor:not-allowed}.tn-radio__checkmark{position:relative;height:18px;width:18px;border:2px solid var(--tn-lines, #e5e7eb);border-radius:50%;background-color:transparent;transition:all .2s ease;flex-shrink:0}.tn-radio__checkmark:after{content:\"\";position:absolute;display:none;top:50%;left:50%;width:8px;height:8px;border-radius:50%;background-color:#fff;transform:translate(-50%,-50%)}.tn-radio__text{color:var(--tn-fg1, #000000);font-size:14px;line-height:1.4}.tn-radio__error{margin-top:.25rem;font-size:12px;color:var(--tn-red, #dc3545)}.tn-radio--disabled .tn-radio__label{cursor:not-allowed;opacity:.6}.tn-radio--disabled .tn-radio__text{color:var(--tn-fg2, #6c757d)}.tn-radio--error .tn-radio__checkmark{border-color:var(--tn-red, #dc3545)}\n"] }]
3243
3243
  }], propDecorators: { radioEl: [{ type: i0.ViewChild, args: ['radioEl', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], change: [{ type: i0.Output, args: ["change"] }] } });
3244
3244
 
3245
+ /**
3246
+ * Harness for interacting with `tn-slide-toggle` in tests.
3247
+ *
3248
+ * @example
3249
+ * ```typescript
3250
+ * const toggle = await loader.getHarness(
3251
+ * TnSlideToggleHarness.with({ label: 'Enable notifications' })
3252
+ * );
3253
+ * await toggle.toggle();
3254
+ * expect(await toggle.isChecked()).toBe(true);
3255
+ * ```
3256
+ */
3257
+ class TnSlideToggleHarness extends ComponentHarness {
3258
+ static hostSelector = 'tn-slide-toggle';
3259
+ _input = this.locatorFor('.tn-slide-toggle__input');
3260
+ _label = this.locatorForOptional('.tn-slide-toggle__label-text');
3261
+ /**
3262
+ * Gets a `HarnessPredicate` that can be used to search for a slide toggle
3263
+ * with specific attributes.
3264
+ */
3265
+ static with(options = {}) {
3266
+ return new HarnessPredicate(TnSlideToggleHarness, options)
3267
+ .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabelText(), label))
3268
+ .addOption('testId', options.testId, async (harness, testId) => {
3269
+ return (await harness.getTestId()) === testId;
3270
+ });
3271
+ }
3272
+ /**
3273
+ * Gets the label text of the slide toggle.
3274
+ *
3275
+ * @example
3276
+ * ```typescript
3277
+ * expect(await toggle.getLabelText()).toBe('Enable notifications');
3278
+ * ```
3279
+ */
3280
+ async getLabelText() {
3281
+ const label = await this._label();
3282
+ return label ? (await label.text()).trim() : '';
3283
+ }
3284
+ /**
3285
+ * Checks whether the slide toggle is currently checked.
3286
+ *
3287
+ * @example
3288
+ * ```typescript
3289
+ * expect(await toggle.isChecked()).toBe(false);
3290
+ * ```
3291
+ */
3292
+ async isChecked() {
3293
+ const input = await this._input();
3294
+ return (await input.getProperty('checked')) ?? false;
3295
+ }
3296
+ /**
3297
+ * Checks whether the slide toggle is disabled.
3298
+ */
3299
+ async isDisabled() {
3300
+ const input = await this._input();
3301
+ return (await input.getProperty('disabled')) ?? false;
3302
+ }
3303
+ /**
3304
+ * Checks whether the slide toggle is required.
3305
+ */
3306
+ async isRequired() {
3307
+ const input = await this._input();
3308
+ return (await input.getProperty('required')) ?? false;
3309
+ }
3310
+ /**
3311
+ * Gets the test ID attribute value.
3312
+ */
3313
+ async getTestId() {
3314
+ const root = await this.locatorFor('.tn-slide-toggle')();
3315
+ return root.getAttribute('data-testid');
3316
+ }
3317
+ /**
3318
+ * Toggles the slide toggle by clicking the input element.
3319
+ *
3320
+ * @example
3321
+ * ```typescript
3322
+ * await toggle.toggle();
3323
+ * ```
3324
+ */
3325
+ async toggle() {
3326
+ const input = await this._input();
3327
+ await input.click();
3328
+ }
3329
+ /**
3330
+ * Checks the slide toggle. No-op if already checked.
3331
+ */
3332
+ async check() {
3333
+ if (!(await this.isChecked())) {
3334
+ await this.toggle();
3335
+ }
3336
+ }
3337
+ /**
3338
+ * Unchecks the slide toggle. No-op if already unchecked.
3339
+ */
3340
+ async uncheck() {
3341
+ if (await this.isChecked()) {
3342
+ await this.toggle();
3343
+ }
3344
+ }
3345
+ }
3346
+
3245
3347
  class TnTabComponent {
3246
3348
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
3247
3349
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
@@ -4010,6 +4112,190 @@ class TnTabsHarness extends ComponentHarness {
4010
4112
  }
4011
4113
  }
4012
4114
 
4115
+ /**
4116
+ * Harness for a single menu item (`button.tn-menu-item`).
4117
+ * @internal Used by TnMenuHarness to interact with individual items.
4118
+ */
4119
+ class TnMenuItemHarness extends ComponentHarness {
4120
+ static hostSelector = '.tn-menu-item';
4121
+ _label = this.locatorFor('.tn-menu-item-label');
4122
+ /** Gets the label text of this menu item. */
4123
+ async getLabel() {
4124
+ const label = await this._label();
4125
+ return (await label.text()).trim();
4126
+ }
4127
+ /** Checks whether this menu item is disabled via the native `disabled` attribute. */
4128
+ async isDisabled() {
4129
+ const host = await this.host();
4130
+ const disabled = await host.getProperty('disabled');
4131
+ return !!disabled;
4132
+ }
4133
+ /** Clicks this menu item. */
4134
+ async click() {
4135
+ const host = await this.host();
4136
+ await host.click();
4137
+ }
4138
+ }
4139
+ /**
4140
+ * Harness for interacting with `tn-menu` in tests.
4141
+ *
4142
+ * Menus render in a CDK overlay **outside** the component tree, so a regular
4143
+ * `TestbedHarnessEnvironment.loader(fixture)` won't find them. Use
4144
+ * `TnMenuTesting.rootLoader(fixture)` instead — it searches the entire document.
4145
+ *
4146
+ * ## Test Setup
4147
+ *
4148
+ * ```typescript
4149
+ * import {
4150
+ * TnMenuHarness,
4151
+ * TnMenuTesting,
4152
+ * TnIconButtonHarness,
4153
+ * } from '@truenas/ui-components';
4154
+ *
4155
+ * let loader: HarnessLoader; // for components
4156
+ * let rootLoader: HarnessLoader; // for overlays (menus, dialogs)
4157
+ *
4158
+ * beforeEach(() => {
4159
+ * spectator = createComponent();
4160
+ * loader = TestbedHarnessEnvironment.loader(spectator.fixture);
4161
+ * rootLoader = TnMenuTesting.rootLoader(spectator.fixture);
4162
+ * });
4163
+ * ```
4164
+ *
4165
+ * ## Opening a Menu
4166
+ *
4167
+ * Click the trigger element to open the menu before querying it:
4168
+ *
4169
+ * ```typescript
4170
+ * // Via harness (if trigger is a tn-icon-button)
4171
+ * const trigger = await loader.getHarness(
4172
+ * TnIconButtonHarness.with({ name: 'more_vert' })
4173
+ * );
4174
+ * await trigger.click();
4175
+ *
4176
+ * // Or via DOM
4177
+ * spectator.click('[tnMenuTriggerFor]');
4178
+ * ```
4179
+ *
4180
+ * ## Querying and Clicking Items
4181
+ *
4182
+ * ```typescript
4183
+ * const menu = await rootLoader.getHarness(TnMenuHarness);
4184
+ * expect(await menu.getItemLabels()).toEqual(['Edit', 'Delete']);
4185
+ * await menu.clickItem({ label: 'Edit' });
4186
+ * await menu.clickItem({ label: /del/i }); // regex match
4187
+ * expect(await menu.isItemDisabled({ label: 'Delete' })).toBe(false);
4188
+ * ```
4189
+ */
4190
+ class TnMenuHarness extends ComponentHarness {
4191
+ static hostSelector = '.tn-menu';
4192
+ _items = this.locatorForAll(TnMenuItemHarness);
4193
+ /**
4194
+ * Gets a `HarnessPredicate` that can be used to search for a menu.
4195
+ */
4196
+ static with(options = {}) {
4197
+ return new HarnessPredicate(TnMenuHarness, options);
4198
+ }
4199
+ /**
4200
+ * Gets the labels of all menu items.
4201
+ *
4202
+ * @example
4203
+ * ```typescript
4204
+ * const menu = await rootLoader.getHarness(TnMenuHarness);
4205
+ * expect(await menu.getItemLabels()).toEqual(['Edit', 'Delete']);
4206
+ * ```
4207
+ */
4208
+ async getItemLabels() {
4209
+ const items = await this._items();
4210
+ return Promise.all(items.map(item => item.getLabel()));
4211
+ }
4212
+ /**
4213
+ * Clicks a menu item by its label text.
4214
+ *
4215
+ * @param filter Object with `label` to match (string or RegExp).
4216
+ *
4217
+ * @example
4218
+ * ```typescript
4219
+ * const menu = await rootLoader.getHarness(TnMenuHarness);
4220
+ * await menu.clickItem({ label: 'Delete' });
4221
+ * ```
4222
+ */
4223
+ async clickItem(filter) {
4224
+ const items = await this._items();
4225
+ for (const item of items) {
4226
+ const text = await item.getLabel();
4227
+ const matches = filter.label instanceof RegExp
4228
+ ? filter.label.test(text)
4229
+ : text === filter.label;
4230
+ if (matches) {
4231
+ await item.click();
4232
+ return;
4233
+ }
4234
+ }
4235
+ throw new Error(`Could not find menu item matching label "${String(filter.label)}"`);
4236
+ }
4237
+ /**
4238
+ * Checks if a menu item is disabled.
4239
+ *
4240
+ * @param filter Object with `label` to match (string or RegExp).
4241
+ * @returns Promise resolving to true if the item is disabled.
4242
+ *
4243
+ * @example
4244
+ * ```typescript
4245
+ * const menu = await rootLoader.getHarness(TnMenuHarness);
4246
+ * expect(await menu.isItemDisabled({ label: 'Delete' })).toBe(true);
4247
+ * ```
4248
+ */
4249
+ async isItemDisabled(filter) {
4250
+ const items = await this._items();
4251
+ for (const item of items) {
4252
+ const text = await item.getLabel();
4253
+ const matches = filter.label instanceof RegExp
4254
+ ? filter.label.test(text)
4255
+ : text === filter.label;
4256
+ if (matches) {
4257
+ return item.isDisabled();
4258
+ }
4259
+ }
4260
+ throw new Error(`Could not find menu item with label "${String(filter.label)}"`);
4261
+ }
4262
+ /**
4263
+ * Gets the number of menu items (excluding separators).
4264
+ */
4265
+ async getItemCount() {
4266
+ return (await this._items()).length;
4267
+ }
4268
+ }
4269
+
4270
+ /**
4271
+ * Test utilities for TnMenu.
4272
+ *
4273
+ * Provides a document-root `HarnessLoader` that can find menus rendered
4274
+ * in CDK overlays. Use this alongside `TnDialogTesting.rootLoader()` if
4275
+ * your tests also open dialogs.
4276
+ *
4277
+ * @example
4278
+ * ```typescript
4279
+ * import { TnMenuTesting, TnMenuHarness } from '@truenas/ui-components';
4280
+ *
4281
+ * const rootLoader = TnMenuTesting.rootLoader(spectator.fixture);
4282
+ *
4283
+ * // Open the menu trigger, then query the overlay
4284
+ * spectator.click('[tnMenuTriggerFor]');
4285
+ * const menu = await rootLoader.getHarness(TnMenuHarness);
4286
+ * await menu.clickItem({ label: 'Edit' });
4287
+ * ```
4288
+ */
4289
+ class TnMenuTesting {
4290
+ /**
4291
+ * Creates a `HarnessLoader` that searches the entire document,
4292
+ * including CDK overlays where menus are rendered.
4293
+ */
4294
+ static rootLoader(fixture) {
4295
+ return TestbedHarnessEnvironment.documentRootLoader(fixture);
4296
+ }
4297
+ }
4298
+
4013
4299
  class TnKeyboardShortcutComponent {
4014
4300
  shortcut = input('', ...(ngDevMode ? [{ debugName: "shortcut" }] : []));
4015
4301
  platform = input('auto', ...(ngDevMode ? [{ debugName: "platform" }] : []));
@@ -11361,5 +11647,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
11361
11647
  * Generated bundle index. Do not edit.
11362
11648
  */
11363
11649
 
11364
- 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, 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, 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, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, 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 };
11650
+ 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, 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, 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, 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 };
11365
11651
  //# sourceMappingURL=truenas-ui-components.mjs.map