Commit 511a352b authored by Sean Hammond's avatar Sean Hammond

Merge pull request #3041 from hypothesis/focus-sidebar-for-new-annots

Focus input field after creating a new annotation
parents 0053227a dbbeadf8
...@@ -36,6 +36,13 @@ module.exports = class Host extends Guest ...@@ -36,6 +36,13 @@ module.exports = class Host extends Guest
# Show the UI # Show the UI
@frame.css('display', '') @frame.css('display', '')
this.on 'beforeAnnotationCreated', (annotation) ->
# When a new non-highlight annotation is created, focus
# the sidebar so that the text editor can be focused as
# soon as the annotation card appears
if !annotation.$highlight
app[0].contentWindow.focus()
destroy: -> destroy: ->
@frame.remove() @frame.remove()
super super
...@@ -10,8 +10,9 @@ describe 'Host', -> ...@@ -10,8 +10,9 @@ describe 'Host', ->
CrossFrame = null CrossFrame = null
fakeCrossFrame = null fakeCrossFrame = null
createHost = (options={}) -> createHost = (options={}, element=null) ->
element = document.createElement('div') if !element
element = document.createElement('div')
return new Host(element, options) return new Host(element, options)
beforeEach -> beforeEach ->
...@@ -41,6 +42,34 @@ describe 'Host', -> ...@@ -41,6 +42,34 @@ describe 'Host', ->
host.publish('panelReady') host.publish('panelReady')
assert.equal(host.frame.css('display'), '') assert.equal(host.frame.css('display'), '')
describe 'focus', ->
element = null
frame = null
host = null
beforeEach ->
element = document.createElement('div')
document.body.appendChild(element)
host = createHost({}, element)
frame = element.querySelector('[name=hyp_sidebar_frame]')
sinon.spy(frame.contentWindow, 'focus')
afterEach ->
frame.contentWindow.focus.restore()
element.parentNode.removeChild(element)
it 'focuses the sidebar when a new annotation is created', ->
host.publish('beforeAnnotationCreated', [{
$highlight: false,
}])
assert.called(frame.contentWindow.focus)
it 'does not focus the sidebar when a new highlight is created', ->
host.publish('beforeAnnotationCreated', [{
$highlight: true,
}])
assert.notCalled(frame.contentWindow.focus)
describe 'options', -> describe 'options', ->
it 'disables highlighting if showHighlights: false is given', (done) -> it 'disables highlighting if showHighlights: false is given', (done) ->
host = createHost(showHighlights: false) host = createHost(showHighlights: false)
......
...@@ -34,7 +34,7 @@ var loadMathJax = function() { ...@@ -34,7 +34,7 @@ var loadMathJax = function() {
* the markdown editor. * the markdown editor.
*/ */
// @ngInject // @ngInject
module.exports = function($filter, $sanitize, $sce, $timeout) { module.exports = function($filter, $sanitize, $sce) {
return { return {
link: function(scope, elem, attr, ctrl) { link: function(scope, elem, attr, ctrl) {
if (!(typeof ctrl !== "undefined" && ctrl !== null)) { return; } if (!(typeof ctrl !== "undefined" && ctrl !== null)) { return; }
...@@ -63,6 +63,16 @@ module.exports = function($filter, $sanitize, $sce, $timeout) { ...@@ -63,6 +63,16 @@ module.exports = function($filter, $sanitize, $sce, $timeout) {
input.focus(); input.focus();
} }
function focusInput() {
// When the visibility of the editor changes, focus it.
// A timeout is used so that focus() is not called until
// the visibility change has been applied (by adding or removing
// the relevant CSS classes)
setTimeout(function () {
input.focus();
}, 0);
}
scope.insertBold = function() { scope.insertBold = function() {
updateState(function (state) { updateState(function (state) {
return commands.toggleSpanStyle(state, '**', '**', 'Bold'); return commands.toggleSpanStyle(state, '**', '**', 'Bold');
...@@ -145,7 +155,7 @@ module.exports = function($filter, $sanitize, $sce, $timeout) { ...@@ -145,7 +155,7 @@ module.exports = function($filter, $sanitize, $sce, $timeout) {
return ctrl.$render(); return ctrl.$render();
} else { } else {
input.style.height = output.style.height; input.style.height = output.style.height;
return $timeout(function() { return input.focus(); }); focusInput();
} }
} }
}; };
...@@ -265,12 +275,12 @@ module.exports = function($filter, $sanitize, $sce, $timeout) { ...@@ -265,12 +275,12 @@ module.exports = function($filter, $sanitize, $sce, $timeout) {
// Reset height of output div in case it has been changed. // Reset height of output div in case it has been changed.
// Re-render when it becomes uneditable. // Re-render when it becomes uneditable.
// Auto-focus the input box when the widget becomes editable. // Auto-focus the input box when the widget becomes editable.
return scope.$watch('readOnly', function(readOnly) { scope.$watch('readOnly', function(readOnly) {
scope.preview = false; scope.preview = false;
output.style.height = ""; output.style.height = "";
ctrl.$render(); ctrl.$render();
if (!readOnly) { if (!readOnly) {
input.focus(); focusInput();
} }
}); });
}, },
......
...@@ -229,7 +229,10 @@ module.exports = [ ...@@ -229,7 +229,10 @@ module.exports = [
ctrl.container = thread ctrl.container = thread
if ctrl.isNew() if ctrl.isNew()
# Scroll the sidebar to show new annotations. # Scroll the sidebar to show new annotations.
$location.hash(ctrl.id) # Note that only top level annotation cards have their ID set
annotationCard = document.getElementById(ctrl.id)
if annotationCard
annotationCard.scrollIntoView();
scope.$digest() scope.$digest()
controller: ThreadController controller: ThreadController
......
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