@truenas/ui-components 0.1.28 → 0.1.30

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.
@@ -4148,8 +4148,13 @@ class TnFormFieldComponent {
4148
4148
  if (errors['max']) {
4149
4149
  return `Maximum value is ${errors['max'].max}`;
4150
4150
  }
4151
- // Return custom error message if available
4152
- return Object.keys(errors)[0] || 'Invalid input';
4151
+ // Return custom error message if the value is a string, otherwise use the key
4152
+ const firstKey = Object.keys(errors)[0];
4153
+ if (firstKey) {
4154
+ const value = errors[firstKey];
4155
+ return typeof value === 'string' ? value : firstKey;
4156
+ }
4157
+ return 'Invalid input';
4153
4158
  }
4154
4159
  showError = computed(() => {
4155
4160
  return this.hasError() && !!this.errorMessage();
@@ -4165,6 +4170,163 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
4165
4170
  args: [{ selector: 'tn-form-field', standalone: true, imports: [], template: "<div class=\"tn-form-field\" [attr.data-testid]=\"testId()\">\n <!-- Label -->\n @if (label()) {\n <label class=\"tn-form-field-label\" [class.required]=\"required()\">\n {{ label() }}\n @if (required()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n </label>\n }\n\n <!-- Form Control Content -->\n <div class=\"tn-form-field-wrapper\">\n <ng-content />\n </div>\n\n <!-- Hint or Error Message -->\n <div class=\"tn-form-field-subscript\">\n @if (showError()) {\n <div\n class=\"tn-form-field-error\"\n role=\"alert\"\n aria-live=\"polite\">\n {{ errorMessage() }}\n </div>\n }\n @if (showHint()) {\n <div class=\"tn-form-field-hint\">\n {{ hint() }}\n </div>\n }\n </div>\n</div>", styles: [".tn-form-field{display:block;width:100%;margin-bottom:1rem;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-form-field-label{display:block;margin-bottom:.5rem;font-size:.875rem;font-weight:500;color:var(--tn-fg1, #333);line-height:1.4}.tn-form-field-label.required .required-asterisk{color:var(--tn-error, #dc3545);margin-left:.25rem}.tn-form-field-wrapper{position:relative;width:100%;overflow:visible}.tn-form-field-wrapper :ng-deep .tn-select-container,.tn-form-field-wrapper :ng-deep .tn-input-container{margin-bottom:0}.tn-form-field-wrapper :ng-deep .tn-select-label,.tn-form-field-wrapper :ng-deep .tn-input-label{display:none}.tn-form-field-wrapper :ng-deep .tn-select-error,.tn-form-field-wrapper :ng-deep .tn-input-error{display:none}.tn-form-field-wrapper :ng-deep .tn-select-dropdown{z-index:1000}.tn-form-field-subscript{min-height:1.25rem;margin-top:.25rem;font-size:.75rem;line-height:1.4}.tn-form-field-error{color:var(--tn-error, #dc3545);margin:0}.tn-form-field-hint{color:var(--tn-fg2, #6c757d);margin:0}.tn-form-field-wrapper:has(:focus-visible) .tn-form-field-label{color:var(--tn-primary, #007bff)}.tn-form-field-wrapper:has(.error) .tn-form-field-label{color:var(--tn-error, #dc3545)}@media(prefers-reduced-motion:reduce){.tn-form-field-label{transition:none}}@media(prefers-contrast:high){.tn-form-field-label,.tn-form-field-error{font-weight:600}}\n"] }]
4166
4171
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], control: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { isSignal: true }] }] } });
4167
4172
 
