Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
8a2aa46
[DURACOM-347] make placeholder presence in model configurable,fix wro…
FrancescoMolinaro Apr 15, 2025
800a9b5
[DURACOM-347] refactor, add test and doc
FrancescoMolinaro Apr 16, 2025
4bf7bed
[DURACOM-347] fix placeholder used as aria label
FrancescoMolinaro Apr 16, 2025
1a4fb9f
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-347
FrancescoMolinaro Oct 23, 2025
ef8590f
[DURACOM-347] refactor, fix missing label for library components
FrancescoMolinaro Oct 24, 2025
4139d35
[DURACOM-347] remove unnecessary check and fix circular dependency
FrancescoMolinaro Oct 24, 2025
e560bb4
[DURACOM-347] fix lint
FrancescoMolinaro Oct 24, 2025
e27b2e2
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-347
FrancescoMolinaro Nov 12, 2025
044da75
[DURACOM-347] fix lint
FrancescoMolinaro Nov 12, 2025
a1f6c66
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-347
FrancescoMolinaro Dec 1, 2025
ac69e2f
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-347
FrancescoMolinaro Feb 6, 2026
68dd890
[DURACOM-347] remove property, disable placeholder on date field and …
FrancescoMolinaro Feb 6, 2026
4680a00
[DURACOM-347] fix html indentation
FrancescoMolinaro Feb 6, 2026
5a52763
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-347
FrancescoMolinaro Mar 19, 2026
9f9cd7c
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-347
FrancescoMolinaro Mar 31, 2026
1d0925c
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-347
FrancescoMolinaro Mar 31, 2026
0323bd1
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-347
FrancescoMolinaro Apr 2, 2026
a448157
[DURACOM-347] fix tests
FrancescoMolinaro Apr 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
@let showClearfix = shouldShowClearfix();
@let showErrors = shouldShowErrorMessages();

@if (!isCheckbox && hasLabel) {
@if (!isCheckbox && hasLabel && !isDateField) {
<label
[id]="'label_' + model.id"
[for]="id"
class="form-label"
[innerHTML]="(model.required && model.label) ? (model.label | translate) + ' *' : (model.label | translate)"
[ngClass]="[getClass('element', 'label'), getClass('grid', 'label')]"></label>
[ngClass]="[getClass('element', 'label'), getClass('grid', 'label')]"></label>
}
<ng-container *ngTemplateOutlet="startTemplate?.templateRef; context: { $implicit: model };"></ng-container>
<!-- Should be *ngIf instead of class d-none, but that breaks the #componentViewContainer reference-->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ describe('DsDynamicFormControlContainerComponent test suite', () => {
const testItem: Item = new Item();
const testWSI: WorkspaceItem = new WorkspaceItem();
testWSI.item = of(createSuccessfulRemoteDataObject(testItem));
const renderer = jasmine.createSpyObj('Renderer2', ['setAttribute']);

const actions$: ReplaySubject<any> = new ReplaySubject<any>(1);

beforeEach(waitForAsync(() => {
Expand Down Expand Up @@ -303,6 +305,7 @@ describe('DsDynamicFormControlContainerComponent test suite', () => {
});

fixture.detectChanges();
renderer.setAttribute.calls.reset();
testElement = debugElement.query(By.css(`input[id='${testModel.id}']`));
}));

Expand Down Expand Up @@ -452,4 +455,43 @@ describe('DsDynamicFormControlContainerComponent test suite', () => {
});
});

it('should not show a label if is a checkbox or a date field', () => {
const checkboxLabel = fixture.debugElement.query(By.css('#label_' + formModel[0].id));
const dsDatePickerLabel = fixture.debugElement.query(By.css('#label_' + formModel[22].id));

expect(checkboxLabel).toBeNull();
expect(dsDatePickerLabel).toBeNull();
});

it('should not call handleAriaLabelForLibraryComponents if is SSR', () => {
(component as any).platformId = 'server';
(component as any).componentRef = {
instance: new DynamicNGBootstrapInputComponent(null, null),
location: { nativeElement: document.createElement('div') },
} as any;
fixture.detectChanges();

(component as any).handleAriaLabelForLibraryComponents();

expect(renderer.setAttribute).not.toHaveBeenCalled();
});

