Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -1,14 +1,14 @@
<div [class.mb-2]="hasLabel || model.type === 'DATE' || (model.type !== 'GROUP' && asBootstrapFormGroup) || getClass('element', 'container').includes('mb-2')"
[class.d-none]="model.hidden"
[formGroup]="group"
[ngClass]="[getClass('element', 'container'), getClass('grid', 'container')]">
@if (!isCheckbox && hasLabel) {
[formGroup]="group"
[ngClass]="[getClass('element', 'container'), getClass('grid', 'container')]">
@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 All @@ -20,8 +20,7 @@
</div>

@if (hasHint && (formBuilderService.hasArrayGroupValue(model) || (!model.repeatable && (isRelationship === false || value?.value === null)) || (model.repeatable === true && context?.index === context?.context?.groups?.length - 1)) && (!showErrorMessages || errorMessages.length === 0)) {
<small
class="text-muted ds-hint" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
<small class="text-muted ds-hint" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
}
<!-- In case of repeatable fields show empty space for all elements except the first -->
@if (context?.parent?.groups?.length > 1 && (!showErrorMessages || errorMessages.length === 0)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ describe('DsDynamicFormControlContainerComponent test suite', () => {
testWSI.item = of(createSuccessfulRemoteDataObject(testItem));
const actions$: ReplaySubject<any> = new ReplaySubject<any>(1);

const renderer = jasmine.createSpyObj('Renderer2', ['setAttribute']);

beforeEach(waitForAsync(() => {

TestBed.configureTestingModule({
Expand Down Expand Up @@ -286,6 +288,7 @@ describe('DsDynamicFormControlContainerComponent test suite', () => {
});

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

Expand Down Expand Up @@ -434,4 +437,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 All @@ -40,10 +43,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 @@ -130,6 +135,7 @@ import {
} 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 { DsDynamicLookupRelationModalComponent } from './relation-lookup-modal/dynamic-lookup-relation-modal.component';

@Component({
Expand Down Expand Up @@ -223,6 +229,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 @@ -324,6 +332,10 @@ 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 @@ -345,6 +357,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo

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

protected createFormControlComponent(): void {
Expand Down Expand Up @@ -516,4 +529,22 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
this.subs.push(collection$.subscribe((collection) => this.collection = collection));

}

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);
}
}
}
}
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 @@ -41,7 +41,7 @@
class="form-control"
[attr.aria-labelledby]="'label_' + model.id"
[attr.autoComplete]="model.autoComplete"
[attr.aria-label]="model.label | translate"
[attr.aria-label]="(model.label || model?.additional?.ariaLabel) | translate"
Comment thread
tdonohue marked this conversation as resolved.
[class.is-invalid]="showErrorMessages"
[id]="model.id"
[inputFormatter]="formatter"
Expand Down Expand Up @@ -71,7 +71,7 @@
<input class="form-control"
[attr.aria-labelledby]="'label_' + model.id"
[attr.autoComplete]="model.autoComplete"
[attr.aria-label]="model.label | translate"
[attr.aria-label]="(model.label || model?.additional?.ariaLabel) | translate"
[class.is-invalid]="showErrorMessages"
[class.tree-input]="!model.readOnly"
[id]="id"
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 @@ -92,18 +92,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 @@ -67,4 +67,11 @@ describe('DateFieldParser test suite', () => {

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

it('should skip setting the placeholder', () => {
const parser = new DateFieldParser(submissionId, field, initFormValues, parserOptions, 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 @@ -11,8 +11,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 @@ -306,7 +306,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