Commit d47d16ca authored by Robert Knight's avatar Robert Knight

Focus annotation in sidebar when clicking on "Show" button

This provides a way for screen reader users (or just keyboard-centric
users) to efficiently navigate from a highlight to the corresponding
annotation card in order to read/edit comments and replies.
parent b9b4b56c
...@@ -148,7 +148,14 @@ export class Guest { ...@@ -148,7 +148,14 @@ export class Guest {
this._adder = new Adder(this.element, { this._adder = new Adder(this.element, {
onAnnotate: () => this.createAnnotation(), onAnnotate: () => this.createAnnotation(),
onHighlight: () => this.createAnnotation({ highlight: true }), onHighlight: () => this.createAnnotation({ highlight: true }),
onShowAnnotations: tags => this.selectAnnotations(tags),
// When the "Show" button is triggered, open the sidebar and select the
// annotations. Also give keyboard focus to the first selected annotation.
// This is an important affordance for eg. screen reader users as it gives
// them an efficient way to navigate from highlights in the document to
// the corresponding comments in the sidebar.
onShowAnnotations: tags =>
this.selectAnnotations(tags, { focusInSidebar: true }),
}); });
this._selectionObserver = new SelectionObserver(range => { this._selectionObserver = new SelectionObserver(range => {
...@@ -262,7 +269,7 @@ export class Guest { ...@@ -262,7 +269,7 @@ export class Guest {
const tags = annotationsAt(/** @type {Element} */ (target)); const tags = annotationsAt(/** @type {Element} */ (target));
if (tags.length && this._highlightsVisible) { if (tags.length && this._highlightsVisible) {
const toggle = metaKey || ctrlKey; const toggle = metaKey || ctrlKey;
this.selectAnnotations(tags, toggle); this.selectAnnotations(tags, { toggle });
} }
}); });
...@@ -353,7 +360,7 @@ export class Guest { ...@@ -353,7 +360,7 @@ export class Guest {
* @param {string[]} tags * @param {string[]} tags
* @param {boolean} toggle * @param {boolean} toggle
*/ */
(tags, toggle) => this.selectAnnotations(tags, toggle) (tags, toggle) => this.selectAnnotations(tags, { toggle })
); );
this._hostRPC.on( this._hostRPC.on(
...@@ -735,17 +742,22 @@ export class Guest { ...@@ -735,17 +742,22 @@ export class Guest {
* Show the given annotations in the sidebar. * Show the given annotations in the sidebar.
* *
* This sets up a filter in the sidebar to show only the selected annotations * This sets up a filter in the sidebar to show only the selected annotations
* and opens the sidebar. * and opens the sidebar. Optionally it can also transfer keyboard focus to
* the annotation card for the first selected annotation.
* *
* @param {string[]} tags * @param {string[]} tags
* @param {boolean} [toggle] - Toggle whether the annotations are selected * @param {object} options
* instead of showing them regardless of whether they are currently selected. * @param {boolean} [options.toggle] - Toggle whether the annotations are
* selected, as opposed to just selecting them
* @param {boolean} [options.focusInSidebar] - Whether to transfer keyboard
* focus to the card for the first annotation in the selection. This
* option has no effect if {@link toggle} is true.
*/ */
selectAnnotations(tags, toggle = false) { selectAnnotations(tags, { toggle = false, focusInSidebar = false } = {}) {
if (toggle) { if (toggle) {
this._sidebarRPC.call('toggleAnnotationSelection', tags); this._sidebarRPC.call('toggleAnnotationSelection', tags);
} else { } else {
this._sidebarRPC.call('showAnnotations', tags); this._sidebarRPC.call('showAnnotations', tags, focusInSidebar);
} }
this._sidebarRPC.call('openSidebar'); this._sidebarRPC.call('openSidebar');
} }
......
...@@ -267,7 +267,7 @@ describe('Guest', () => { ...@@ -267,7 +267,7 @@ describe('Guest', () => {
emitHostEvent('selectAnnotations', tags, toggle); emitHostEvent('selectAnnotations', tags, toggle);
assert.calledWith(guest.selectAnnotations, tags, toggle); assert.calledWith(guest.selectAnnotations, tags, { toggle });
assert.calledWith(sidebarRPC().call, 'openSidebar'); assert.calledWith(sidebarRPC().call, 'openSidebar');
}); });
}); });
...@@ -805,7 +805,12 @@ describe('Guest', () => { ...@@ -805,7 +805,12 @@ describe('Guest', () => {
FakeAdder.instance.options.onShowAnnotations(tags); FakeAdder.instance.options.onShowAnnotations(tags);
assert.calledWith(sidebarRPC().call, 'openSidebar'); assert.calledWith(sidebarRPC().call, 'openSidebar');
assert.calledWith(sidebarRPC().call, 'showAnnotations', tags); assert.calledWith(
sidebarRPC().call,
'showAnnotations',
tags,
true // Focus annotation in sidebar
);
}); });
}); });
...@@ -816,14 +821,19 @@ describe('Guest', () => { ...@@ -816,14 +821,19 @@ describe('Guest', () => {
guest.selectAnnotations(tags); guest.selectAnnotations(tags);
assert.calledWith(sidebarRPC().call, 'showAnnotations', tags); assert.calledWith(
sidebarRPC().call,
'showAnnotations',
tags,
false // Don't focus annotation in sidebar
);
}); });
it('toggles the annotations if `toggle` is true', () => { it('toggles the annotations if `toggle` is true', () => {
const guest = createGuest(); const guest = createGuest();
const tags = ['t1', 't2']; const tags = ['t1', 't2'];
guest.selectAnnotations(tags, true /* toggle */); guest.selectAnnotations(tags, { toggle: true });
assert.calledWith(sidebarRPC().call, 'toggleAnnotationSelection', tags); assert.calledWith(sidebarRPC().call, 'toggleAnnotationSelection', tags);
}); });
...@@ -835,6 +845,20 @@ describe('Guest', () => { ...@@ -835,6 +845,20 @@ describe('Guest', () => {
assert.calledWith(sidebarRPC().call, 'openSidebar'); assert.calledWith(sidebarRPC().call, 'openSidebar');
}); });
it('focuses first selected annotation in sidebar if `focusInSidebar` is true', () => {
const guest = createGuest();
const tags = ['t1', 't2'];
guest.selectAnnotations(tags, { focusInSidebar: true });
assert.calledWith(
sidebarRPC().call,
'showAnnotations',
tags,
true // Focus in sidebar
);
});
}); });
describe('#getDocumentInfo', () => { describe('#getDocumentInfo', () => {
......
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