it('should set aria-label when valid input and additional property ariaLabel exist and is on browser', () => {
(component as any).platformId = 'browser';
const inputEl = document.createElement('input');
const hostEl = {
querySelector: jasmine.createSpy('querySelector').and.returnValue(inputEl),
};

(component as any).componentRef = {
instance: new DynamicNGBootstrapInputComponent(null, null),
location: { nativeElement: hostEl },
} as any;
(component as any).renderer = renderer;
component.model = { additional: { ariaLabel: 'Accessible Label' } } as any;
fixture.detectChanges();
(component as any).handleAriaLabelForLibraryComponents();
expect(renderer.setAttribute).toHaveBeenCalledWith(inputEl, 'aria-label', 'Accessible Label');
});

});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
AsyncPipe,
isPlatformBrowser,
NgClass,
NgTemplateOutlet,
} from '@angular/common';
Expand All @@ -19,7 +20,9 @@ import {
OnDestroy,
OnInit,
Output,
PLATFORM_ID,
QueryList,
Renderer2,
SimpleChanges,
Type,
ViewChild,
Expand Down Expand Up @@ -79,10 +82,12 @@ import {
import {
DYNAMIC_FORM_CONTROL_MAP_FN,
DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX,
DynamicFormArrayComponent,
DynamicFormArrayGroupModel,
DynamicFormArrayModel,
DynamicFormComponentService,
DynamicFormControl,
DynamicFormControlComponent,
DynamicFormControlContainerComponent,
DynamicFormControlEvent,
DynamicFormControlEventType,
Expand Down Expand Up @@ -129,6 +134,7 @@ import { DsDynamicTypeBindRelationService } from './ds-dynamic-type-bind-relatio
import { ExistingMetadataListElementComponent } from './existing-metadata-list-element/existing-metadata-list-element.component';
import { ExistingRelationListElementComponent } from './existing-relation-list-element/existing-relation-list-element.component';
import { DYNAMIC_FORM_CONTROL_TYPE_CUSTOM_SWITCH } from './models/custom-switch/custom-switch.model';
import { DYNAMIC_FORM_CONTROL_TYPE_DSDATEPICKER } from './models/date-picker/date-picker.model';
import { DynamicConcatModel } from './models/ds-dynamic-concat.model';
import { DsDynamicLookupRelationModalComponent } from './relation-lookup-modal/dynamic-lookup-relation-modal.component';
import { NameVariantService } from './relation-lookup-modal/name-variant.service';
Expand Down Expand Up @@ -227,6 +233,8 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
@Inject(APP_CONFIG) protected appConfig: AppConfig,
@Inject(DYNAMIC_FORM_CONTROL_MAP_FN) protected dynamicFormControlFn: DynamicFormControlMapFn,
private actions$: Actions,
protected renderer: Renderer2,
@Inject(PLATFORM_ID) protected platformId: string,
) {
super(ref, componentFactoryResolver, layoutService, validationService, dynamicFormComponentService, relationService);
this.fetchThumbnail = this.appConfig.browseBy.showThumbnails;
Expand Down Expand Up @@ -336,6 +344,11 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
return this.model.type === DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX || this.model.type === DYNAMIC_FORM_CONTROL_TYPE_CUSTOM_SWITCH;
}


get isDateField(): boolean {
return this.model.type === DYNAMIC_FORM_CONTROL_TYPE_DSDATEPICKER;
}

ngOnChanges(changes: SimpleChanges) {
if (changes && !this.isRelationship && hasValue(this.group.get(this.model.id))) {
super.ngOnChanges(changes);
Expand All @@ -357,6 +370,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo

ngAfterViewInit() {
this.showErrorMessagesPreviousStage = this.showErrorMessages;
this.handleAriaLabelForLibraryComponents();
}

protected createFormControlComponent(): void {
Expand Down Expand Up @@ -562,6 +576,24 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
}
}

private handleAriaLabelForLibraryComponents(): void {
if (!isPlatformBrowser(this.platformId)) {
return;
}

if ((this.componentRef.instance instanceof DynamicFormControlComponent) &&
!(this.componentRef.instance instanceof DynamicFormArrayComponent) &&
this.componentRef.location.nativeElement) {
const inputEl: HTMLElement | null =
this.componentRef.location.nativeElement.querySelector('input,textarea,select,[role="textbox"]');


if (inputEl && this.model?.additional?.ariaLabel) {
this.renderer.setAttribute(inputEl, 'aria-label', this.model.additional.ariaLabel);
}
}
}

/**
* Determines whether a form field should be validated based on its parent group's state.
* @returns {boolean} True if the field should be validated, false otherwise
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
<fieldset class="d-flex justify-content-start flex-wrap gap-2">
@if (!model.repeatable) {
<legend [id]="'legend_' + model.id" [ngClass]="[getClass('element', 'label'), getClass('grid', 'label')]">
{{ model.placeholder }}
{{model.label}}
@if (model.required) {
<span>*</span>
}
</legend>
}
</legend>
}
<ds-number-picker
tabindex="0"
[id]="model.id + '_year'"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@
class="form-control"
[attr.aria-labelledby]="'label_' + model.id"
[attr.autoComplete]="model.autoComplete"
[attr.aria-label]="model.label | translate"
[class.is-invalid]="showErrorMessages"
[attr.aria-label]="(model.label || model?.additional?.ariaLabel) | translate" [class.is-invalid]="showErrorMessages"
[id]="model.id"
[inputFormatter]="formatter"
[name]="model.name"
Expand Down Expand Up @@ -96,8 +95,7 @@
<input class="form-control"
[attr.aria-labelledby]="'label_' + model.id"
[attr.autoComplete]="model.autoComplete"
[attr.aria-label]="model.label | translate"
[class.is-invalid]="showErrorMessages"
[attr.aria-label]="(model.label || model?.additional?.ariaLabel) | translate" [class.is-invalid]="showErrorMessages"
[class.tree-input]="!model.readOnly"
[id]="id"
[name]="model.name"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
}
<input class="form-control"
[attr.aria-controls]="'combobox_' + id + '_listbox'"
[attr.aria-label]="model.placeholder"
[attr.aria-label]="model.label"
[attr.autoComplete]="model.autoComplete"
[class.is-invalid]="showErrorMessages"
[class.scrollable-dropdown-input]="!model.readOnly"
Expand All @@ -32,11 +32,11 @@

<div #dropdownMenu ngbDropdownMenu
class="dropdown-menu scrollable-dropdown-menu w-100"
[attr.aria-label]="model.placeholder | translate">
[attr.aria-label]="model.label | translate">
<div class="scrollable-menu"
role="listbox"
[id]="'combobox_' + id + '_listbox'"
[attr.aria-label]="model.placeholder | translate"
[attr.aria-label]="model.label | translate"
infiniteScroll
[infiniteScrollDistance]="2"
[infiniteScrollThrottle]="50"
Expand Down
8 changes: 4 additions & 4 deletions src/app/shared/form/builder/parsers/concat-field-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,18 @@ export class ConcatFieldParser extends FieldParser {
concatGroup.disabled = input1ModelConfig.readOnly;

if (isNotEmpty(this.firstPlaceholder)) {
input1ModelConfig.placeholder = this.firstPlaceholder;
input1ModelConfig.label = this.firstPlaceholder;
}

if (isNotEmpty(this.secondPlaceholder)) {
input2ModelConfig.placeholder = this.secondPlaceholder;
input2ModelConfig.label = this.secondPlaceholder;
}

// Split placeholder if is like 'placeholder1/placeholder2'
const placeholder = this.configData.label.split('/');
if (placeholder.length === 2) {
input1ModelConfig.placeholder = placeholder[0];
input2ModelConfig.placeholder = placeholder[1];
input1ModelConfig.label = placeholder[0];
input2ModelConfig.label = placeholder[1];
}

const model1 = new DsDynamicInputModel(input1ModelConfig, clsInput);
Expand Down
7 changes: 7 additions & 0 deletions src/app/shared/form/builder/parsers/date-field-parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,11 @@ describe('DateFieldParser test suite', () => {

expect(fieldModel.value).toEqual(expectedValue);
});

it('should skip setting the placeholder', () => {
const parser = new DateFieldParser(submissionId, field, initFormValues, parserOptions, null, translateService);
const fieldModel = parser.parse();

expect(fieldModel.placeholder).toBeNull();
});
});
4 changes: 2 additions & 2 deletions src/app/shared/form/builder/parsers/date-field-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export class DateFieldParser extends FieldParser {

public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any {
let malformedDate = false;
const inputDateModelConfig: DynamicDsDateControlModelConfig = this.initModel(null, false, true);
inputDateModelConfig.legend = this.configData.label;
const inputDateModelConfig: DynamicDsDateControlModelConfig = this.initModel(null, label, true);
inputDateModelConfig.legend = this.configData.repeatable ? null : this.configData.label;
inputDateModelConfig.disabled = inputDateModelConfig.readOnly;
inputDateModelConfig.toggleIcon = 'fas fa-calendar';
this.setValues(inputDateModelConfig as any, fieldValue);
Expand Down
3 changes: 2 additions & 1 deletion src/app/shared/form/builder/parsers/field-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,8 @@ export abstract class FieldParser {
if (hint) {
controlModel.hint = this.configData.hints || '&nbsp;';
}
controlModel.placeholder = this.configData.label;

controlModel.additional = { ...controlModel.additional, ariaLabel: this.configData.label };

if (this.configData.mandatory && setErrors) {
this.markAsRequired(controlModel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
[readonly]="disabled"
[disabled]="disabled"
[ngClass]="{'is-invalid': invalid}"
placeholder="{{'form.number-picker.label.' + placeholder | translate }}"
title="{{'form.number-picker.label.' + placeholder | translate}}"
[attr.aria-labelledby]="id + ' increment-' + id + ' decrement-' + id"
>
Expand Down
Loading