Commit 1c0c2904 by Matjaz Gregoric

[TNL-6028][TNL-6020] Implement back to beginning button.

The 'Go to Beginning' button is only exposed to keyboard and
screenreader users. It automatically receives focus on next TAB press
after dropping an item, except when a feedback popup is shown after
dropping the item, in which case the focus moves to the 'close' button
of the popup.

Clicking any of these two buttons moves focus back to the first item in
the item bank.
parent 8821e1b1
......@@ -531,6 +531,10 @@ msgid "Close"
msgstr ""
#: public/js/drag_and_drop.js
msgid "Go to Beginning"
msgstr ""
#: public/js/drag_and_drop.js
msgid "Problem"
msgstr ""
......
......@@ -631,6 +631,10 @@ msgid "Close"
msgstr "Çlösé Ⱡ'σя#"
#: public/js/drag_and_drop.js
msgid "Go to Beginning"
msgstr "Gö tö Bégïnnïng Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт α#"
#: public/js/drag_and_drop.js
msgid "Problem"
msgstr "Prößlém Ⱡ'σяєм ιρѕυм #"
......
......@@ -124,6 +124,9 @@ class BaseIntegrationTest(SeleniumBaseTest):
def _get_keyboard_help_dialog(self):
return self._page.find_element_by_css_selector(".keyboard-help-dialog")
def _get_go_to_beginning_button(self):
return self._page.find_element_by_css_selector('.go-to-beginning-button')
def _get_reset_button(self):
return self._page.find_element_by_css_selector('.reset-button')
......@@ -152,6 +155,14 @@ class BaseIntegrationTest(SeleniumBaseTest):
query = 'return $("{selector}").get(0).style.{style}'
return self.browser.execute_script(query.format(selector=selector, style=style))
def assertFocused(self, element):
focused_element = self.browser.switch_to.active_element
self.assertTrue(element == focused_element, 'expected element to have focus')
def assertNotFocused(self, element):
focused_element = self.browser.switch_to.active_element
self.assertTrue(element != focused_element, 'expected element to not have focus')
@staticmethod
def get_element_html(element):
return element.get_attribute('innerHTML').strip()
......@@ -310,10 +321,6 @@ class InteractionTestBase(object):
def assertNotDraggable(self, item_value):
self.assertFalse(self._get_draggable_property(item_value))
def assertFocused(self, element):
focused_element = self.browser.switch_to.active_element
self.assertTrue(element == focused_element, 'expected element to have focus')
@staticmethod
def wait_until_ondrop_xhr_finished(elem):
"""
......
......@@ -63,7 +63,7 @@ class EventsFiredTest(DefaultDataTestMixin, ParameterizedTestsMixin, BaseEventsT
{
'name': 'edx.drag_and_drop_v2.feedback.closed',
'data': {
'manually': False,
'manually': True,
'content': ITEM_CORRECT_FEEDBACK.format(zone=TOP_ZONE_TITLE),
'truncated': False,
},
......
......@@ -27,6 +27,22 @@ ITEM_DRAG_KEYBOARD_KEYS = (None, Keys.RETURN, Keys.CONTROL+'m')
class ParameterizedTestsMixin(object):
def _test_popup_focus_and_close(self, popup):
dismiss_popup_button = popup.find_element_by_css_selector('.close-feedback-popup-button')
self.assertFocused(dismiss_popup_button)
dismiss_popup_button.click()
self.assertFalse(popup.is_displayed())
# Assert focus moves to first enabled button in item bank after closing the popup.
focusable_items_in_bank = [item for item in self._get_items() if item.get_attribute('tabindex') == '0']
if len(focusable_items_in_bank) > 0:
self.assertFocused(focusable_items_in_bank[0])
def _test_next_tab_goes_to_go_to_beginning_button(self):
go_to_beginning_button = self._get_go_to_beginning_button()
self.assertNotFocused(go_to_beginning_button)
ActionChains(self.browser).send_keys(Keys.TAB).perform()
self.assertFocused(go_to_beginning_button)
def parameterized_item_positive_feedback_on_good_move(
self, items_map, scroll_down=100, action_key=None, assessment_mode=False
):
......@@ -44,10 +60,14 @@ class ParameterizedTestsMixin(object):
if assessment_mode:
self.assertEqual(feedback_popup_html, '')
self.assertFalse(popup.is_displayed())
if action_key:
# Next TAB keypress should move focus to "Go to Beginning button"
self._test_next_tab_goes_to_go_to_beginning_button()
else:
self.assertEqual(feedback_popup_html, "<p>{}</p>".format(definition.feedback_positive))
self.assert_popup_correct(popup)
self.assertTrue(popup.is_displayed())
self._test_popup_focus_and_close(popup)
def parameterized_item_negative_feedback_on_bad_move(
self, items_map, all_zones, scroll_down=100, action_key=None, assessment_mode=False
......@@ -75,11 +95,14 @@ class ParameterizedTestsMixin(object):
self.assertEqual(feedback_popup_html, '')
self.assertFalse(popup.is_displayed())
self.assert_placed_item(definition.item_id, zone_title, assessment_mode=True)
if action_key:
self._test_next_tab_goes_to_go_to_beginning_button()
else:
self.wait_until_html_in(definition.feedback_negative, feedback_popup_content)
self.assert_popup_incorrect(popup)
self.assertTrue(popup.is_displayed())
self.assert_reverted_item(definition.item_id)
self._test_popup_focus_and_close(popup)
def parameterized_move_items_between_zones(self, items_map, all_zones, scroll_down=100, action_key=None):
# Scroll drop zones into view to make sure Selenium can successfully drop items
......@@ -90,6 +113,8 @@ class ParameterizedTestsMixin(object):
for zone_id, zone_title in all_zones:
self.place_item(item_key, zone_id, action_key)
self.assert_placed_item(item_key, zone_title, assessment_mode=True)
if action_key:
self._test_next_tab_goes_to_go_to_beginning_button()
# Finally, move them all back to the bank.
self.place_item(item_key, None, action_key)
self.assert_reverted_item(item_key)
......
......@@ -2,6 +2,7 @@
from ddt import ddt, unpack, data
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.keys import Keys
from xblockutils.resources import ResourceLoader
......@@ -210,6 +211,30 @@ class TestDragAndDropRender(BaseIntegrationTest):
self.assertEqual(popup_content.text, "")
self.assertEqual(popup_wrapper.get_attribute('aria-live'), 'polite')
@data(None, Keys.RETURN)
def test_go_to_beginning_button(self, action_key):
self.load_scenario()
self.scroll_down(250)
button = self._get_go_to_beginning_button()
self.assertEqual(button.get_attribute('tabindex'), '0')
# Button is only visible to screen reader users by default.
self.assertIn('sr', button.get_attribute('class').split())
# Set focus to the element (cannot find a way to do this without execute_script).
self.browser.execute_script('$("button.go-to-beginning-button").focus()')
self.assertFocused(button)
# Button should be visible when focused.
self.assertNotIn('sr', button.get_attribute('class').split())
# Click/activate the button to move focus to the top.
if action_key:
button.send_keys(action_key)
else:
button.click()
first_focusable_item = self._get_items()[0]
self.assertFocused(first_focusable_item)
# Button should only be visible to screen readers again.
self.assertIn('sr', button.get_attribute('class').split())
def test_keyboard_help(self):
self.load_scenario()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment