Commit a37b3476 authored by Robert Knight's avatar Robert Knight

Convert `Host` class to JS

This is a straightforward conversion with decaffeinate, followed by
manual fixups of lint errors etc.
parent 0b9a114f
$ = require('jquery')
Guest = require('./guest')
module.exports = class Host extends Guest
constructor: (element, config) ->
# Some config settings are not JSON-stringifiable (e.g. JavaScript
# functions) and will be omitted when the config is JSON-stringified.
# Add a JSON-stringifiable option for each of these so that the sidebar can
# at least know whether the callback functions were provided or not.
if config.services?[0]
service = config.services[0]
if service.onLoginRequest
service.onLoginRequestProvided = true
if service.onLogoutRequest
service.onLogoutRequestProvided = true
if service.onSignupRequest
service.onSignupRequestProvided = true
if service.onProfileRequest
service.onProfileRequestProvided = true
if service.onHelpRequest
service.onHelpRequestProvided = true
# Make a copy of all config settings except `config.sidebarAppUrl`, the app base URL,
# and `config.pluginClasses`
configParam = 'config=' + encodeURIComponent(
JSON.stringify(Object.assign({}, config, {sidebarAppUrl: undefined, pluginClasses: undefined }))
)
sidebarAppSrc = config.sidebarAppUrl + '#' + configParam
# Create the iframe
app = $('<iframe></iframe>')
.attr('name', 'hyp_sidebar_frame')
# enable media in annotations to be shown fullscreen
.attr('allowfullscreen', '')
.attr('seamless', '')
.attr('src', sidebarAppSrc)
.attr('title', 'Hypothesis annotation viewer')
.addClass('h-sidebar-iframe')
externalContainer = null
if config.externalContainerSelector
# Use the native method to also validate the input
externalContainer = document.querySelector(config.externalContainerSelector)
if externalContainer
@externalFrame = $(externalContainer)
else
@frame = $('<div></div>')
.css('display', 'none')
.addClass('annotator-frame annotator-outer')
if config.theme == 'clean'
@frame.addClass('annotator-frame--drop-shadow-enabled')
@frame.appendTo(element)
super
app.appendTo(@frame || @externalFrame)
this.on 'panelReady', =>
# Show the UI
@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: ->
@frame?.remove()
super
import $ from 'jquery';
// TODO - Convert this to an ES import once the `Guest` class is converted to JS.
// @ts-expect-error
const Guest = require('./guest');
export default class Host extends Guest {
constructor(element, config) {
// Some config settings are not JSON-stringifiable (e.g. JavaScript
// functions) and will be omitted when the config is JSON-stringified.
// Add a JSON-stringifiable option for each of these so that the sidebar can
// at least know whether the callback functions were provided or not.
if (config.services?.length > 0) {
const service = config.services[0];
if (service.onLoginRequest) {
service.onLoginRequestProvided = true;
}
if (service.onLogoutRequest) {
service.onLogoutRequestProvided = true;
}
if (service.onSignupRequest) {
service.onSignupRequestProvided = true;
}
if (service.onProfileRequest) {
service.onProfileRequestProvided = true;
}
if (service.onHelpRequest) {
service.onHelpRequestProvided = true;
}
}
// Make a copy of all config settings except `config.sidebarAppUrl`, the app base URL,
// and `config.pluginClasses`
const configParam =
'config=' +
encodeURIComponent(
JSON.stringify(
Object.assign({}, config, {
sidebarAppUrl: undefined,
pluginClasses: undefined,
})
)
);
const sidebarAppSrc = config.sidebarAppUrl + '#' + configParam;
// Create the iframe
const app = $('<iframe></iframe>')
.attr('name', 'hyp_sidebar_frame')
// enable media in annotations to be shown fullscreen
.attr('allowfullscreen', '')
.attr('seamless', '')
.attr('src', sidebarAppSrc)
.attr('title', 'Hypothesis annotation viewer')
.addClass('h-sidebar-iframe');
let externalContainer = null;
if (config.externalContainerSelector) {
// Use the native method to also validate the input
externalContainer = document.querySelector(
config.externalContainerSelector
);
}
super(...arguments);
if (externalContainer) {
this.externalFrame = $(externalContainer);
} else {
this.frame = $('<div></div>')
.css('display', 'none')
.addClass('annotator-frame annotator-outer');
if (config.theme === 'clean') {
this.frame.addClass('annotator-frame--drop-shadow-enabled');
}
this.frame.appendTo(element);
}
app.appendTo(this.frame || this.externalFrame);
this.on('panelReady', () => {
// Show the UI
this.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() {
this.frame?.remove();
super.destroy();
}
}
......@@ -5,6 +5,7 @@ import sidebarTrigger from './sidebar-trigger';
import events from '../shared/bridge-events';
import features from './features';
import Host from './host';
import { ToolbarController } from './toolbar';
/**
......@@ -17,10 +18,6 @@ import { ToolbarController } from './toolbar';
// Minimum width to which the frame can be resized.
const MIN_RESIZE = 280;
// TODO - Convert this to an ES import once `host.coffee` is converted.
// @ts-expect-error
const Host = require('./host');
const defaultConfig = {
Document: {},
TextSelection: {},
......
Host = require('../host')
describe 'Host', ->
sandbox = sinon.createSandbox()
CrossFrame = null
fakeCrossFrame = null
hostConfig = {pluginClasses: {}}
createHost = (config={}, element=null) ->
config = Object.assign({sidebarAppUrl: '/base/annotator/test/empty.html'}, hostConfig, config)
if !element
element = document.createElement('div')
return new Host(element, config)
beforeEach ->
# Disable any Host logging.
sandbox.stub(console, 'log')
fakeCrossFrame = {}
fakeCrossFrame.onConnect = sandbox.stub().returns(fakeCrossFrame)
fakeCrossFrame.on = sandbox.stub().returns(fakeCrossFrame)
fakeCrossFrame.call = sandbox.spy()
CrossFrame = sandbox.stub()
CrossFrame.returns(fakeCrossFrame)
hostConfig.pluginClasses['CrossFrame'] = CrossFrame
afterEach ->
sandbox.restore()
describe 'widget visibility', ->
it 'starts hidden', ->
host = createHost()
assert.equal(host.frame.css('display'), 'none')
it 'becomes visible when the "panelReady" event fires', ->
host = createHost()
host.publish('panelReady')
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 'config', ->
it 'disables highlighting if showHighlights: false is given', (done) ->
host = createHost(showHighlights: false)
host.on 'panelReady', ->
assert.isFalse(host.visibleHighlights)
done()
host.publish('panelReady')
it 'passes config to the sidebar iframe', ->
appURL = new URL('/base/annotator/test/empty.html', window.location.href)
host = createHost({annotations: '1234'})
configStr = encodeURIComponent(JSON.stringify({annotations: '1234'}))
assert.equal(host.frame[0].children[0].src, appURL + '#config=' + configStr)
it 'adds drop shadow if the clean theme is enabled', ->
host = createHost({theme: 'clean'})
assert.isTrue(host.frame.hasClass('annotator-frame--drop-shadow-enabled'))
import Host from '../host';
describe('Host', () => {
const sandbox = sinon.createSandbox();
const hostConfig = { pluginClasses: {} };
let CrossFrame;
let fakeCrossFrame;
const createHost = (config = {}, element = null) => {
config = Object.assign(
{ sidebarAppUrl: '/base/annotator/test/empty.html' },
hostConfig,
config
);
if (!element) {
element = document.createElement('div');
}
return new Host(element, config);
};
beforeEach(() => {
// Disable any Host logging.
sandbox.stub(console, 'log');
fakeCrossFrame = {};
fakeCrossFrame.onConnect = sandbox.stub().returns(fakeCrossFrame);
fakeCrossFrame.on = sandbox.stub().returns(fakeCrossFrame);
fakeCrossFrame.call = sandbox.spy();
CrossFrame = sandbox.stub();
CrossFrame.returns(fakeCrossFrame);
hostConfig.pluginClasses.CrossFrame = CrossFrame;
});
afterEach(() => {
sandbox.restore();
});
describe('widget visibility', () => {
it('starts hidden', () => {
const host = createHost();
assert.equal(host.frame.css('display'), 'none');
});
it('becomes visible when the "panelReady" event fires', () => {
const host = createHost();
host.publish('panelReady');
assert.equal(host.frame.css('display'), '');
});
});
describe('focus', () => {
let element;
let frame;
let host;
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('config', () => {
it('disables highlighting if showHighlights: false is given', done => {
const host = createHost({ showHighlights: false });
host.on('panelReady', () => {
assert.isFalse(host.visibleHighlights);
done();
});
host.publish('panelReady');
});
it('passes config to the sidebar iframe', () => {
const appURL = new URL(
'/base/annotator/test/empty.html',
window.location.href
);
const host = createHost({ annotations: '1234' });
const configStr = encodeURIComponent(
JSON.stringify({ annotations: '1234' })
);
assert.equal(
host.frame[0].children[0].src,
appURL + '#config=' + configStr
);
});
it('adds drop shadow if the clean theme is enabled', () => {
const host = createHost({ theme: 'clean' });
assert.isTrue(
host.frame.hasClass('annotator-frame--drop-shadow-enabled')
);
});
});
});
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