Skip to content

Commit 1a07ffb

Browse files
authored
Merge branch 'master' into mdragnev/fix-9994
2 parents 5f79457 + a0e631e commit 1a07ffb

File tree

6 files changed

+51
-29
lines changed

6 files changed

+51
-29
lines changed

projects/igniteui-angular/src/lib/date-picker/date-picker.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
</igx-prefix>
77

88
<input class="igx-date-picker__input-date" [displayValuePipe]="this.formatter ? displayValue : null" igxInput
9-
[igxDateTimeEditor]="this.inputFormat" [minValue]="this.minValue" [maxValue]="this.maxValue"
10-
[spinDelta]="this.spinDelta" [spinLoop]="this.spinLoop" [displayFormat]="this.displayFormat"
9+
[igxDateTimeEditor]="this.inputFormat" [displayFormat]="this.displayFormat"
10+
[minValue]="this.minValue" [maxValue]="this.maxValue" [spinDelta]="this.spinDelta" [spinLoop]="this.spinLoop"
1111
[disabled]="this.disabled" [placeholder]="this.placeholder" [readonly]="!this.isDropdown || this.readOnly"
1212
[igxTextSelection]="this.isDropdown && !this.readOnly" [locale]="this.locale" [attr.aria-expanded]="!this.collapsed"
1313
[attr.aria-labelledby]="this.label?.id" aria-haspopup="dialog" aria-autocomplete="none" role="combobox">

projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@
2727
<igx-input-group [type]="this.type" [displayDensity]="this.displayDensity" (click)="this.open()">
2828
<!-- only set mask placeholder when empty, otherwise input group might use it as label if none is set -->
2929
<input #singleInput igxInput type="text" readonly [disabled]="this.disabled" [placeholder]="this.value ? '' : singleInputFormat"
30-
role="combobox" aria-haspopup="grid" [attr.aria-expanded]="!this.collapsed"
31-
[attr.aria-labelledby]="this.label?.id"
30+
role="combobox" aria-haspopup="grid" [attr.aria-expanded]="!this.collapsed" [attr.aria-labelledby]="this.label?.id"
3231
[value]="this.value | dateRange: this.appliedFormat : this.locale : this.formatter" />
3332

3433
<igx-prefix *ngIf="!this.toggleComponents.length">

projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,15 @@ export class IgxDateRangePickerComponent extends PickerBaseDirective
392392
return this.getComponentDensityClass('igx-date-range-picker__label');
393393
}
394394

395+
private get required(): boolean {
396+
if (this._ngControl && this._ngControl.control && this._ngControl.control.validator) {
397+
const error = this._ngControl.control.validator({} as AbstractControl);
398+
return (error && error.required) ? true : false;
399+
}
400+
401+
return false;
402+
}
403+
395404
private get calendar(): IgxCalendarComponent {
396405
return this._calendar;
397406
}
@@ -404,15 +413,6 @@ export class IgxDateRangePickerComponent extends PickerBaseDirective
404413
return Object.assign({}, this._dialogOverlaySettings, this.overlaySettings);
405414
}
406415

407-
private get required(): boolean {
408-
if (this._ngControl && this._ngControl.control && this._ngControl.control.validator) {
409-
const error = this._ngControl.control.validator({} as AbstractControl);
410-
return (error && error.required) ? true : false;
411-
}
412-
413-
return false;
414-
}
415-
416416
private _resourceStrings = CurrentResourceStrings.DateRangePickerResStrings;
417417
private _doneButtonText = null;
418418
private _dateSeparator = null;

