Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/annotator/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { urlFromLinkTag } from './url-from-link-tag';
*/
function configurationKeys(appContext) {
const contexts = {
annotator: ['clientUrl', 'showHighlights', 'subFrameIdentifier'],
annotator: ['clientUrl', 'subFrameIdentifier'],
sidebar: [
'appType',
'annotations',
Expand Down
4 changes: 2 additions & 2 deletions src/annotator/config/test/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ describe('annotator/config/index', () => {
[
{
app: 'annotator',
expectedKeys: ['clientUrl', 'showHighlights', 'subFrameIdentifier'],
expectedKeys: ['clientUrl', 'subFrameIdentifier'],
},
{
app: 'sidebar',
Expand Down Expand Up @@ -252,7 +252,7 @@ describe('annotator/config/index', () => {
],
},
].forEach(test => {
it(`ignore values not belonging to "${test.app}" context`, () => {
it(`ignores values not belonging to "${test.app}" context`, () => {
const config = getConfig(test.app, 'WINDOW');
assert.deepEqual(Object.keys(config), test.expectedKeys);
});
Expand Down
25 changes: 10 additions & 15 deletions src/annotator/guest.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export default class Guest {
constructor(element, eventBus, config = {}, hostFrame = window) {
this.element = element;
this._emitter = eventBus.createEmitter();
this._visibleHighlights = false;
this._highlightsVisible = false;
this._isAdderVisible = false;

this._adder = new Adder(this.element, {
Expand All @@ -133,7 +133,6 @@ export default class Guest {
/** @type {Selection} */ (document.getSelection()).removeAllRanges();
},
onHighlight: async () => {
this.setVisibleHighlights(true);
await this.createAnnotation({ highlight: true });
/** @type {Selection} */ (document.getSelection()).removeAllRanges();
},
Expand Down Expand Up @@ -171,9 +170,6 @@ export default class Guest {
* @type {Bridge<GuestToSidebarEvent,SidebarToGuestEvent>}
*/
this._bridge = new Bridge();
this._bridge.onConnect(() => {
this.setVisibleHighlights(config.showHighlights === 'always');
});
this._connectSidebarEvents();

// Set up listeners for when the sidebar asks us to add or remove annotations
Expand Down Expand Up @@ -232,7 +228,7 @@ export default class Guest {
this._listeners.add(this.element, 'mouseup', event => {
const { target, metaKey, ctrlKey } = /** @type {MouseEvent} */ (event);
const annotations = annotationsAt(/** @type {Element} */ (target));
if (annotations.length && this._visibleHighlights) {
if (annotations.length && this._highlightsVisible) {
const toggle = metaKey || ctrlKey;
this.selectAnnotations(annotations, toggle);
}
Expand All @@ -250,13 +246,13 @@ export default class Guest {

this._listeners.add(this.element, 'mouseover', ({ target }) => {
const annotations = annotationsAt(/** @type {Element} */ (target));
if (annotations.length && this._visibleHighlights) {
if (annotations.length && this._highlightsVisible) {
this._focusAnnotations(annotations);
}
});

this._listeners.add(this.element, 'mouseout', () => {
if (this._visibleHighlights) {
if (this._highlightsVisible) {
this._focusAnnotations([]);
}
});
Expand Down Expand Up @@ -360,8 +356,8 @@ export default class Guest {
});

// Handler for controls on the sidebar
this._bridge.on('setVisibleHighlights', showHighlights => {
this.setVisibleHighlights(showHighlights);
this._bridge.on('setHighlightsVisible', showHighlights => {
this.setHighlightsVisible(showHighlights);
});
}

Expand Down Expand Up @@ -663,12 +659,11 @@ export default class Guest {
/**
* Set whether highlights are visible in the document or not.
*
* @param {boolean} shouldShowHighlights
* @param {boolean} visible
*/
setVisibleHighlights(shouldShowHighlights) {
setHighlightsVisible(this.element, shouldShowHighlights);
this._visibleHighlights = shouldShowHighlights;
this._emitter.publish('highlightsVisibleChanged', shouldShowHighlights);
setHighlightsVisible(visible) {
setHighlightsVisible(this.element, visible);
this._highlightsVisible = visible;
}

/**
Expand Down
29 changes: 19 additions & 10 deletions src/annotator/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export default class Sidebar {
this.toolbar = new ToolbarController(toolbarContainer, {
createAnnotation: () => guest.createAnnotation(),
setSidebarOpen: open => (open ? this.open() : this.close()),
setHighlightsVisible: show => this.setAllVisibleHighlights(show),
setHighlightsVisible: show => this.setHighlightsVisible(show),
});

if (config.theme === 'clean') {
Expand All @@ -130,9 +130,6 @@ export default class Sidebar {
this.toolbar.useMinimalControls = false;
}

this._emitter.subscribe('highlightsVisibleChanged', visible => {
this.toolbar.highlightsVisible = visible;
});
this._emitter.subscribe('hasSelectionChanged', hasSelection => {
this.toolbar.newAnnotationType = hasSelection ? 'annotation' : 'note';
});
Expand Down Expand Up @@ -197,6 +194,12 @@ export default class Sidebar {
this.iframeContainer.style.display = '';
}

// Set initial highlight visibility. We do this only once the sidebar app
// is ready because `setHighlightsVisible` needs to reflect this state to
// the sidebar app.
const showHighlights = config.showHighlights === 'always';
this.setHighlightsVisible(showHighlights);

if (
config.openSidebar ||
config.annotations ||
Expand Down Expand Up @@ -236,6 +239,9 @@ export default class Sidebar {
sidebarTrigger(document.body, () => this.open());
features.init(this._sidebarRPC);

this._sidebarRPC.on('showHighlights', () =>
Copy link
Contributor

@esanzgar esanzgar Oct 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

showHighlights is a new event type that is sent from the sidebar to the host frame.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. These lines reported errors as expected after a rebase and I've updated the types.

this.setHighlightsVisible(true)
);
this._sidebarRPC.on('openSidebar', () => this.open());
this._sidebarRPC.on('closeSidebar', () => this.close());

Expand Down Expand Up @@ -449,7 +455,7 @@ export default class Sidebar {
this.toolbar.sidebarOpen = true;

if (this.options.showHighlights === 'whenSidebarOpen') {
this.guest.setVisibleHighlights(true);
this.setHighlightsVisible(true);
}

this._notifyOfLayoutChange(true);
Expand All @@ -464,19 +470,22 @@ export default class Sidebar {
this.toolbar.sidebarOpen = false;

if (this.options.showHighlights === 'whenSidebarOpen') {
this.guest.setVisibleHighlights(false);
this.setHighlightsVisible(false);
}

this._notifyOfLayoutChange(false);
}

/**
* Hide or show highlights associated with annotations in the document.
* Set whether highlights are visible in guest frames.
*
* @param {boolean} shouldShowHighlights
* @param {boolean} visible
*/
setAllVisibleHighlights(shouldShowHighlights) {
this._sidebarRPC.call('setVisibleHighlights', shouldShowHighlights);
setHighlightsVisible(visible) {
this.toolbar.highlightsVisible = visible;

// Notify sidebar app of change which will in turn reflect state to guest frames.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Notify sidebar app of change which will in turn reflect state to guest frames.
// Notify sidebar frame of change which will in turn reflect state to guest frames.

this._sidebarRPC.call('setHighlightsVisible', visible);
}

/**
Expand Down
11 changes: 5 additions & 6 deletions src/annotator/test/guest-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ describe('Guest', () => {
createChannel: sinon.stub(),
destroy: sinon.stub(),
on: sinon.stub(),
onConnect: sinon.stub(),
};
FakeBridge = sinon.stub().returns(fakeBridge);

Expand Down Expand Up @@ -406,18 +405,18 @@ describe('Guest', () => {
});
});

describe('on "setVisibleHighlights" event', () => {
describe('on "setHighlightsVisible" event', () => {
it('sets visibility of highlights in document', () => {
const guest = createGuest();

emitSidebarEvent('setVisibleHighlights', true);
emitSidebarEvent('setHighlightsVisible', true);
assert.calledWith(
highlighter.setHighlightsVisible,
guest.element,
true
);

emitSidebarEvent('setVisibleHighlights', false);
emitSidebarEvent('setHighlightsVisible', false);
assert.calledWith(
highlighter.setHighlightsVisible,
guest.element,
Expand All @@ -436,7 +435,7 @@ describe('Guest', () => {
beforeEach(() => {
fakeSidebarFrame = null;
guest = createGuest();
guest.setVisibleHighlights(true);
guest.setHighlightsVisible(true);
rootElement = guest.element;

// Create a fake highlight as a target for hover and click events.
Expand Down Expand Up @@ -519,7 +518,7 @@ describe('Guest', () => {
});

it('does not focus or select annotations in the sidebar if highlights are hidden', () => {
guest.setVisibleHighlights(false);
guest.setHighlightsVisible(false);

fakeHighlight.dispatchEvent(new Event('mouseover', { bubbles: true }));
fakeHighlight.dispatchEvent(new Event('mouseup', { bubbles: true }));
Expand Down
46 changes: 34 additions & 12 deletions src/annotator/test/sidebar-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ describe('Sidebar', () => {
this.contentContainer = sinon.stub().returns(document.body);
this.createAnnotation = sinon.stub();
this.fitSideBySide = sinon.stub();
this.setVisibleHighlights = sinon.stub();
this.setHighlightsVisible = sinon.stub();
}
}
fakeGuest = new FakeGuest();
Expand Down Expand Up @@ -244,14 +244,14 @@ describe('Sidebar', () => {

it('shows or hides highlights when toolbar button is clicked', () => {
const sidebar = createSidebar();
sinon.stub(sidebar, 'setAllVisibleHighlights');
sinon.stub(sidebar, 'setHighlightsVisible');

FakeToolbarController.args[0][1].setHighlightsVisible(true);
assert.calledWith(sidebar.setAllVisibleHighlights, true);
sidebar.setAllVisibleHighlights.resetHistory();
assert.calledWith(sidebar.setHighlightsVisible, true);
sidebar.setHighlightsVisible.resetHistory();

FakeToolbarController.args[0][1].setHighlightsVisible(false);
assert.calledWith(sidebar.setAllVisibleHighlights, false);
assert.calledWith(sidebar.setHighlightsVisible, false);
});

it('creates an annotation when toolbar button is clicked', () => {
Expand Down Expand Up @@ -294,6 +294,15 @@ describe('Sidebar', () => {
return result;
};

describe('on "showHighlights" event', () => {
it('makes all highlights visible', () => {
createSidebar();
assert.isFalse(fakeToolbar.highlightsVisible);
emitEvent('showHighlights');
assert.isTrue(fakeToolbar.highlightsVisible);
});
});

describe('on "open" event', () =>
it('opens the frame', () => {
const target = sandbox.stub(Sidebar.prototype, 'open');
Expand Down Expand Up @@ -562,13 +571,13 @@ describe('Sidebar', () => {
it('shows highlights if "showHighlights" is set to "whenSidebarOpen"', () => {
const sidebar = createSidebar({ showHighlights: 'whenSidebarOpen' });
sidebar.open();
assert.calledWith(sidebar.guest.setVisibleHighlights, true);
assert.calledWith(fakeBridge.call, 'setHighlightsVisible', true);
});

it('does not show highlights otherwise', () => {
const sidebar = createSidebar({ showHighlights: 'never' });
sidebar.open();
assert.notCalled(sidebar.guest.setVisibleHighlights);
assert.neverCalledWith(fakeBridge.call, 'setHighlightsVisible');
});

it('updates the `sidebarOpen` property of the toolbar', () => {
Expand All @@ -585,7 +594,7 @@ describe('Sidebar', () => {
sidebar.open();
sidebar.close();

assert.calledWith(sidebar.guest.setVisibleHighlights, false);
assert.calledWith(fakeBridge.call, 'setHighlightsVisible', false);
});

it('updates the `sidebarOpen` property of the toolbar', () => {
Expand All @@ -598,12 +607,25 @@ describe('Sidebar', () => {
});
});

describe('#setAllVisibleHighlights', () =>
describe('#setHighlightsVisible', () => {
it('requests sidebar to set highlight visibility in guest frames', () => {
const sidebar = createSidebar();
sidebar.setAllVisibleHighlights(true);
assert.calledWith(fakeBridge.call, 'setVisibleHighlights', true);
}));
sidebar.setHighlightsVisible(true);
assert.calledWith(fakeBridge.call, 'setHighlightsVisible', true);

sidebar.setHighlightsVisible(false);
assert.calledWith(fakeBridge.call, 'setHighlightsVisible', false);
});

it('toggles "Show highlights" control in toolbar', () => {
const sidebar = createSidebar();
sidebar.setHighlightsVisible(true);
assert.isTrue(fakeToolbar.highlightsVisible);

sidebar.setHighlightsVisible(false);
assert.isFalse(fakeToolbar.highlightsVisible);
});
});

it('hides toolbar controls when using the "clean" theme', () => {
createSidebar({ theme: 'clean' });
Expand Down
16 changes: 14 additions & 2 deletions src/sidebar/services/frame-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ export class FrameSyncService {
// Set of tags of annotations that are currently loaded into the frame
const inFrame = new Set();

/** Whether highlights are visible in guest frames. */
this._highlightsVisible = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A single state for all the guest frames.


/**
* Watch for changes to the set of annotations displayed in the sidebar and
* notify connected guests about new/updated/deleted annotations.
Expand Down Expand Up @@ -164,6 +167,11 @@ export class FrameSyncService {
this._hostRPC.call('openSidebar');
}

// Ensure that the highlight for the newly-created annotation is visible.
// Currently we only support a single, shared visibility state for all highlights
// in all frames, so this will make all existing highlights visible too.
this._hostRPC.call('showHighlights');
Copy link
Contributor Author

@robertknight robertknight Oct 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To recap the sequence of events here when creating a new highlight while the "Show highlights" control is disabled:

  1. The guest will create a new annotation and send it to the sidebar via a beforeCreateAnnotation request
  2. The sidebar sends a showHighlights request to the host frame
  3. The host frame updates the "Show highlights" control and then sends a setHighlightsVisible request back to the sidebar app
  4. The sidebar app will relay the setHighlightsVisible request to all connected guests, so that the highlight visibility is the same in all of them
  5. The guests handle the setHighlightsVisible request and show or hide highlights in their respective frames

This might seem rather roundabout, but steps 3-5 are the same as when toggling the "Show highlights" control manually, so we avoid having multiple code paths to handle keeping the state in sync across all frames.

In future if we have a direct guest <-> host channel for each guest, we might be able to eliminate the relaying of the setHighlightsVisible message through the sidebar app. CC @esanzgar.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have been thinking about guest-host channel too, while working on #3838 and realising about the relaying of certain messages host -> sidebar -> guest.

I would support the creation of guest-host channel.


// Create the new annotation in the sidebar.
annotationsService.create(annot);
});
Expand Down Expand Up @@ -226,6 +234,9 @@ export class FrameSyncService {
* @param {PortRPC} channel
*/
const addFrame = channel => {
// Synchronize highlight visibility in this guest with the sidebar's controls.
channel.call('setHighlightsVisible', this._highlightsVisible);

channel.call('getDocumentInfo', (err, info) => {
if (err) {
channel.destroy();
Expand Down Expand Up @@ -279,8 +290,9 @@ export class FrameSyncService {

// When user toggles the highlight visibility control in the sidebar container,
// update the visibility in all the guest frames.
this._hostRPC.on('setVisibleHighlights', state => {
this._guestRPC.call('setVisibleHighlights', state);
this._hostRPC.on('setHighlightsVisible', visible => {
this._highlightsVisible = visible;
this._guestRPC.call('setHighlightsVisible', visible);
});

// Create channel for sidebar <-> host communication and send port to host.
Expand Down
Loading