Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
f96d4b71
Commit
f96d4b71
authored
Aug 27, 2015
by
Christine Lytwynec
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Manage focus on modal confirmation prompt
parent
8797a4f6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
129 additions
and
3 deletions
+129
-3
common/static/common/js/components/views/feedback.js
+47
-1
common/static/common/js/components/views/feedback_prompt.js
+9
-0
common/static/common/js/spec/components/feedback_spec.js
+68
-1
common/static/js_test_requirejs.yml
+1
-0
common/test/acceptance/pages/common/utils.py
+4
-0
lms/djangoapps/teams/static/teams/js/views/team_profile.js
+0
-1
No files found.
common/static/common/js/components/views/feedback.js
View file @
f96d4b71
...
@@ -6,6 +6,17 @@
...
@@ -6,6 +6,17 @@
"backbone"
,
"backbone"
,
"text!common/templates/components/system-feedback.underscore"
],
"text!common/templates/components/system-feedback.underscore"
],
function
(
$
,
_
,
str
,
Backbone
,
systemFeedbackTemplate
)
{
function
(
$
,
_
,
str
,
Backbone
,
systemFeedbackTemplate
)
{
var
tabbable_elements
=
[
"a[href]:not([tabindex='-1'])"
,
"area[href]:not([tabindex='-1'])"
,
"input:not([disabled]):not([tabindex='-1'])"
,
"select:not([disabled]):not([tabindex='-1'])"
,
"textarea:not([disabled]):not([tabindex='-1'])"
,
"button:not([disabled]):not([tabindex='-1'])"
,
"iframe:not([tabindex='-1'])"
,
"[tabindex]:not([tabindex='-1'])"
,
"[contentEditable=true]:not([tabindex='-1'])"
];
var
SystemFeedback
=
Backbone
.
View
.
extend
({
var
SystemFeedback
=
Backbone
.
View
.
extend
({
options
:
{
options
:
{
title
:
""
,
title
:
""
,
...
@@ -16,7 +27,8 @@
...
@@ -16,7 +27,8 @@
icon
:
true
,
// should we render an icon related to the message intent?
icon
:
true
,
// should we render an icon related to the message intent?
closeIcon
:
true
,
// should we render a close button in the top right corner?
closeIcon
:
true
,
// should we render a close button in the top right corner?
minShown
:
0
,
// length of time after this view has been shown before it can be hidden (milliseconds)
minShown
:
0
,
// length of time after this view has been shown before it can be hidden (milliseconds)
maxShown
:
Infinity
// length of time after this view has been shown before it will be automatically hidden (milliseconds)
maxShown
:
Infinity
,
// length of time after this view has been shown before it will be automatically hidden (milliseconds)
outFocusElement
:
null
// element to send focus to on hide
/* Could also have an "actions" hash: here is an example demonstrating
/* Could also have an "actions" hash: here is an example demonstrating
the expected structure. For each action, by default the framework
the expected structure. For each action, by default the framework
...
@@ -65,6 +77,40 @@
...
@@ -65,6 +77,40 @@
return
this
;
return
this
;
},
},
inFocus
:
function
()
{
this
.
options
.
outFocusElement
=
this
.
options
.
outFocusElement
||
document
.
activeElement
;
// Set focus to the container.
this
.
$
(
".wrapper"
).
first
().
focus
();
// Make tabs within the prompt loop rather than setting focus
// back to the main content of the page.
var
tabbables
=
this
.
$
(
tabbable_elements
.
join
());
tabbables
.
on
(
"keydown"
,
function
(
event
)
{
// On tab backward from the first tabbable item in the prompt
if
(
event
.
which
===
9
&&
event
.
shiftKey
&&
event
.
target
===
tabbables
.
first
()[
0
])
{
event
.
preventDefault
();
tabbables
.
last
().
focus
();
}
// On tab forward from the last tabbable item in the prompt
else
if
(
event
.
which
===
9
&&
!
event
.
shiftKey
&&
event
.
target
===
tabbables
.
last
()[
0
])
{
event
.
preventDefault
();
tabbables
.
first
().
focus
();
}
});
return
this
;
},
outFocus
:
function
()
{
var
tabbables
=
this
.
$
(
tabbable_elements
.
join
()).
off
(
"keydown"
);
if
(
this
.
options
.
outFocusElement
)
{
this
.
options
.
outFocusElement
.
focus
();
}
return
this
;
},
// public API: show() and hide()
// public API: show() and hide()
show
:
function
()
{
show
:
function
()
{
clearTimeout
(
this
.
hideTimeout
);
clearTimeout
(
this
.
hideTimeout
);
...
...
common/static/common/js/components/views/feedback_prompt.js
View file @
f96d4b71
...
@@ -18,6 +18,15 @@
...
@@ -18,6 +18,15 @@
}
}
// super() in Javascript has awkward syntax :(
// super() in Javascript has awkward syntax :(
return
SystemFeedbackView
.
prototype
.
render
.
apply
(
this
,
arguments
);
return
SystemFeedbackView
.
prototype
.
render
.
apply
(
this
,
arguments
);
},
show
:
function
()
{
SystemFeedbackView
.
prototype
.
show
.
apply
(
this
,
arguments
);
return
this
.
inFocus
();
},
hide
:
function
()
{
SystemFeedbackView
.
prototype
.
hide
.
apply
(
this
,
arguments
);
return
this
.
outFocus
();
}
}
});
});
...
...
common/static/common/js/spec/components/feedback_spec.js
View file @
f96d4b71
// Generated by CoffeeScript 1.6.1
// Generated by CoffeeScript 1.6.1
(
function
()
{
(
function
()
{
define
([
"jquery"
,
"common/js/components/views/feedback"
,
"common/js/components/views/feedback_notification"
,
"common/js/components/views/feedback_alert"
,
"common/js/components/views/feedback_prompt"
,
"sinon"
],
function
(
$
,
SystemFeedback
,
NotificationView
,
AlertView
,
PromptView
,
sinon
)
{
define
([
"jquery"
,
"common/js/components/views/feedback"
,
"common/js/components/views/feedback_notification"
,
"common/js/components/views/feedback_alert"
,
"common/js/components/views/feedback_prompt"
,
"sinon"
,
"jquery.simulate"
],
function
(
$
,
SystemFeedback
,
NotificationView
,
AlertView
,
PromptView
,
sinon
)
{
var
tpl
;
var
tpl
;
tpl
=
readFixtures
(
'system-feedback.underscore'
);
tpl
=
readFixtures
(
'system-feedback.underscore'
);
beforeEach
(
function
()
{
beforeEach
(
function
()
{
...
@@ -114,6 +115,72 @@
...
@@ -114,6 +115,72 @@
});
});
});
});
describe
(
"PromptView"
,
function
()
{
describe
(
"PromptView"
,
function
()
{
beforeEach
(
function
()
{
this
.
options
=
{
title
:
"Confirming Something"
,
message
:
"Are you sure you want to do this?"
,
actions
:
{
primary
:
{
text
:
"Yes, I'm sure."
,
"class"
:
"confirm-button"
,
},
secondary
:
{
text
:
"Cancel"
,
"class"
:
"cancel-button"
,
}
}
}
this
.
inFocusSpy
=
spyOn
(
PromptView
.
Confirmation
.
prototype
,
'inFocus'
).
andCallThrough
();
return
this
.
outFocusSpy
=
spyOn
(
PromptView
.
Confirmation
.
prototype
,
'outFocus'
).
andCallThrough
();
});
it
(
"is focused on show"
,
function
()
{
var
view
;
view
=
new
PromptView
.
Confirmation
(
this
.
options
).
show
();
expect
(
this
.
inFocusSpy
).
toHaveBeenCalled
();
return
waitsFor
(
function
()
{
return
view
.
$
(
'.wrapper-prompt:focus'
).
length
===
1
;
},
"The modal should have focus"
,
500
);
});
it
(
"is not focused on hide"
,
function
()
{
var
view
;
view
=
new
PromptView
.
Confirmation
(
this
.
options
).
hide
();
expect
(
this
.
outFocusSpy
).
toHaveBeenCalled
();
return
waitsFor
(
function
()
{
return
view
.
$
(
'.wrapper-prompt:focus'
).
length
===
0
;
},
"The modal should not have focus"
,
500
);
});
it
(
"traps keyboard focus when moving forward"
,
function
()
{
var
view
;
view
=
new
PromptView
.
Confirmation
(
this
.
options
).
show
();
expect
(
this
.
inFocusSpy
).
toHaveBeenCalled
();
$
(
'.action-secondary'
).
first
().
simulate
(
"keydown"
,
{
keyCode
:
$
.
simulate
.
keyCode
.
TAB
}
);
return
waitsFor
(
function
()
{
return
view
.
$
(
'.action-primary:focus'
).
length
===
1
;
},
"The first action button should have focus"
,
500
);
});
it
(
"traps keyboard focus when moving backward"
,
function
()
{
var
view
;
view
=
new
PromptView
.
Confirmation
(
this
.
options
).
show
();
expect
(
this
.
inFocusSpy
).
toHaveBeenCalled
();
$
(
'.action-primary'
).
first
().
simulate
(
"keydown"
,
{
keyCode
:
$
.
simulate
.
keyCode
.
TAB
,
shiftKey
:
true
}
);
return
waitsFor
(
function
()
{
return
view
.
$
(
'.action-secondary:focus'
).
length
===
1
;
},
"The last action button should have focus"
,
500
);
});
return
it
(
"changes class on body"
,
function
()
{
return
it
(
"changes class on body"
,
function
()
{
var
view
;
var
view
;
view
=
new
PromptView
.
Confirmation
({
view
=
new
PromptView
.
Confirmation
({
...
...
common/static/js_test_requirejs.yml
View file @
f96d4b71
...
@@ -31,6 +31,7 @@ lib_paths:
...
@@ -31,6 +31,7 @@ lib_paths:
-
js/vendor/jquery.min.js
-
js/vendor/jquery.min.js
-
js/vendor/jasmine-jquery.js
-
js/vendor/jasmine-jquery.js
-
js/vendor/jasmine-imagediff.js
-
js/vendor/jasmine-imagediff.js
-
js/vendor/jquery.simulate.js
-
js/vendor/jquery.truncate.js
-
js/vendor/jquery.truncate.js
-
js/vendor/underscore-min.js
-
js/vendor/underscore-min.js
-
js/vendor/underscore.string.min.js
-
js/vendor/underscore.string.min.js
...
...
common/test/acceptance/pages/common/utils.py
View file @
f96d4b71
...
@@ -56,6 +56,10 @@ def confirm_prompt(page, cancel=False, require_notification=None):
...
@@ -56,6 +56,10 @@ def confirm_prompt(page, cancel=False, require_notification=None):
cancel is True.
cancel is True.
"""
"""
page
.
wait_for_element_visibility
(
'.prompt'
,
'Prompt is visible'
)
page
.
wait_for_element_visibility
(
'.prompt'
,
'Prompt is visible'
)
page
.
wait_for_element_visibility
(
'.wrapper-prompt:focus'
,
'Prompt is in focus'
)
confirmation_button_css
=
'.prompt .action-'
+
(
'secondary'
if
cancel
else
'primary'
)
confirmation_button_css
=
'.prompt .action-'
+
(
'secondary'
if
cancel
else
'primary'
)
page
.
wait_for_element_visibility
(
confirmation_button_css
,
'Confirmation button is visible'
)
page
.
wait_for_element_visibility
(
confirmation_button_css
,
'Confirmation button is visible'
)
require_notification
=
(
not
cancel
)
if
require_notification
is
None
else
require_notification
require_notification
=
(
not
cancel
)
if
require_notification
is
None
else
require_notification
...
...
lms/djangoapps/teams/static/teams/js/views/team_profile.js
View file @
f96d4b71
...
@@ -93,7 +93,6 @@
...
@@ -93,7 +93,6 @@
});
});
}
}
);
);
$
(
'.wrapper-prompt'
).
focus
();
}
}
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment