HXIDP-5181 Allow form text fields to render custom field status content (#11523)

This commit is contained in:
Benn Snyder
2026-01-27 08:47:31 -05:00
committed by GitHub
parent 42c2f78baf
commit 15806d7c9f
3 changed files with 61 additions and 8 deletions
@@ -27,10 +27,13 @@
[title]="field.tooltip" [title]="field.tooltip"
(blur)="markAsTouched()"> (blur)="markAsTouched()">
</mat-form-field> </mat-form-field>
<div class="adf-error-messages-container"> <ng-container *ngTemplateOutlet="fieldStatusTemplate ?? defaultErrorMessageTemplate; context: { $implicit: this }" />
<error-widget [error]="field.validationSummary" /> <ng-template #defaultErrorMessageTemplate>
<error-widget *ngIf="isInvalidFieldRequired() && isTouched()" <div class="adf-error-messages-container">
required="{{ 'FORM.FIELD.REQUIRED' | translate }}" /> <error-widget [error]="field.validationSummary" />
</div> <error-widget *ngIf="isInvalidFieldRequired() && isTouched()"
required="{{ 'FORM.FIELD.REQUIRED' | translate }}" />
</div>
</ng-template>
</div> </div>
</div> </div>
@@ -15,6 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormFieldTypes } from '../core/form-field-types'; import { FormFieldTypes } from '../core/form-field-types';
import { FormFieldModel } from '../core/form-field.model'; import { FormFieldModel } from '../core/form-field.model';
@@ -472,4 +473,34 @@ describe('TextWidgetComponent', () => {
expect(asterisk.textContent).toEqual('*'); expect(asterisk.textContent).toEqual('*');
}); });
}); });
describe('when fieldStatusTemplate is provided', () => {
@Component({
standalone: true,
template: `
<ng-template #fieldStatusTemplate let-widget>
<div class="custom-status-message">custom status message for {{ widget.field.name }}</div>
</ng-template>
`
})
class FieldStatusTemplateTestComponent {
@ViewChild('fieldStatusTemplate', { static: true }) fieldStatusTemplate: TextWidgetComponent['fieldStatusTemplate'];
}
beforeEach(() => {
const templateFixture = TestBed.createComponent(FieldStatusTemplateTestComponent);
fixture.componentInstance.fieldStatusTemplate = templateFixture.componentInstance.fieldStatusTemplate;
});
it('should display custom status template', () => {
widget.field = new FormFieldModel(form, {
id: 'text-id',
name: 'text-name',
type: FormFieldTypes.TEXT
});
fixture.detectChanges();
const customStatusMessage = testingUtils.getByCSS('.custom-status-message').nativeElement;
expect(customStatusMessage?.textContent).toBe(`custom status message for ${widget.field.name}`);
});
});
}); });
@@ -17,8 +17,8 @@
/* eslint-disable @angular-eslint/component-selector */ /* eslint-disable @angular-eslint/component-selector */
import { NgIf } from '@angular/common'; import { NgIf, NgTemplateOutlet } from '@angular/common';
import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, Directive, inject, InjectionToken, Input, OnInit, TemplateRef, ViewEncapsulation } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
@@ -27,6 +27,24 @@ import { ErrorWidgetComponent } from '../error/error.component';
import { WidgetComponent } from '../widget.component'; import { WidgetComponent } from '../widget.component';
import { InputMaskDirective } from './text-mask.component'; import { InputMaskDirective } from './text-mask.component';
type FieldStatusTemplate = TemplateRef<{ $implicit: WidgetComponent }>;
const FIELD_STATUS_TEMPLATE = new InjectionToken<FieldStatusTemplate>('FIELD_STATUS_TEMPLATE');
@Directive({
selector: '[adf-field-status-template]',
providers: [
{
provide: FIELD_STATUS_TEMPLATE,
useFactory: (directive: FieldStatusTemplateDirective) => directive.template,
deps: [FieldStatusTemplateDirective]
}
]
})
export class FieldStatusTemplateDirective {
@Input('adf-field-status-template')
template?: FieldStatusTemplate;
}
@Component({ @Component({
selector: 'text-widget', selector: 'text-widget',
templateUrl: './text.widget.html', templateUrl: './text.widget.html',
@@ -42,13 +60,14 @@ import { InputMaskDirective } from './text-mask.component';
'(invalid)': 'event($event)', '(invalid)': 'event($event)',
'(select)': 'event($event)' '(select)': 'event($event)'
}, },
imports: [NgIf, TranslatePipe, MatFormFieldModule, MatInputModule, FormsModule, ErrorWidgetComponent, InputMaskDirective], imports: [NgIf, TranslatePipe, MatFormFieldModule, MatInputModule, FormsModule, ErrorWidgetComponent, InputMaskDirective, NgTemplateOutlet],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class TextWidgetComponent extends WidgetComponent implements OnInit { export class TextWidgetComponent extends WidgetComponent implements OnInit {
mask: string; mask: string;
placeholder: string; placeholder: string;
isMaskReversed: boolean; isMaskReversed: boolean;
fieldStatusTemplate = inject(FIELD_STATUS_TEMPLATE, { optional: true });
ngOnInit() { ngOnInit() {
if (this.field.params) { if (this.field.params) {