4173
+ /**
4174
+ * Harness for interacting with `tn-form-field` in tests.
4175
+ * Provides methods for querying label, hint, error state, and accessing
4176
+ * the projected form control.
4177
+ *
4178
+ * @example
4179
+ * ```typescript
4180
+ * // Find a form field by label
4181
+ * const field = await loader.getHarness(TnFormFieldHarness.with({ label: 'Email' }));
4182
+ * expect(await field.getLabel()).toBe('Email');
4183
+ *
4184
+ * // Check for validation errors
4185
+ * expect(await field.hasError()).toBe(true);
4186
+ * expect(await field.getErrorMessage()).toBe('This field is required');
4187
+ *
4188
+ * // Check hint text
4189
+ * const hinted = await loader.getHarness(TnFormFieldHarness.with({ label: 'Port' }));
4190
+ * expect(await hinted.getHint()).toBe('Default port is 443');
4191
+ *
4192
+ * // Find by testId
4193
+ * const field = await loader.getHarness(TnFormFieldHarness.with({ testId: 'email-field' }));
4194
+ * ```
4195
+ */
4196
+ class TnFormFieldHarness extends ComponentHarness {
4197
+ /**
4198
+ * The selector for the host element of a `TnFormFieldComponent` instance.
4199
+ */
4200
+ static hostSelector = 'tn-form-field';
4201
+ _label = this.locatorForOptional('.tn-form-field-label');
4202
+ _error = this.locatorForOptional('.tn-form-field-error');
4203
+ _hint = this.locatorForOptional('.tn-form-field-hint');
4204
+ /**
4205
+ * Gets a `HarnessPredicate` that can be used to search for a form field
4206
+ * with specific attributes.
4207
+ *
4208
+ * @param options Options for filtering which form field instances are considered a match.
4209
+ * @returns A `HarnessPredicate` configured with the given options.
4210
+ *
4211
+ * @example
4212
+ * ```typescript
4213
+ * // Find by label text
4214
+ * const field = await loader.getHarness(TnFormFieldHarness.with({ label: 'Name' }));
4215
+ *
4216
+ * // Find by label regex
4217
+ * const field = await loader.getHarness(TnFormFieldHarness.with({ label: /email/i }));
4218
+ *
4219
+ * // Find by testId
4220
+ * const field = await loader.getHarness(TnFormFieldHarness.with({ testId: 'name-field' }));
4221
+ * ```
4222
+ */
4223
+ static with(options = {}) {
4224
+ return new HarnessPredicate(TnFormFieldHarness, options)
4225
+ .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))
4226
+ .addOption('testId', options.testId, async (harness, testId) => {
4227
+ return (await harness.getTestId()) === testId;
4228
+ });
4229
+ }
4230
+ /**
4231
+ * Gets the form field label text.
4232
+ *
4233
+ * @returns Promise resolving to the label text, or empty string if no label.
4234
+ *
4235
+ * @example
4236
+ * ```typescript
4237
+ * const field = await loader.getHarness(TnFormFieldHarness.with({ label: 'Name' }));
4238
+ * expect(await field.getLabel()).toBe('Name');
4239
+ * ```
4240
+ */
4241
+ async getLabel() {
4242
+ const label = await this._label();
4243
+ if (!label) {
4244
+ return '';
4245
+ }
4246
+ const text = await label.text();
4247
+ // Strip the required asterisk from the label text
4248
+ return text.replace(/\s*\*\s*$/, '').trim();
4249
+ }
4250
+ /**
4251
+ * Gets the error message text, if visible.
4252
+ *
4253
+ * @returns Promise resolving to the error message, or null if no error is shown.
4254
+ *
4255
+ * @example
4256
+ * ```typescript
4257
+ * const field = await loader.getHarness(TnFormFieldHarness.with({ label: 'Email' }));
4258
+ * expect(await field.getErrorMessage()).toBe('Please enter a valid email address');
4259
+ * ```
4260
+ */
4261
+ async getErrorMessage() {
4262
+ const error = await this._error();
4263
+ return error ? (await error.text()).trim() : null;
4264
+ }
4265
+ /**
4266
+ * Checks whether the form field is currently showing an error.
4267
+ *
4268
+ * @returns Promise resolving to true if an error message is visible.
4269
+ *
4270
+ * @example
4271
+ * ```typescript
4272
+ * const field = await loader.getHarness(TnFormFieldHarness.with({ label: 'Email' }));
4273
+ * expect(await field.hasError()).toBe(true);
4274
+ * ```
4275
+ */
4276
+ async hasError() {
4277
+ const error = await this._error();
4278
+ return error !== null;
4279
+ }
4280
+ /**
4281
+ * Gets the hint text, if visible.
4282
+ *
4283
+ * @returns Promise resolving to the hint text, or null if no hint is shown.
4284
+ *
4285
+ * @example
4286
+ * ```typescript
4287
+ * const field = await loader.getHarness(TnFormFieldHarness.with({ label: 'Port' }));
4288
+ * expect(await field.getHint()).toBe('Default port is 443');
4289
+ * ```
4290
+ */
4291
+ async getHint() {
4292
+ const hint = await this._hint();
4293
+ return hint ? (await hint.text()).trim() : null;
4294
+ }
4295
+ /**
4296
+ * Checks whether the form field is marked as required.
4297
+ *
4298
+ * @returns Promise resolving to true if the required asterisk is present.
4299
+ *
4300
+ * @example
4301
+ * ```typescript
4302
+ * const field = await loader.getHarness(TnFormFieldHarness.with({ label: 'Name' }));
4303
+ * expect(await field.isRequired()).toBe(true);
4304
+ * ```
4305
+ */
4306
+ async isRequired() {
4307
+ const label = await this._label();
4308
+ if (!label) {
4309
+ return false;
4310
+ }
4311
+ return label.hasClass('required');
4312
+ }
4313
+ /**
4314
+ * Gets the test ID attribute value.
4315
+ *
4316
+ * @returns Promise resolving to the data-testid string, or null.
4317
+ *
4318
+ * @example
4319
+ * ```typescript
4320
+ * const field = await loader.getHarness(TnFormFieldHarness);
4321
+ * expect(await field.getTestId()).toBe('email-field');
4322
+ * ```
4323
+ */
4324
+ async getTestId() {
4325
+ const root = await this.locatorFor('.tn-form-field')();
4326
+ return root.getAttribute('data-testid');
4327
+ }
4328
+ }
4329
+
4168
4330
  class TnSelectComponent {
4169
4331
  options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
4170
4332
  optionGroups = input([], ...(ngDevMode ? [{ debugName: "optionGroups" }] : []));
@@ -10892,5 +11054,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
10892
11054
  * Generated bundle index. Do not edit.
10893
11055
  */
10894
11056
 
10895
- 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, 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, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
11057
+ 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, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
10896
11058
  //# sourceMappingURL=truenas-ui-components.mjs.map