Calendar control now behaves like tabs
fixes CNVS-25666 Test plan: * Visually the appearance of the calendar button control should be the same as it always has been * For KO and SR users when the calendar button control is tabbed to, the active button should receive focus * The following constraints should be true no matter which button in the calendar button control has focus * When tab is pressed and a button in the calendar button control already has focus, focus should transition to the next focusable element in the DOM outside of the calendar button control * Similarly, when shift+tab is pressed focus should transition to the previous focusable element in the DOM outside of the calendar button control * When using the calendar button control with a SR it should announce itself as a tab list and state which tab is currently selected * As new tabs are activated using the arrow keys the SR should announce which is currently selected * Pressing the left and up arrow keys should change the active button to the previous button in the calendar button control and the calendar view should be updated to the newly activate button's view. * This behavior should wrap around when the beginning of the list is reached * Pressing the right and down arrow keys should change the active button to the next button in the calendar button control and the calendar view should be updated to the newly activate button's view. * This behavior should wrap around when the end of the list is reached Change-Id: I308b6cd121d71a7c24bbb7c235aa99b1d5250d44 Reviewed-on: https://gerrit.instructure.com/70237 Tested-by: Jenkins Reviewed-by: Matthew Wheeler <mwheeler@instructure.com> Product-Review: Aaron Cannon <acannon@instructure.com> QA-Review: Adrian Russell <arussell@instructure.com>
This commit is contained in:
parent
7e527b7e1f
commit
53b1e3a1c8
|
@ -27,6 +27,7 @@ define [
|
||||||
'click .scheduler_done_button': '_triggerDone'
|
'click .scheduler_done_button': '_triggerDone'
|
||||||
'click #create_new_event_link': '_triggerCreateNewEvent'
|
'click #create_new_event_link': '_triggerCreateNewEvent'
|
||||||
'click #refresh_calendar_link': '_triggerRefreshCalendar'
|
'click #refresh_calendar_link': '_triggerRefreshCalendar'
|
||||||
|
'keydown .calendar_view_buttons': '_handleKeyDownEvent'
|
||||||
|
|
||||||
initialize: ->
|
initialize: ->
|
||||||
super
|
super
|
||||||
|
@ -49,12 +50,27 @@ define [
|
||||||
|
|
||||||
toggleView: (e) ->
|
toggleView: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
$target = $(this)
|
$target = $(e.currentTarget)
|
||||||
$target.attr('aria-checked', true)
|
$target.attr('aria-selected', true)
|
||||||
.addClass('active')
|
.addClass('active')
|
||||||
|
.attr('tabindex', 0)
|
||||||
$target.siblings()
|
$target.siblings()
|
||||||
.attr('aria-checked', false)
|
.attr('aria-selected', false)
|
||||||
.removeClass('active')
|
.removeClass('active')
|
||||||
|
.attr('tabindex', -1)
|
||||||
|
|
||||||
|
moveToCalendarViewButton: (direction) ->
|
||||||
|
buttons = @$calendarViewButtons.children('button')
|
||||||
|
active = @$calendarViewButtons.find('.active')
|
||||||
|
activeIndex = buttons.index(active)
|
||||||
|
lastIndex = buttons.length - 1
|
||||||
|
|
||||||
|
if direction == 'prev'
|
||||||
|
activeIndex = (activeIndex + lastIndex) % buttons.length
|
||||||
|
else if direction == 'next'
|
||||||
|
activeIndex = (activeIndex + 1) % buttons.length
|
||||||
|
|
||||||
|
buttons.eq(activeIndex).focus().click()
|
||||||
|
|
||||||
showNavigator: ->
|
showNavigator: ->
|
||||||
@$navigator.show()
|
@$navigator.show()
|
||||||
|
@ -124,3 +140,12 @@ define [
|
||||||
_triggerRefreshCalendar: (event) ->
|
_triggerRefreshCalendar: (event) ->
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@trigger('refreshCalendar')
|
@trigger('refreshCalendar')
|
||||||
|
|
||||||
|
_handleKeyDownEvent: (event) ->
|
||||||
|
switch event.which
|
||||||
|
when 37, 38 # left, up
|
||||||
|
event.preventDefault()
|
||||||
|
@moveToCalendarViewButton('prev')
|
||||||
|
when 39, 40 # right, down
|
||||||
|
event.preventDefault()
|
||||||
|
@moveToCalendarViewButton('next')
|
||||||
|
|
|
@ -58,6 +58,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="calendar_header"></div>
|
<div id="calendar_header"></div>
|
||||||
<div id="calendar-app"></div>
|
<div id="calendar-app" role="tabpanel"></div>
|
||||||
<div id="calendar-drag-and-drop-container"></div>
|
<div id="calendar-drag-and-drop-container"></div>
|
||||||
|
|
||||||
|
|
|
@ -14,20 +14,20 @@
|
||||||
data-tooltip>
|
data-tooltip>
|
||||||
<span class="screenreader-only">{{#t "loading"}}Loading{{/t}}</span>
|
<span class="screenreader-only">{{#t "loading"}}Loading{{/t}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="calendar_view_buttons btn-group" role="radiogroup">
|
<span class="calendar_view_buttons btn-group" role="tablist">
|
||||||
<button type="button" id="week" class="btn calendar-button" role="radio" aria-checked="false">
|
<button type="button" id="week" class="btn calendar-button" role="tab" aria-selected="false" aria-controls="calendar-app" tabindex="-1">
|
||||||
{{#t "links.calendar_week"}}Week{{/t}}
|
{{#t "links.calendar_week"}}Week{{/t}}
|
||||||
<span class="screenreader-only accessibility-warning">{{#t "links.accessibility_warning"}}Warning: For improved accessibility of calendar events, please use the agenda view.{{/t}}</span>
|
<span class="screenreader-only accessibility-warning">{{#t "links.accessibility_warning"}}Warning: For improved accessibility of calendar events, please use the agenda view.{{/t}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" id="month" class="btn calendar-button" role="radio" aria-checked="false">
|
<button type="button" id="month" class="btn calendar-button" role="tab" aria-selected="false" aria-controls="calendar-app" tabindex="-1">
|
||||||
{{#t "links.calendar_month"}}Month{{/t}}
|
{{#t "links.calendar_month"}}Month{{/t}}
|
||||||
<span class="screenreader-only accessibility-warning">{{#t "links.accessibility_warning"}}Warning: For improved accessibility of calendar events, please use the agenda view.{{/t}}</span>
|
<span class="screenreader-only accessibility-warning">{{#t "links.accessibility_warning"}}Warning: For improved accessibility of calendar events, please use the agenda view.{{/t}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" id="agenda" class="btn" role="radio" aria-checked="false">
|
<button type="button" id="agenda" class="btn" role="tab" aria-selected="false" aria-controls="calendar-app" tabindex="-1">
|
||||||
{{#t "links.calendar_agenda"}}Agenda{{/t}}
|
{{#t "links.calendar_agenda"}}Agenda{{/t}}
|
||||||
</button>
|
</button>
|
||||||
{{#if showScheduler}}
|
{{#if showScheduler}}
|
||||||
<button type="button" id="scheduler" class="btn" role="radio" aria-checked="false">
|
<button type="button" id="scheduler" class="btn" role="tab" aria-selected="false" aria-controls="calendar-app" tabindex="-1">
|
||||||
{{#t "links.calendar_scheduler"}}Scheduler{{/t}}
|
{{#t "links.calendar_scheduler"}}Scheduler{{/t}}
|
||||||
</button>
|
</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
define [
|
||||||
|
'jquery'
|
||||||
|
'compiled/views/calendar/CalendarHeader'
|
||||||
|
], ($, CalendarHeader) ->
|
||||||
|
|
||||||
|
module 'CalendarHeader',
|
||||||
|
setup: ->
|
||||||
|
@header = new CalendarHeader()
|
||||||
|
@header.$el.appendTo $('#fixtures')
|
||||||
|
|
||||||
|
teardown: ->
|
||||||
|
@header.$el.remove()
|
||||||
|
$("#fixtures").empty()
|
||||||
|
|
||||||
|
test '#moveToCalendarViewButton clicks the next calendar view button', (assert) ->
|
||||||
|
done = assert.async()
|
||||||
|
buttons = $('.calendar_view_buttons button')
|
||||||
|
buttons.first().click()
|
||||||
|
buttons.eq(1).on('click', ->
|
||||||
|
ok true, "next button was clicked"
|
||||||
|
done()
|
||||||
|
)
|
||||||
|
|
||||||
|
@header.moveToCalendarViewButton('next')
|
||||||
|
|
||||||
|
test '#moveToCalendarViewButton wraps around to the first calendar view button', (assert) ->
|
||||||
|
done = assert.async()
|
||||||
|
buttons = $('.calendar_view_buttons button')
|
||||||
|
buttons.last().click()
|
||||||
|
|
||||||
|
buttons.first().on('click', ->
|
||||||
|
ok true, "first button was clicked"
|
||||||
|
done()
|
||||||
|
)
|
||||||
|
|
||||||
|
@header.moveToCalendarViewButton('next')
|
||||||
|
|
||||||
|
test '#moveToCalendarViewButton clicks the previous calendar view button', (assert) ->
|
||||||
|
done = assert.async()
|
||||||
|
buttons = $('.calendar_view_buttons button')
|
||||||
|
buttons.last().click()
|
||||||
|
buttons.eq(buttons.length - 2).on('click', ->
|
||||||
|
ok true, "previous button was clicked"
|
||||||
|
done()
|
||||||
|
)
|
||||||
|
|
||||||
|
@header.moveToCalendarViewButton('prev')
|
||||||
|
|
||||||
|
test '#moveToCalendarViewButton wraps around to the last calendar view button', (assert) ->
|
||||||
|
done = assert.async()
|
||||||
|
buttons = $('.calendar_view_buttons button')
|
||||||
|
buttons.first().click()
|
||||||
|
|
||||||
|
buttons.last().on('click', ->
|
||||||
|
ok true, "last button was clicked"
|
||||||
|
done()
|
||||||
|
)
|
||||||
|
|
||||||
|
@header.moveToCalendarViewButton('prev')
|
||||||
|
|
||||||
|
test "calls #moveToCalendarViewButton with 'prev' when left key is pressed", (assert) ->
|
||||||
|
done = assert.async()
|
||||||
|
moveToCalendarViewButton = @header.moveToCalendarViewButton
|
||||||
|
@header.moveToCalendarViewButton = (direction) =>
|
||||||
|
equal direction, 'prev'
|
||||||
|
@header.moveToCalendarViewButton = moveToCalendarViewButton
|
||||||
|
done()
|
||||||
|
e = $.Event('keydown', { which: 37 })
|
||||||
|
$('.calendar_view_buttons').trigger(e)
|
||||||
|
|
||||||
|
test "calls #moveToCalendarViewButton with 'prev' when up key is pressed", (assert) ->
|
||||||
|
done = assert.async()
|
||||||
|
moveToCalendarViewButton = @header.moveToCalendarViewButton
|
||||||
|
@header.moveToCalendarViewButton = (direction) =>
|
||||||
|
equal direction, 'prev'
|
||||||
|
@header.moveToCalendarViewButton = moveToCalendarViewButton
|
||||||
|
done()
|
||||||
|
e = $.Event('keydown', { which: 38 })
|
||||||
|
$('.calendar_view_buttons').trigger(e)
|
||||||
|
|
||||||
|
test "calls #moveToCalendarViewButton with 'next' when right key is pressed", (assert) ->
|
||||||
|
done = assert.async()
|
||||||
|
moveToCalendarViewButton = @header.moveToCalendarViewButton
|
||||||
|
@header.moveToCalendarViewButton = (direction) =>
|
||||||
|
equal direction, 'next'
|
||||||
|
@header.moveToCalendarViewButton = moveToCalendarViewButton
|
||||||
|
done()
|
||||||
|
e = $.Event('keydown', { which: 39 })
|
||||||
|
$('.calendar_view_buttons').trigger(e)
|
||||||
|
|
||||||
|
test "calls #moveToCalendarViewButton with 'next' when down key is pressed", (assert) ->
|
||||||
|
done = assert.async()
|
||||||
|
moveToCalendarViewButton = @header.moveToCalendarViewButton
|
||||||
|
@header.moveToCalendarViewButton = (direction) =>
|
||||||
|
equal direction, 'next'
|
||||||
|
@header.moveToCalendarViewButton = moveToCalendarViewButton
|
||||||
|
done()
|
||||||
|
e = $.Event('keydown', { which: 40 })
|
||||||
|
$('.calendar_view_buttons').trigger(e)
|
||||||
|
|
||||||
|
test 'when a calendar view button is clicked it is properly activated', (assert) ->
|
||||||
|
done = assert.async()
|
||||||
|
$('.calendar_view_buttons button').last().on 'click', (e) =>
|
||||||
|
@header.toggleView(e)
|
||||||
|
|
||||||
|
button = $('.calendar_view_buttons button').last()
|
||||||
|
equal button.attr('aria-selected'), 'true'
|
||||||
|
equal button.attr('tabindex'), '0'
|
||||||
|
ok button.hasClass('active')
|
||||||
|
|
||||||
|
button.siblings().each ->
|
||||||
|
equal $(this).attr('aria-selected'), 'false'
|
||||||
|
equal $(this).attr('tabindex'), '-1'
|
||||||
|
notOk $(this).hasClass('active')
|
||||||
|
|
||||||
|
done()
|
||||||
|
|
||||||
|
$('.calendar_view_buttons button').last().click()
|
||||||
|
|
Loading…
Reference in New Issue