Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 13fba2c

Browse files
crisbetokara
authored andcommitted
fix(datepicker): add aria-owns and aria-expanded support (#9733)
* Adds the `aria-owns` attribute to the datepicker, linking it to the calendar pane. This is necessary since the calendar isn't a child of the datepicker in the DOM. * Adds the `aria-expanded` attribute, indicating when the calendar is open. * Moves the accessibility tests to their own separate suite. Fixes #9727.
1 parent 038f3ed commit 13fba2c

File tree

2 files changed

+46
-16
lines changed

2 files changed

+46
-16
lines changed

src/components/datepicker/js/datepickerDirective.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,15 @@
8989
(ariaLabelValue ? 'aria-label="' + ariaLabelValue + '" ' : '') +
9090
'class="md-datepicker-input" ' +
9191
'aria-haspopup="true" ' +
92+
'aria-expanded="{{ctrl.isCalendarOpen}}" ' +
93+
'aria-owns="{{::ctrl.calendarPaneId}}"' +
9294
'ng-focus="ctrl.setFocused(true)" ' +
9395
'ng-blur="ctrl.setFocused(false)"> ' +
9496
triangleButton +
9597
'</div>' +
9698

9799
// This pane will be detached from here and re-attached to the document body.
98-
'<div class="md-datepicker-calendar-pane md-whiteframe-z1">' +
100+
'<div class="md-datepicker-calendar-pane md-whiteframe-z1" id="{{::ctrl.calendarPaneId}}">' +
99101
'<div class="md-datepicker-input-mask">' +
100102
'<div class="md-datepicker-input-mask-opaque"></div>' +
101103
'</div>' +
@@ -311,7 +313,7 @@
311313
this.calendarPaneOpenedFrom = null;
312314

313315
/** @type {String} Unique id for the calendar pane. */
314-
this.calendarPane.id = 'md-date-pane' + $mdUtil.nextUid();
316+
this.calendarPaneId = 'md-date-pane' + $mdUtil.nextUid();
315317

316318
/** Pre-bound click handler is saved so that the event listener can be removed. */
317319
this.bodyClickHandler = angular.bind(this, this.handleBodyClick);

src/components/datepicker/js/datepickerDirective.spec.js

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,6 @@ describe('md-datepicker', function() {
105105
expect(controller.inputElement.placeholder).toBe('Fancy new placeholder');
106106
});
107107

108-
it('should forward the aria-label to the generated input', function() {
109-
createDatepickerInstance('<md-datepicker ng-model="myDate" aria-label="Enter a date"></md-datepicker>');
110-
expect(controller.ngInputElement.attr('aria-label')).toBe('Enter a date');
111-
});
112-
113108
it('should throw an error when the model is not a date', function() {
114109
expect(function() {
115110
pageScope.myDate = '2015-01-01';
@@ -793,20 +788,53 @@ describe('md-datepicker', function() {
793788
});
794789
});
795790

796-
describe('tabindex behavior', function() {
797-
beforeEach(function() {
791+
describe('accessibility', function() {
792+
it('should forward the aria-label to the generated input', function() {
798793
ngElement && ngElement.remove();
794+
createDatepickerInstance('<md-datepicker ng-model="myDate" aria-label="Enter a date"></md-datepicker>');
795+
expect(controller.ngInputElement.attr('aria-label')).toBe('Enter a date');
799796
});
800797

801-
it('should remove the datepicker from the tab order, if no tabindex is specified', function() {
802-
createDatepickerInstance('<md-datepicker ng-model="myDate"></md-datepicker>');
803-
expect(ngElement.attr('tabindex')).toBe('-1');
798+
it('should set the aria-owns value, corresponding to the id of the calendar pane', function() {
799+
var ariaAttr = controller.ngInputElement.attr('aria-owns');
800+
801+
expect(ariaAttr).toBeTruthy();
802+
expect(controller.calendarPane.id).toBe(ariaAttr);
804803
});
805804

806-
it('should forward the tabindex to the input', function() {
807-
createDatepickerInstance('<md-datepicker ng-model="myDate" tabindex="1"></md-datepicker>');
808-
expect(ngElement.attr('tabindex')).toBeFalsy();
809-
expect(controller.ngInputElement.attr('tabindex')).toBe('1');
805+
it('should toggle the aria-expanded value', function() {
806+
expect(controller.ngInputElement.attr('aria-expanded')).toBe('false');
807+
808+
controller.openCalendarPane({
809+
target: controller.inputElement
810+
});
811+
scope.$apply();
812+
813+
expect(controller.ngInputElement.attr('aria-expanded')).toBe('true');
814+
815+
controller.closeCalendarPane();
816+
scope.$apply();
817+
818+
expect(controller.ngInputElement.attr('aria-expanded')).toBe('false');
819+
});
820+
821+
describe('tabindex behavior', function() {
822+
beforeEach(function() {
823+
ngElement && ngElement.remove();
824+
});
825+
826+
it('should remove the datepicker from the tab order, if no tabindex is specified', function() {
827+
createDatepickerInstance('<md-datepicker ng-model="myDate"></md-datepicker>');
828+
expect(ngElement.attr('tabindex')).toBe('-1');
829+
});
830+
831+
it('should forward the tabindex to the input', function() {
832+
createDatepickerInstance('<md-datepicker ng-model="myDate" tabindex="1"></md-datepicker>');
833+
expect(ngElement.attr('tabindex')).toBeFalsy();
834+
expect(controller.ngInputElement.attr('tabindex')).toBe('1');
835+
});
810836
});
837+
811838
});
839+
812840
});

0 commit comments

Comments
 (0)