projects/igniteui-angular/src/lib/directives/input/input.directive.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ describe('IgxInput', () => {
369369

370370
const inputGroupElement = fixture.componentInstance.igxInputGroup.element.nativeElement;
371371
const igxInput = fixture.componentInstance.igxInput;
372+
const inputElement = igxInput.nativeElement;
372373
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
373374
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
374375

@@ -380,6 +381,7 @@ describe('IgxInput', () => {
380381

381382
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
382383
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
384+
expect(inputElement.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('false');
383385

384386
igxInput.value = '';
385387
fixture.detectChanges();
@@ -388,6 +390,7 @@ describe('IgxInput', () => {
388390

389391
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
390392
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
393+
expect(inputElement.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('false');
391394

392395
igxInput.value = undefined;
393396
fixture.detectChanges();
@@ -396,6 +399,7 @@ describe('IgxInput', () => {
396399

397400
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
398401
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
402+
expect(inputElement.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('false');
399403
}));
400404

401405
it('should correctly update state of input when value is changed via reactive, no user interactions', fakeAsync(() => {
@@ -405,6 +409,7 @@ describe('IgxInput', () => {
405409
const igxInputGroups = fixture.debugElement.queryAll(By.css('igx-input-group'));
406410
const inputGroupElement = igxInputGroups[0].nativeElement;
407411
const igxInput = fixture.componentInstance.strIgxInput;
412+
const inputElement = igxInput.nativeElement;
408413

409414
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
410415
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
@@ -414,24 +419,28 @@ describe('IgxInput', () => {
414419

415420
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
416421
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
422+
expect(inputElement.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('false');
417423

418424
fixture.componentInstance.form.patchValue({ str: '' });
419425
fixture.detectChanges();
420426

421427
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
422428
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
429+
expect(inputElement.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('false');
423430

424431
fixture.componentInstance.form.patchValue({ str: undefined });
425432
fixture.detectChanges();
426433

427434
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
428435
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
436+
expect(inputElement.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('false');
429437

430438
fixture.componentInstance.form.reset();
431439
fixture.detectChanges();
432440

433441
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
434442
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
443+
expect(inputElement.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('false');
435444
}));
436445

437446
it('should correctly update state of input when updated through ngModel, with user interactions', fakeAsync(() => {
@@ -450,6 +459,7 @@ describe('IgxInput', () => {
450459

451460
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(true);
452461
expect(igxInput.valid).toBe(IgxInputState.INVALID);
462+
expect(inputElement.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('true');
453463

454464
fixture.componentInstance.user.firstName = 'Bobby';
455465

@@ -459,6 +469,7 @@ describe('IgxInput', () => {
459469

460470
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
461471
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
472+
expect(inputElement.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('false');
462473

463474
fixture.componentInstance.user.firstName = '';
464475
fixture.detectChanges();
@@ -467,6 +478,7 @@ describe('IgxInput', () => {
467478

468479
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(true);
469480
expect(igxInput.valid).toBe(IgxInputState.INVALID);
481+
expect(inputElement.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('true');
470482

471483
fixture.componentInstance.user.firstName = undefined;
472484
fixture.detectChanges();
@@ -475,6 +487,7 @@ describe('IgxInput', () => {
475487

476488
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(true);
477489
expect(igxInput.valid).toBe(IgxInputState.INVALID);
490+
expect(inputElement.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('true');
478491
}));
479492

480493
it('should correctly update state of input when value is changed via reactive, with user interactions', fakeAsync(() => {
@@ -492,30 +505,35 @@ describe('IgxInput', () => {
492505

493506
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(true);
494507
expect(igxInput.valid).toBe(IgxInputState.INVALID);
508+
expect(input.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('true');
495509

496510
fixture.componentInstance.form.patchValue({ str: 'Bobby' });
497511
fixture.detectChanges();
498512

499513
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
500514
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
515+
expect(input.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('false');
501516

502517
fixture.componentInstance.form.patchValue({ str: '' });
503518
fixture.detectChanges();
504519

505520
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(true);
506521
expect(igxInput.valid).toBe(IgxInputState.INVALID);
522+
expect(input.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('true');
507523

508524
fixture.componentInstance.form.patchValue({ str: undefined });
509525
fixture.detectChanges();
510526

511527
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(true);
512528
expect(igxInput.valid).toBe(IgxInputState.INVALID);
529+
expect(input.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('true');
513530

514531
fixture.componentInstance.form.reset();
515532
fixture.detectChanges();
516533

517534
expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
518535
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
536+
expect(input.attributes.getNamedItem('aria-invalid').nodeValue).toEqual('false');
519537
}));
520538

521539
it('should correctly update enabled/disabled state of igxInput when changed via reactive form', fakeAsync(() => {
@@ -686,6 +704,7 @@ describe('IgxInput', () => {
686704
let asterisk = window.getComputedStyle(dom.query(By.css('.' + CSS_CLASS_INPUT_GROUP_LABEL)).nativeElement, ':after').content;
687705
expect(asterisk).toBe('"*"');
688706
expect(inputGroup.classList.contains(INPUT_GROUP_REQUIRED_CSS_CLASS)).toBe(true);
707+
expect(input.nativeElement.attributes.getNamedItem('aria-required').nodeValue).toEqual('true');
689708

690709
// 2) check that input group's --invalid class is NOT applied
691710
expect(inputGroup.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
@@ -701,6 +720,7 @@ describe('IgxInput', () => {
701720

702721
expect(inputGroup.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(true);
703722
expect(inputGroup.classList.contains(INPUT_GROUP_REQUIRED_CSS_CLASS)).toBe(true);
723+
expect(input.nativeElement.attributes.getNamedItem('aria-required').nodeValue).toEqual('true');
704724

705725
// 3) Check if the input group's --invalid and --required classes are removed when validator is dynamically cleared
706726
fix.componentInstance.removeValidators(formGroup);
@@ -713,6 +733,7 @@ describe('IgxInput', () => {
713733
expect(formReference).toBeDefined();
714734
expect(input).toBeDefined();
715735
expect(input.nativeElement.value).toEqual('');
736+
expect(input.nativeElement.attributes.getNamedItem('aria-required').nodeValue).toEqual('false');
716737
expect(inputGroup.classList.contains(INPUT_GROUP_REQUIRED_CSS_CLASS)).toEqual(false);
717738
expect(asterisk).toBe('none');
718739
expect(input.valid).toEqual(IgxInputState.INITIAL);
@@ -732,6 +753,7 @@ describe('IgxInput', () => {
732753
// interaction test - expect actual asterisk
733754
asterisk = window.getComputedStyle(dom.query(By.css('.' + CSS_CLASS_INPUT_GROUP_LABEL)).nativeElement, ':after').content;
734755
expect(asterisk).toBe('"*"');
756+
expect(input.nativeElement.attributes.getNamedItem('aria-required').nodeValue).toEqual('true');
735757
}));
736758
});
737759

projects/igniteui-angular/src/lib/directives/input/input.directive.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ import {
99
Input,
1010
OnDestroy,
1111
Optional,
12+
Renderer2,
1213
Self,
1314
} from '@angular/core';
1415
import {
1516
AbstractControl,
1617
FormControlName,
1718
NgControl,
18-
NgModel,
19+
NgModel
1920
} from '@angular/forms';
2021
import { Subscription } from 'rxjs';
2122
import { IgxInputGroupBase } from '../../input-group/input-group.common';
@@ -110,12 +111,14 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
110111
@Inject(FormControlName)
111112
protected formControl: FormControlName,
112113
protected element: ElementRef<HTMLInputElement>,
113-
protected cdr: ChangeDetectorRef
114+
protected cdr: ChangeDetectorRef,
115+
protected renderer: Renderer2
114116
) { }
115117

116118
private get ngControl(): NgControl {
117119
return this.ngModel ? this.ngModel : this.formControl;
118120
}
121+
119122
/**
120123
* Sets the `value` property.
121124
*
@@ -201,7 +204,11 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
201204
* ```
202205
*/
203206
public get required() {
204-
return this.nativeElement.hasAttribute('required');
207+
let validation;
208+
if (this.ngControl && (this.ngControl.control.validator || this.ngControl.control.asyncValidator)) {
209+
validation = this.ngControl.control.validator({} as AbstractControl);
210+
}
211+
return validation && validation.required || this.nativeElement.hasAttribute('required');
205212
}
206213
/**
207214
* @hidden
@@ -281,17 +288,12 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
281288
this._valid = IgxInputState.INITIAL;
282289
}
283290
// Also check the control's validators for required
284-
if (
285-
!this.inputGroup.isRequired &&
286-
this.ngControl &&
287-
this.ngControl.control.validator
288-
) {
289-
const validation = this.ngControl.control.validator(
290-
{} as AbstractControl
291-
);
292-
this.inputGroup.isRequired = validation && validation.required;
291+
if (this.required && !this.inputGroup.isRequired) {
292+
this.inputGroup.isRequired = this.required;
293293
}
294294

295+
this.renderer.setAttribute(this.nativeElement, 'aria-required', this.required.toString());
296+
295297
const elTag = this.nativeElement.tagName.toLowerCase();
296298
if (elTag === 'textarea') {
297299
this.isTextArea = true;
@@ -368,6 +370,9 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
368370
this._valid = IgxInputState.INITIAL;
369371
this.inputGroup.isRequired = false;
370372
}
373+
this.renderer.setAttribute(this.nativeElement, 'aria-required', this.required.toString());
374+
const ariaInvalid = this.valid === IgxInputState.INVALID;
375+
this.renderer.setAttribute(this.nativeElement, 'aria-invalid', ariaInvalid.toString());
371376
} else {
372377
this.checkNativeValidity();
373378
}

projects/igniteui-angular/src/lib/tabs/tabs.directive.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,6 @@ export interface ITabsSelectedItemChangeEventArgs extends ITabsBaseEventArgs {
2828
@Directive()
2929
export abstract class IgxTabsDirective extends IgxCarouselComponentBase implements IgxTabsBase, AfterViewInit, OnDestroy {
3030

31-
/** @hidden */
32-
@HostBinding('attr.role')
33-
public role = 'tabs';
34-
3531
/**
3632
* An @Input property that gets/sets the index of the selected item.
3733
* Default value is 0 if contents are defined otherwise defaults to -1.

0 commit comments

Comments
 (0)