@truenas/ui-components 0.2.3 → 0.2.4
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.
|
@@ -6448,7 +6448,18 @@ class TnFormFieldComponent {
|
|
|
6448
6448
|
required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
|
|
6449
6449
|
testId = input(undefined, ...(ngDevMode ? [{ debugName: "testId" }] : []));
|
|
6450
6450
|
subscriptSizing = input('dynamic', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : []));
|
|
6451
|
-
/**
|
|
6451
|
+
/**
|
|
6452
|
+
* Optional tooltip shown via a help icon.
|
|
6453
|
+
*
|
|
6454
|
+
* With a `label`, the icon renders next to the label in the label row. Without
|
|
6455
|
+
* one, the icon renders inline after the projected control instead — for
|
|
6456
|
+
* controls that carry their own label (e.g. `tn-checkbox`), where a detached
|
|
6457
|
+
* icon row above the control would look orphaned.
|
|
6458
|
+
*
|
|
6459
|
+
* Inline mode targets compact, self-labeled controls: the wrapper becomes a
|
|
6460
|
+
* flex row, so a full-width control (e.g. a label-less `tn-input`) would
|
|
6461
|
+
* shrink toward its content width. Give such fields a `label` instead.
|
|
6462
|
+
*/
|
|
6452
6463
|
tooltip = input('', ...(ngDevMode ? [{ debugName: "tooltip" }] : []));
|
|
6453
6464
|
/** Placement of the tooltip relative to its help icon. */
|
|
6454
6465
|
tooltipPosition = input('above', ...(ngDevMode ? [{ debugName: "tooltipPosition" }] : []));
|
|
@@ -6481,10 +6492,24 @@ class TnFormFieldComponent {
|
|
|
6481
6492
|
/**
|
|
6482
6493
|
* Whether the required indicator renders: forced via the `required` input, or
|
|
6483
6494
|
* inferred from the projected control's validators (mirrors Angular Material's
|
|
6484
|
-
* `hasValidator(Validators.required)` approach
|
|
6485
|
-
* or custom required-like
|
|
6495
|
+
* `hasValidator(Validators.required)` approach, extended to `requiredTrue` for
|
|
6496
|
+
* boolean controls — reference equality, so composed or custom required-like
|
|
6497
|
+
* validators need the explicit input).
|
|
6486
6498
|
*/
|
|
6487
6499
|
showRequired = computed(() => this.required() || this.controlState().required, ...(ngDevMode ? [{ debugName: "showRequired" }] : []));
|
|
6500
|
+
/**
|
|
6501
|
+
* Whether the tooltip icon renders inline after the projected control rather
|
|
6502
|
+
* than in the label row — true when a tooltip is set but no label is.
|
|
6503
|
+
*/
|
|
6504
|
+
showInlineTooltip = computed(() => !!this.tooltip() && !this.label(), ...(ngDevMode ? [{ debugName: "showInlineTooltip" }] : []));
|
|
6505
|
+
/**
|
|
6506
|
+
* Whether the required indicator renders inline after the projected control —
|
|
6507
|
+
* with no label there is no label row to host the asterisk, so a required
|
|
6508
|
+
* self-labeled control (e.g. a consent `tn-checkbox`) still gets one.
|
|
6509
|
+
*/
|
|
6510
|
+
showInlineRequired = computed(() => !this.label() && this.showRequired(), ...(ngDevMode ? [{ debugName: "showInlineRequired" }] : []));
|
|
6511
|
+
/** Whether the wrapper hosts any inline extras and lays out as a flex row. */
|
|
6512
|
+
showInlineExtras = computed(() => this.showInlineTooltip() || this.showInlineRequired(), ...(ngDevMode ? [{ debugName: "showInlineExtras" }] : []));
|
|
6488
6513
|
hasError = computed(() => {
|
|
6489
6514
|
const state = this.controlState();
|
|
6490
6515
|
return state.invalid && state.interacted;
|
|
@@ -6517,7 +6542,8 @@ class TnFormFieldComponent {
|
|
|
6517
6542
|
invalid: !!control.invalid,
|
|
6518
6543
|
interacted: !!(control.dirty || control.touched),
|
|
6519
6544
|
errors: control.errors ?? null,
|
|
6520
|
-
required: !!control.control?.hasValidator(Validators.required)
|
|
6545
|
+
required: !!(control.control?.hasValidator(Validators.required)
|
|
6546
|
+
|| control.control?.hasValidator(Validators.requiredTrue)),
|
|
6521
6547
|
});
|
|
6522
6548
|
}
|
|
6523
6549
|
}
|
|
@@ -6589,11 +6615,11 @@ class TnFormFieldComponent {
|
|
|
6589
6615
|
return this.subscriptSizing() === 'fixed' || this.showError() || this.showHint();
|
|
6590
6616
|
}, ...(ngDevMode ? [{ debugName: "showSubscript" }] : []));
|
|
6591
6617
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnFormFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6592
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnFormFieldComponent, isStandalone: true, selector: "tn-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null }, tooltipPosition: { classPropertyName: "tooltipPosition", publicName: "tooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "control", first: true, predicate: NgControl, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tn-form-field\" tnTestIdType=\"form-field\" [tnTestId]=\"testId()\">\n <!-- Label -->\n @if (label()
|
|
6618
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnFormFieldComponent, isStandalone: true, selector: "tn-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null }, tooltipPosition: { classPropertyName: "tooltipPosition", publicName: "tooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "control", first: true, predicate: NgControl, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tn-form-field\" tnTestIdType=\"form-field\" [tnTestId]=\"testId()\">\n <!-- Label -->\n @if (label()) {\n <div class=\"tn-form-field-label-row\">\n <label class=\"tn-form-field-label\" [class.required]=\"showRequired()\">\n {{ label() }}\n @if (showRequired()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n </label>\n @if (tooltip()) {\n <ng-container [ngTemplateOutlet]=\"tooltipButton\" />\n }\n </div>\n }\n\n <!-- Form Control Content -->\n <div\n class=\"tn-form-field-wrapper\"\n [class.tn-form-field-wrapper--inline]=\"showInlineExtras()\">\n <ng-content />\n @if (showInlineRequired()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n @if (showInlineTooltip()) {\n <ng-container [ngTemplateOutlet]=\"tooltipButton\" />\n }\n </div>\n\n <!-- Hint or Error Message -->\n @if (showSubscript()) {\n <div class=\"tn-form-field-subscript\" [class.tn-form-field-subscript-dynamic]=\"subscriptSizing() === 'dynamic'\">\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 }\n</div>\n\n<ng-template #tooltipButton>\n <button\n type=\"button\"\n class=\"tn-form-field-tooltip\"\n [attr.aria-label]=\"tooltip()\"\n [tnTooltip]=\"tooltip()\"\n [tnTooltipPosition]=\"tooltipPosition()\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" />\n </button>\n</ng-template>\n", styles: [".tn-form-field{display:block;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-form-field-label-row{display:flex;align-items:center;gap:.375rem;margin-bottom:.5rem}.tn-form-field-label{display:block;font-size:1rem;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-tooltip{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--tn-fg2, #6c757d);cursor:help;line-height:0}.tn-form-field-tooltip:hover,.tn-form-field-tooltip:focus-visible{color:var(--tn-primary, #007bff)}.tn-form-field-tooltip:focus-visible{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px;border-radius:50%}.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-wrapper--inline{align-items:center;display:flex;gap:.375rem}.tn-form-field-wrapper--inline .required-asterisk{color:var(--tn-error, #dc3545)}.tn-form-field-subscript{min-height:1.25rem;margin-top:.25rem;font-size:.75rem;line-height:1.4}.tn-form-field-subscript-dynamic{min-height:0}.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"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }, { kind: "directive", type: TnTooltipDirective, selector: "[tnTooltip]", inputs: ["tnTooltip", "tnTooltipPosition", "tnTooltipDisabled", "tnTooltipShowDelay", "tnTooltipHideDelay", "tnTooltipClass"] }] });
|
|
6593
6619
|
}
|
|
6594
6620
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnFormFieldComponent, decorators: [{
|
|
6595
6621
|
type: Component,
|
|
6596
|
-
args: [{ selector: 'tn-form-field', standalone: true, imports: [TnTestIdDirective, TnIconComponent, TnTooltipDirective], template: "<div class=\"tn-form-field\" tnTestIdType=\"form-field\" [tnTestId]=\"testId()\">\n <!-- Label -->\n @if (label()
|
|
6622
|
+
args: [{ selector: 'tn-form-field', standalone: true, imports: [NgTemplateOutlet, TnTestIdDirective, TnIconComponent, TnTooltipDirective], template: "<div class=\"tn-form-field\" tnTestIdType=\"form-field\" [tnTestId]=\"testId()\">\n <!-- Label -->\n @if (label()) {\n <div class=\"tn-form-field-label-row\">\n <label class=\"tn-form-field-label\" [class.required]=\"showRequired()\">\n {{ label() }}\n @if (showRequired()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n </label>\n @if (tooltip()) {\n <ng-container [ngTemplateOutlet]=\"tooltipButton\" />\n }\n </div>\n }\n\n <!-- Form Control Content -->\n <div\n class=\"tn-form-field-wrapper\"\n [class.tn-form-field-wrapper--inline]=\"showInlineExtras()\">\n <ng-content />\n @if (showInlineRequired()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n @if (showInlineTooltip()) {\n <ng-container [ngTemplateOutlet]=\"tooltipButton\" />\n }\n </div>\n\n <!-- Hint or Error Message -->\n @if (showSubscript()) {\n <div class=\"tn-form-field-subscript\" [class.tn-form-field-subscript-dynamic]=\"subscriptSizing() === 'dynamic'\">\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 }\n</div>\n\n<ng-template #tooltipButton>\n <button\n type=\"button\"\n class=\"tn-form-field-tooltip\"\n [attr.aria-label]=\"tooltip()\"\n [tnTooltip]=\"tooltip()\"\n [tnTooltipPosition]=\"tooltipPosition()\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" />\n </button>\n</ng-template>\n", styles: [".tn-form-field{display:block;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-form-field-label-row{display:flex;align-items:center;gap:.375rem;margin-bottom:.5rem}.tn-form-field-label{display:block;font-size:1rem;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-tooltip{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--tn-fg2, #6c757d);cursor:help;line-height:0}.tn-form-field-tooltip:hover,.tn-form-field-tooltip:focus-visible{color:var(--tn-primary, #007bff)}.tn-form-field-tooltip:focus-visible{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px;border-radius:50%}.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-wrapper--inline{align-items:center;display:flex;gap:.375rem}.tn-form-field-wrapper--inline .required-asterisk{color:var(--tn-error, #dc3545)}.tn-form-field-subscript{min-height:1.25rem;margin-top:.25rem;font-size:.75rem;line-height:1.4}.tn-form-field-subscript-dynamic{min-height:0}.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"] }]
|
|
6597
6623
|
}], 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 }] }], subscriptSizing: [{ type: i0.Input, args: [{ isSignal: true, alias: "subscriptSizing", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], tooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPosition", required: false }] }], errorMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessages", required: false }] }], control: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { isSignal: true }] }] } });
|
|
6598
6624
|
|
|
6599
6625
|
/**
|
|
@@ -6749,6 +6775,22 @@ class TnFormFieldHarness extends ComponentHarness {
|
|
|
6749
6775
|
const tooltip = await this._tooltip();
|
|
6750
6776
|
return tooltip ? tooltip.getAttribute('aria-label') : null;
|
|
6751
6777
|
}
|
|
6778
|
+
/**
|
|
6779
|
+
* Checks whether the tooltip help icon renders inline after the projected
|
|
6780
|
+
* control (label-less mode) rather than in the label row.
|
|
6781
|
+
*
|
|
6782
|
+
* @returns Promise resolving to true if the inline tooltip trigger is present.
|
|
6783
|
+
*
|
|
6784
|
+
* @example
|
|
6785
|
+
* ```typescript
|
|
6786
|
+
* const field = await loader.getHarness(TnFormFieldHarness.with({ testId: 'enable-fxp' }));
|
|
6787
|
+
* expect(await field.isTooltipInline()).toBe(true);
|
|
6788
|
+
* ```
|
|
6789
|
+
*/
|
|
6790
|
+
async isTooltipInline() {
|
|
6791
|
+
const inline = await this.locatorForOptional('.tn-form-field-wrapper .tn-form-field-tooltip')();
|
|
6792
|
+
return inline !== null;
|
|
6793
|
+
}
|
|
6752
6794
|
/**
|
|
6753
6795
|
* Checks whether the form field is marked as required.
|
|
6754
6796
|
*
|
|
@@ -6762,10 +6804,12 @@ class TnFormFieldHarness extends ComponentHarness {
|
|
|
6762
6804
|
*/
|
|
6763
6805
|
async isRequired() {
|
|
6764
6806
|
const label = await this._label();
|
|
6765
|
-
if (
|
|
6766
|
-
return
|
|
6807
|
+
if (label) {
|
|
6808
|
+
return label.hasClass('required');
|
|
6767
6809
|
}
|
|
6768
|
-
|
|
6810
|
+
// Label-less mode renders the asterisk inline after the projected control.
|
|
6811
|
+
const inlineAsterisk = await this.locatorForOptional('.tn-form-field-wrapper .required-asterisk')();
|
|
6812
|
+
return inlineAsterisk !== null;
|
|
6769
6813
|
}
|
|
6770
6814
|
/**
|
|
6771
6815
|
* Gets the test ID attribute value.
|