Skip to content

Commit 9a0d8c4

Browse files
committed
Initial ARIA & Keyboard fixes for datepicker rewrite (inline only)
1 parent c1773c3 commit 9a0d8c4

File tree

3 files changed

+113
-26
lines changed

3 files changed

+113
-26
lines changed

datepicker-rewrite/date.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
22
* Calendar math built on jquery-global
3-
*
3+
*
44
* Based on Marc Grabanski's jQuery Date Plugin
55
* http://marcgrabanski.com/articles/jquery-date-plugin
66
*/
77
(function( $, undefined ) {
8-
8+
99
if ( typeof( $.global.culture ) == "undefined" ) {
1010
$.global.culture = $.global.cultures[ "default" ];
1111
}
@@ -23,7 +23,7 @@ $.date = function ( datestring, formatstring ) {
2323
setFormat: function( formatstring ) {
2424
if ( formatstring ) {
2525
format = formatstring;
26-
}
26+
}
2727
return this;
2828
},
2929
setDay: function( day ) {
@@ -34,6 +34,10 @@ $.date = function ( datestring, formatstring ) {
3434
var day = period == "D" ? date.getDate() + offset : date.getDate(),
3535
month = period == "M" ? date.getMonth() + offset : date.getMonth(),
3636
year = period == "Y" ? date.getFullYear() + offset : date.getFullYear();
37+
38+
if ( period != "D" ) {
39+
day = Math.max(1, Math.min( day, this.daysInMonth( year, month ) ) );
40+
}
3741
date = new Date( year, month, day );
3842
return this;
3943
},
@@ -45,6 +49,12 @@ $.date = function ( datestring, formatstring ) {
4549
monthname: function() {
4650
return calendar.months.names[ date.getMonth() ];
4751
},
52+
day: function() {
53+
return date.getDate();
54+
},
55+
myMonth: function() {
56+
return date.getMonth();
57+
},
4858
year: function() {
4959
return date.getFullYear();
5060
},
@@ -55,7 +65,7 @@ $.date = function ( datestring, formatstring ) {
5565
var day = ( dow + calendar.firstDay ) % 7;
5666
result.push( {
5767
shortname: calendar.days.namesShort[ day ],
58-
fullname: calendar.days.names[ day ],
68+
fullname: calendar.days.names[ day ]
5969
});
6070
}
6171
return result;

datepicker-rewrite/index.html

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -100,31 +100,36 @@
100100
</script>
101101

102102
<script id="ui-datepicker-tmpl" type="text/x-jquery-tmpl">
103-
<div class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all">
103+
<div class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all" role="region" aria-labelledby="${instance.id} + "-title"">
104104
<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix ui-corner-all">
105-
<a class="ui-datepicker-prev ui-corner-all" title="${labels.prevText}"><span class="ui-icon ui-icon-circle-triangle-w">${labels.prevText}</span></a>
106-
<a class="ui-datepicker-next ui-corner-all" title="${labels.nextText}"><span class="ui-icon ui-icon-circle-triangle-e">${labels.nextText}</span></a>
107-
<div class="ui-datepicker-title">
108-
<span class="ui-datepicker-month">${date.monthname()}</span> <span class="ui-datepicker-year">${date.year()}</span>
105+
<a href="#" class="ui-datepicker-prev ui-corner-all" title="${labels.prevText}"><span class="ui-icon ui-icon-circle-triangle-w">${labels.prevText}</span></a>
106+
<a href="#" class="ui-datepicker-next ui-corner-all" title="${labels.nextText}"><span class="ui-icon ui-icon-circle-triangle-e">${labels.nextText}</span></a>
107+
<div role="header" id="${instance.id} + "-title"" class="ui-datepicker-title" aria-live="assertive" aria-atomic="true">
108+
<div id="${instance.id}-month-lbl">
109+
<span class="ui-datepicker-month">${date.monthname()}</span> <span class="ui-datepicker-year">${date.year()}</span>
110+
</div>
111+
<span class="ui-helper-hidden-accessible">Date Picker</span>
109112
</div>
110113
</div>
111-
<table class="ui-datepicker-calendar">
112-
<thead>
113-
<tr>
114+
<table class="ui-datepicker-calendar" role="grid" aria-labelledby="${instance.id}-month-lbl" tabindex="0" aria-activedescendant="${instance.id}-${instance.focusedDay}">
115+
<thead role="presentation">
116+
<tr role="row">
114117
{{each(index, day) date.weekdays()}}
115-
<th class=""><span title="${day.fullname}">${day.shortname}</span></th>
118+
<th class="" role="columnheader" abbr="${day.fullname}" aria-label="${day.fullname}"><span title="${day.fullname}">${day.shortname}</span></th>
116119
{{/each}}
117120
</tr>
118121
</thead>
119-
<tbody>
120-
{{each(index, week) date.days()}}
121-
<tr>
122-
{{each(index, day) week.days}}
123-
<td>
122+
<tbody role="presentation">
123+
{{each(weekIndex, week) date.days()}}
124+
<tr role="row">
125+
{{each(dayIndex, day) week.days}}
126+
<td {{if day.render}}id="${instance.id}-${day.date}"{{/if}} role="gridcell" aria-selected="{{if day.current}}true{{else}}false{{/if}}" {{if !day.selectable}}aria-disabled="true"{{/if}}>
124127
{{if day.render}}
125128
{{if day.selectable}}
126-
<a title="${day.title}" class="ui-state-default{{if day.current}} ui-state-active{{/if}}{{if day.today}} ui-state-highlight{{/if}} ${day.extraClasses}" href="#">
129+
<a title="${day.title}" class="{{if day.date == instance.focusedDay}}ui-state-focus {{/if}}ui-state-default{{if day.current}} ui-state-active{{/if}}{{if day.today}} ui-state-highlight{{/if}} ${day.extraClasses}" href="#" tabindex="-1">
127130
${day.date}
131+
{{if day.today}} <span class="ui-helper-hidden-accessible">, today</span>{{/if}}
132+
{{if day.current}}<span class="ui-helper-hidden-accessible">, selected</span>{{/if}}
128133
</a>
129134
{{/if}}
130135
{{if !day.selectable}}

datepicker-rewrite/picker.js

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
/*
22
* Experimental datepicker rewrite to evaluate jquery-tmpl.
3-
*
3+
*
44
* Based on Marc Grabanski's https://github.com/1Marc/jquery-ui-datepicker-lite
55
*/
66
(function( $, undefined ) {
77

8+
var idIncrement = 0;
89
$.widget( "ui.datepicker", {
910
options: {
1011
eachDay: $.noop,
@@ -18,6 +19,8 @@ $.widget( "ui.datepicker", {
1819
var self = this;
1920
this.date = $.date();
2021
this.date.eachDay = this.options.eachDay;
22+
this.id = "ui-datepicker-" + idIncrement;
23+
idIncrement++;
2124
if ( this.element.is( "input" ) ) {
2225
self._bind( {
2326
click: "open",
@@ -39,7 +42,7 @@ $.widget( "ui.datepicker", {
3942
});
4043
this.picker.delegate( ".ui-datepicker-next", "click", function( event ) {
4144
event.preventDefault();
42-
self.date.adjust( "M", +1 )
45+
self.date.adjust( "M", 1 );
4346
self.refresh();
4447
});
4548
this.picker.delegate( ".ui-datepicker-calendar a", "click", function( event ) {
@@ -54,31 +57,100 @@ $.widget( "ui.datepicker", {
5457
self.refresh();
5558
}
5659
self._trigger( "select", event, {
57-
date: self.date.format(),
60+
date: self.date.format()
5861
});
5962
});
60-
63+
6164
this.picker.delegate( ".ui-datepicker-header a, .ui-datepicker-calendar a", "mouseenter.datepicker mouseleave.datepicker", function() {
6265
$( this ).toggleClass( "ui-state-hover" );
6366
});
64-
67+
68+
69+
this.picker.delegate(".ui-datepicker-calendar", "keydown", function(event) {
70+
if (jQuery.inArray(event.keyCode, [ 13, 33, 34, 35, 36, 37, 38, 39, 40]) == -1) {
71+
return; //only interested navigation keys
72+
}
73+
var noDateChange = false,
74+
activeCell = $( "#" + self.grid.attr( "aria-activedescendant" ) )
75+
oldMonth = self.date.myMonth();
76+
oldYear = self.date.year();
77+
78+
switch ( event.keyCode ) {
79+
case 13: // Enter
80+
activeCell.children("a").first().click();
81+
self.grid.focus(1);
82+
return;
83+
break;
84+
case 33: //PgUp
85+
self.date.adjust(event.ctrlKey || event.metaKey ? "Y" : "M", 1);
86+
break;
87+
case 34: //PgDn
88+
self.date.adjust(event.ctrlKey || event.metaKey ? "Y" : "M", -1);
89+
break;
90+
case 35: //End
91+
self.date.setDay(self.date.daysInMonth());
92+
break;
93+
case 36: //Home
94+
self.date.setDay(1);
95+
break;
96+
case 37: //Left
97+
self.date.adjust("D", -1);
98+
break;
99+
case 38: //Up
100+
self.date.adjust("D", -7);
101+
break;
102+
case 39: //Right
103+
self.date.adjust("D", 1);
104+
break;
105+
case 40: //Down
106+
self.date.adjust("D", 7);
107+
break;
108+
}
109+
110+
if ( self.date.myMonth() != oldMonth || self.date.year() != oldYear ) {
111+
self.refresh();
112+
self.grid.focus(1);
113+
}
114+
else {
115+
var newId = self.id + "-" + self.date.day(),
116+
117+
newCell = $("#" + newId);
118+
119+
if ( !newCell.length ) {
120+
return;
121+
}
122+
self.grid.attr("aria-activedescendant", newId);
123+
124+
activeCell.children("a").removeClass("ui-state-focus");
125+
newCell.children("a").addClass("ui-state-focus");
126+
}
127+
event.stopPropagation();
128+
event.preventDefault();
129+
});
130+
65131
this.refresh();
66132
},
67133
refresh: function() {
68134
this.date.refresh();
69135
this.picker.empty();
70136

137+
//determine which day gridcell to focus after refresh
138+
//TODO: Prevent disabled cells from being focused
139+
var focusedDay = this.date.day();
140+
71141
$( this.options.tmpl ).tmpl( {
72142
date: this.date,
73-
labels: $.global.localize( "datepicker" )
143+
labels: $.global.localize( "datepicker" ),
144+
instance : {id : this.id, focusedDay : focusedDay}
74145
}).appendTo( this.picker )
75146
.find( "button" ).button().end()
76147

77148
if ( this.inline ) {
78149
this.picker.children().addClass( "ui-datepicker-inline" );
79-
}
150+
}
80151
// against display:none in datepicker.css
81152
this.picker.find( ".ui-datepicker" ).css( "display", "block" );
153+
this.grid = this.picker.find( ".ui-datepicker-calendar" );
82154
},
83155
open: function( event ) {
84156
this.picker.fadeIn( "fast" );

0 commit comments

Comments
 (0)