Commit 35b6f8d8 authored by Robert Knight's avatar Robert Knight

Send "scrolltorange" event when clicking bucket bar up/down arrows

Allow the host page to respond when a highlight is scrolled into view by
clicking the up/down-pointing buttons at the top/bottom of the bucket bar, in
the same way the host page can respond when clicking on an annotation card.
parent 327347ab
......@@ -458,6 +458,31 @@ export class Guest extends TinyEmitter implements Annotator, Destroyable {
this._hostRPC.connect(hostPort);
}
/**
* Scroll an anchor into view and notify the host page.
*
* Returns a promise that resolves when scrolling has completed. See
* {@link Integration.scrollToAnchor}.
*/
private async _scrollToAnchor(anchor: Anchor) {
const range = resolveAnchor(anchor);
if (!range) {
return;
}
// Emit a custom event that the host page can respond to. This is useful
// if the content is in a hidden section of the page that needs to be
// revealed before it can be scrolled to.
const event = new ScrollToRangeEvent(range);
const defaultNotPrevented = this.element.dispatchEvent(event);
if (defaultNotPrevented) {
await event.ready;
await this._integration.scrollToAnchor(anchor);
}
}
async _connectSidebar() {
this._sidebarRPC.on(
'featureFlagsUpdated',
......@@ -475,22 +500,7 @@ export class Guest extends TinyEmitter implements Annotator, Destroyable {
if (!anchor?.highlights) {
return;
}
const range = resolveAnchor(anchor);
if (!range) {
return;
}
// Emit a custom event that the host page can respond to. This is useful
// if the content is in a hidden section of the page that needs to be
// revealed before it can be scrolled to.
const event = new ScrollToRangeEvent(range);
const defaultNotPrevented = this.element.dispatchEvent(event);
if (defaultNotPrevented) {
const ready = Promise.resolve(event.ready);
ready.then(() => this._integration.scrollToAnchor(anchor));
}
this._scrollToAnchor(anchor);
});
// Handler for controls on the sidebar
......@@ -735,13 +745,16 @@ export class Guest extends TinyEmitter implements Annotator, Destroyable {
/**
* Scroll to the closest off screen anchor.
*/
_scrollToClosestOffScreenAnchor(tags: string[], direction: 'down' | 'up') {
private async _scrollToClosestOffScreenAnchor(
tags: string[],
direction: 'down' | 'up'
) {
const anchors = this.anchors.filter(({ annotation }) =>
tags.includes(annotation.$tag)
);
const closest = findClosestOffscreenAnchor(anchors, direction);
if (closest) {
this._integration.scrollToAnchor(closest);
await this._scrollToAnchor(closest);
}
}
......
......@@ -68,12 +68,14 @@ describe('Guest', () => {
return fakePortRPCs[1];
};
// Simulate event from host frame.
//
// Returns the result of the guest's event handler. That result would normally
// not be used, but is useful as a way for the guest to indicate to tests
// when async handling is done, by returning a promise.
const emitHostEvent = (event, ...args) => {
for (let [evt, fn] of hostRPC().on.args) {
if (event === evt) {
fn(...args);
}
}
const [, callback] = hostRPC().on.args.find(args => args[0] === event);
return callback?.(...args);
};
const emitSidebarEvent = (event, ...args) => {
......@@ -274,25 +276,24 @@ describe('Guest', () => {
});
describe('on "scrollToClosestOffScreenAnchor" event', () => {
it('scrolls to the nearest off-screen anchor"', () => {
it('scrolls to the nearest off-screen anchor"', async () => {
const guest = createGuest();
guest.anchors = [
{ annotation: { $tag: 't1' } },
{ annotation: { $tag: 't2' } },
{ annotation: { $tag: 't1' }, range: new FakeTextRange({}) },
{ annotation: { $tag: 't2' }, range: new FakeTextRange({}) },
];
const anchor = {};
fakeFindClosestOffscreenAnchor.returns(anchor);
fakeFindClosestOffscreenAnchor.returns(guest.anchors[0]);
const tags = ['t1', 't2'];
const direction = 'down';
emitHostEvent('scrollToClosestOffScreenAnchor', tags, direction);
await emitHostEvent('scrollToClosestOffScreenAnchor', tags, direction);
assert.calledWith(
fakeFindClosestOffscreenAnchor,
guest.anchors,
direction
);
assert.calledWith(fakeIntegration.scrollToAnchor, anchor);
assert.calledWith(fakeIntegration.scrollToAnchor, guest.anchors[0]);
});
});
......
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