Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keyboard events and ARIA markup added to speed control #2381

Merged
merged 1 commit into from
Feb 10, 2014

Conversation

jmclaus
Copy link

@jmclaus jmclaus commented Jan 30, 2014

Addresses BLD-402.

@jmclaus
Copy link
Author

jmclaus commented Jan 31, 2014

@polesye @valera-rozuvan DO NOT MERGE. Please review the new functionality of the speed menu button, I made it adhere to the following standards: http://www.w3.org/TR/wai-aria-practices/#menubutton. I will then fix the tests that are failing.

@jmclaus
Copy link
Author

jmclaus commented Feb 3, 2014

@polesye @valera-rozuvan By the way, I did not add aria-haspopup="true" to the speed button on purpose, it doesn't work well with Voice Over. If added, it will also announce the position of the video and its total length. Will investigate why. For the moment, it is not an issue as the volume control doesn't have that ARIA markup either.

speedControl = $('div.speeds'),
speedEntries = $('div.speeds>a'),
firstSpeedEntry = speedEntries.first(),
secondSpeedEntry = $(speedEntries.eq(1)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$(speedEntries.eq(1)) => speedEntries.eq(1)

@jmclaus
Copy link
Author

jmclaus commented Feb 3, 2014

@polesye @valera-rozuvan Fixed tests. Will add a couple of missing cases (ENTER & ESC on speed entries) tomorrow.


// Get previous element in array or cyles back to the last if it
// is the first.
previousSpeed = function(index) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmclaus Why is this function declared globally? Use the var keyword...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@valera-rozuvan Done. (Why is this function declared globally? Use the var keyword)

@polesye
Copy link
Contributor

polesye commented Feb 4, 2014

My thoughts: let's avoid navigation via TAB through the menu items.

When the container or its active descendant has focus, the user may navigate through the container by pressing additional keys, such as the arrow keys, to change the currently active descendant. Any additional press of the main navigation key (generally the TAB key) will move out of the container to the next widget. See w3.org

What do you think about following behaviors:

If focus is on the hint button:

  • Enter: Open or close hint popup. Select last focused hint item if opening
  • Space: Open or close hint popup. Select last focused hint item if opening
  • Esc: close hint popup
  • Tab: focus goes to the next hint button (Volume button)
  • Shift + Tab: focus goes to the previous hint button (Play/Pause button)

If focus is on a hint item:

  • Left arrow: Select previous hint item
  • Up arrow: Select previous hint item
  • Right arrow: Select next hint item
  • Down arrow: Select next hint item
  • Esc: close hint popup
  • Tab: focus goes to the next hint button (Volume button)
  • Shift + Tab: focus goes to the previous hint button (Play/Pause button)

@jmclaus
Copy link
Author

jmclaus commented Feb 4, 2014

@polesye I was modeling the TAB behavior on menu items after this:

http://adobe-accessibility.github.io/Accessible-Mega-Menu/

But I agree, it should rather adhere to the W3 standards. It's easy to change in the case of the speed control. And we should include this behavior in the upcoming plugin.

I'm not sure of the behavior of:

  1. the UP and DOWN arrows, the focus should stay on menu control:
    (in http://www.w3.org/TR/wai-aria-practices/#menubutton)
    Space or Enter - With focus on the button pressing Space or Enter will toggle the display of the drop-down menu. Focus remains on the button.
  2. the LEFT and RIGHT arrows: I think they should rather behave as the SHIFT + TAB and TAB ie:
    LEFT arrow: focus goes to the previous hint button (Play/Pause button)
    RIGHT arrow: focus goes to the next hint button (Volume button)

This is also the standard behavior in native menus.

@polesye
Copy link
Contributor

polesye commented Feb 4, 2014

I think we need to implement [Menu Button(http://www.w3.org/TR/wai-aria-practices/#menubutton) behavior in this PR and no need to support LEFT/RIGHT arrows like in Menu.

I have updated behavior:
If focus is on the menu button:

  • Enter: Open or close menu. Select the first menu item if opening
  • Space: Open or close menu. Select the first menu item if opening
  • Up arrow: Open menu and move focus onto the first menu item.
  • Esc: Close menu
  • Tab: Move focus to the next menu button (Volume button)
  • Shift + Tab: Move focus to the previous menu button (Play/Pause button)

If focus is on a menu item:

  • Up arrow: Select previous menu item
  • Down arrow: Select next menu item
  • Esc: Close menu popup
  • Tab: Move focus to the next menu button (Volume button)
  • Shift + Tab: Move focus to the previous menu button (Play/Pause button)

@jmclaus
Copy link
Author

jmclaus commented Feb 4, 2014

@anton Agreed, lets not pack this PR with too many new features. I just noticed this (under 'Menu or Menu bar' not 'Menu button'):

If a menu bar item has focus and the menu is not open, then:
Enter, Space, and the up down arrow keys opens the menu and places focus on the first menu item in the opened menu or child menu bar.

So I agree on behavior of Space/Enter as it is not an isolated menu button but part of a menu bar.

I've added a few things. Here's the updated behavior:

If focus is on the menu button:

Enter/Space/Up arrow: Open menu and move focus onto the first menu item.
Esc: Close menu. Return focus to menu button.
Tab: Move focus to the next menu button (Volume button).
Shift + Tab: Move focus to the previous menu button (Play/Pause button).

If focus is on a menu item:

Up arrow: Select previous menu item.
Down arrow: Select next menu item.
Esc: Close menu popup. Return focus to menu button.
Enter: Select menu item value. Return focus to menu button.
Tab: Close menu. Move focus to the next menu button (Volume button).
Shift + Tab: Close menu. Move focus to the previous menu button (Play/Pause button).

beforeEach(function () {
state = jasmine.initializePlayer();
speedControl = $('div.speeds');
speedEntries = $('div.speeds>a');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 speedEntries = speedControl.children('a')

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@polesye Done (speedEntries = speedControl.children('a')).

@jmclaus
Copy link
Author

jmclaus commented Feb 4, 2014

@polesye @valera-rozuvan Addressed your comments, please continue review. Two specs were added, ENTER and ESCAPE behavior on menu items (I can't figure how to test the speed change on the ENTER keypress and left a TODO note. Any idea?). Also, using 'toBeFocused()' now makes the tests fail...

@jmclaus
Copy link
Author

jmclaus commented Feb 4, 2014

@polesye @valera-rozuvan New behavior left to implement after our recent discussion:

  1. Pressing Space or Enter when Speed button has focus should open the menu and give focus to the last item in list (not the Speed button).
  2. Pressing TAB or SHIFT + TAB should give focus to Play/Pause button and Volume button respectively.

@jmclaus
Copy link
Author

jmclaus commented Feb 4, 2014

@polesye @valera-rozuvan Implemented the following:

'Pressing Space or Enter when Speed button has focus should open the menu and give focus to the last item in list (not the Speed button).'

and changed the tests accordingly. Also reverted to not using 'toBeFocused()'.

@jmclaus
Copy link
Author

jmclaus commented Feb 5, 2014

@polesye @valera-rozuvan Please continue review. I just implemented the following:

Pressing TAB or SHIFT + TAB should give focus to Play/Pause button and Volume button respectively.

and added related tests. In these, there are four things I cannot get to work (tests fail, therefore I omitted them):

  1. On ENTER, TAB, TAB+SHIFT, verify that menu closes.
  2. On ENTER, verify that speed has changed.

I welcome any suggestion.

@polesye
Copy link
Contributor

polesye commented Feb 6, 2014

  1. Open the menu with the keyboard when you mouse click away from the menu speed it should be closed.
  2. Open the menu with the keyboard -> focus in on last item -> mouse over speed menu -> mouse leave speed menu -> speed menu is closed and focus is lost. Probably, menu button should catch this focus on menu closing.
  3. Choose new speed in the menu via mouse -> menu is closed - > menu button catches focus. I think when we're working via mouse, menu button should not be outlined. What do you think?

@jmclaus
Copy link
Author

jmclaus commented Feb 6, 2014

@polesye

  1. I agree.
  2. I think that when the menu was opened with keyboard interaction, hovering the mouse on it then away from should do absolutely nothing. See http://adobe-accessibility.github.io/Accessible-Mega-Menu for example.
  3. I agree, it is BLD-804.

@polesye
Copy link
Contributor

polesye commented Feb 6, 2014

I think that when the menu was opened with keyboard interaction, hovering the mouse on it then away from should do absolutely nothing. See http://adobe-accessibility.github.io/Accessible-Mega-Menu for example.

Agree.

@polesye
Copy link
Contributor

polesye commented Feb 6, 2014

Anyway it looks great! I believe we'll add the same behavior for the captions in future.

$(nextSpeedLink).focus();
break;
// Close menu.
case KEY.TAB:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that we should not do that explicitly, it should works via native tab ordering.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@polesye If I remove the focus() call, things break. I'll leave it as is for the moment, let me wrap up the rest beforehand. Thanks.

@valera-rozuvan
Copy link
Contributor

@jmclaus Wow! How much work went into this PR! Crisp and clean. I like it = )

👍 @polesye Great job with the perfectionistic code review! You basically leave me no work = )

// entries.
speedLinks.on('click', _clickHandler.bind(state));

speedLinks.each(function(index, speedLink) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To improve performance and memory usage:

var speedButtonAnchor = state.videoSpeedControl.el.children('a');

state.videoSpeedControl.videoSpeedsEl.on('keydown', 'a.speed_link', function (event) {
    event.preventDefault();
    event.stopImmediatePropagation();

    var el = state.videoSpeedControl.el,
        index = $(event.currentTarget).parent().index();

    switch (event.keyCode) {
        // Scroll up menu, wrapping at the top. Keep menu open.
        case KEY.UP:
            _previousSpeedLink(speedLinks, index).focus();
            break;
        // Scroll down  menu, wrapping at the bottom. Keep menu
        // open.
        case KEY.DOWN:
            _nextSpeedLink(speedLinks, index).focus();
            break;
        // Close menu.
        case KEY.TAB:
           el.removeClass('open');
        break;
        // Close menu, give focus to speed control and change
        // speed.
        case KEY.ENTER:
        case KEY.SPACE:
            el.removeClass('open');
            speedButtonAnchor.focus();
            state.videoSpeedControl.changeVideoSpeed(event);
            break;
        // Close menu and give focus to speed control.
        case KEY.ESCAPE:
            el.removeClass('open');
            speedButtonAnchor.focus();
            break;
    }
});

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@polesye Yes, delegated events, elegant. Done.

@jmclaus
Copy link
Author

jmclaus commented Feb 7, 2014

@polesye @valera-rozuvan The following has been done:

  1. Open the menu with the keyboard when you mouse click away from the menu speed it should be closed.

  2. I think that when the menu was opened with keyboard interaction, hovering the mouse on it then away from should do absolutely nothing. See http://adobe-accessibility.github.io/Accessible-Mega-Menu for example.

And addressed all comments, except the one regarding TAB (or Tab + SHIFT) keypress on the speed entries. Did a lot of refactoring and got rid of the last anonymous eventHandler (key press on speed entries).

Left to do: correct the failing tests. And add a few more of them to test new functionality. It's starting to look good! Please review again when you have time. Thanks for all your help and comments!

@polesye
Copy link
Contributor

polesye commented Feb 7, 2014

👍 once all tests will be written.

@valera-rozuvan
Copy link
Contributor

👍

@jmclaus
Copy link
Author

jmclaus commented Feb 7, 2014

@valera-rozuvan @polesye OK, will let you know when they are done. Thanks.

@jmclaus
Copy link
Author

jmclaus commented Feb 10, 2014

@polesye @valera-rozuvan I added the missing tests. Please review. What should we do with the 3 disabled tests? 2 of them are not relevant since the refactoring. Should I just remove them? And the other one, I cannot get it to function. Anton suggested that we open a new ticket for that issue. Should I do that and remove the test in video_speed_control_spec.js?

@polesye
Copy link
Contributor

polesye commented Feb 10, 2014

@jmclaus I spent some time to figure out why this test doesn't work and found the problem:
Use here

spyOn($.fn, 'focus').andCallThrough();

instead

spyOn($.fn, 'focus');

And test do not close the speed menu on mouseleave if... pass.

2 of them are not relevant since the refactoring.

Yes, remove them.

@jmclaus
Copy link
Author

jmclaus commented Feb 10, 2014

@polesye @valera-rozuvan Removed 2 irrelevant tests. Fixed last test that wasn't working. Rebased and squashed commits. Good to merge? (when it passes Jenkins)


function _closeMenu(state) {
// Remove the previously added clickHandler from window element.
$(window).off('click', _clickHandler.bind(state));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this handler really removed after this? I'm not sure, because of bind.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@jmclaus
Copy link
Author

jmclaus commented Feb 10, 2014

@polesye You are right. Let me remove the bind, it is not necessary here.

focused = true
}
});
return focused;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Description: Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments. (See jquery is)

I think it can be rewritten so:

function _speedLinksFocused(state) {
    var speedLinks = state.videoSpeedControl.videoSpeedsEl
                                            .find('a.speed_link');

    return speedLinks.is(':focus');

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You right, done.

@polesye
Copy link
Contributor

polesye commented Feb 10, 2014

Good job! 👍 once Jenkins pass.

@jmclaus
Copy link
Author

jmclaus commented Feb 10, 2014

@polesye @valera-rozuvan Addressed latest comments. Rebased & squashed last commit. Checked new functionality -- all behaves correctly. All tests all pass locally.

@valera-rozuvan
Copy link
Contributor

@jmclaus "Test Result (no failures)" means you should restart the Jenkins tests manually. Go to https://jenkins.testeng.edx.org/ , log in, select "edx-all-tests-manual-pr", click on "Build with Parameters", enter the PR number. After the second pass, Jenkins usually reports OK.

@polesye
Copy link
Contributor

polesye commented Feb 10, 2014

@valera-rozuvan it doesn't help. I have the same issue in my PR.

@valera-rozuvan
Copy link
Contributor

@jmclaus 👍

@polesye
Copy link
Contributor

polesye commented Feb 10, 2014

👍

…ymous event handlers by named functions. Menu stays open on mouseleave when a speed entry has focus. In that case, the menu can be closed by clicking anywhere outside of it. [BLD-402, BLD-363]
jmclaus pushed a commit that referenced this pull request Feb 10, 2014
…_improved_a11y

Keyboard events and ARIA markup added to speed control
@jmclaus jmclaus merged commit 72ea40d into master Feb 10, 2014
@jmclaus jmclaus deleted the jmclaus/feature_video_speed_control_improved_a11y branch February 10, 2014 21:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants