Commit 0cdccd07 authored by Robert Knight's avatar Robert Knight Committed by GitHub

Merge pull request #499 from hypothesis/on-layout-change-support

Adding support for onLayoutChange notifier configuration
parents 2cb95beb 0f2f8b09
......@@ -10,29 +10,26 @@ var settingsFrom = require('./settings');
function configFrom(window_) {
var settings = settingsFrom(window_);
return {
annotations: settings.annotations,
// URL where client assets are served from. Used when injecting the client
// into child iframes.
assetRoot: settings.hostPageSetting('assetRoot', {allowInBrowserExt: true}),
branding: settings.hostPageSetting('branding'),
// URL of the client's boot script. Used when injecting the client into
// child iframes.
clientUrl: settings.clientUrl,
annotations: settings.annotations,
branding: settings.hostPageSetting('branding'),
// Temporary feature flag override for 1st-party OAuth
oauthEnabled: settings.hostPageSetting('oauthEnabled'),
onLayoutChange: settings.hostPageSetting('onLayoutChange'),
openLoginForm: settings.hostPageSetting('openLoginForm', {allowInBrowserExt: true}),
openSidebar: settings.hostPageSetting('openSidebar', {allowInBrowserExt: true}),
query: settings.query,
services: settings.hostPageSetting('services'),
showHighlights: settings.showHighlights,
sidebarAppUrl: settings.sidebarAppUrl,
// Subframe identifier given when a frame is being embedded into
// by a top level client
subFrameIdentifier: settings.hostPageSetting('subFrameIdentifier', {allowInBrowserExt: true}),
openLoginForm: settings.hostPageSetting('openLoginForm', {allowInBrowserExt: true}),
openSidebar: settings.hostPageSetting('openSidebar', {allowInBrowserExt: true}),
query: settings.query,
// Temporary feature flag override for 1st-party OAuth
oauthEnabled: settings.hostPageSetting('oauthEnabled'),
};
}
......
......@@ -5,8 +5,8 @@ Hammer = require('hammerjs')
Host = require('./host')
annotationCounts = require('./annotation-counts')
sidebarTrigger = require('./sidebar-trigger')
events = require('../shared/bridge-events');
features = require('./features');
events = require('../shared/bridge-events')
features = require('./features')
# Minimum width to which the frame can be resized.
MIN_RESIZE = 280
......@@ -35,6 +35,7 @@ module.exports = class Sidebar extends Host
@plugins.BucketBar.element.on 'click', (event) => this.show()
if @plugins.Toolbar?
@toolbarWidth = parseInt(window.getComputedStyle(this.plugins.Toolbar.toolbar[0]).width)
this._setupGestures()
# The partner-provided callback functions.
......@@ -46,6 +47,11 @@ module.exports = class Sidebar extends Host
@onProfileRequest = serviceConfig.onProfileRequest
@onHelpRequest = serviceConfig.onHelpRequest
@onLayoutChange = config.onLayoutChange
# initial layout notification
this._notifyOfLayoutChange(false)
this._setupSidebarEvents()
_setupSidebarEvents: ->
......@@ -58,23 +64,23 @@ module.exports = class Sidebar extends Host
@crossframe.on(events.LOGIN_REQUESTED, =>
if @onLoginRequest
@onLoginRequest()
);
)
@crossframe.on(events.LOGOUT_REQUESTED, =>
if @onLogoutRequest
@onLogoutRequest()
);
)
@crossframe.on(events.SIGNUP_REQUESTED, =>
if @onSignupRequest
@onSignupRequest()
);
)
@crossframe.on(events.PROFILE_REQUESTED, =>
if @onProfileRequest
@onProfileRequest()
);
)
@crossframe.on(events.HELP_REQUESTED, =>
if @onHelpRequest
@onHelpRequest()
);
)
# Return this for chaining
this
......@@ -120,6 +126,62 @@ module.exports = class Sidebar extends Host
w = -m
@frame.css('margin-left', "#{m}px")
if w >= MIN_RESIZE then @frame.css('width', "#{w}px")
this._notifyOfLayoutChange()
###*
# Notify integrator when sidebar layout changes via `onLayoutChange` callback.
#
# @param [boolean] explicitExpandedState - `true` or `false` if the sidebar
# is being directly opened or closed, as opposed to being resized via
# the sidebar's drag handles.
###
_notifyOfLayoutChange: (explicitExpandedState) =>
toolbarWidth = @toolbarWidth || 0
# The sidebar structure is:
#
# [ Toolbar ][ ]
# [ ---------- ][ Sidebar iframe container (@frame) ]
# [ Bucket Bar ][ ]
#
# The sidebar iframe is hidden or shown by adjusting the left margin of its
# container.
if @onLayoutChange
rect = @frame[0].getBoundingClientRect()
computedStyle = window.getComputedStyle(@frame[0])
width = parseInt(computedStyle.width)
leftMargin = parseInt(computedStyle.marginLeft)
# The width of the sidebar that is visible on screen, including the
# toolbar, which is always visible.
frameVisibleWidth = toolbarWidth
if explicitExpandedState?
# When we are explicitly saying to open or close, jump
# straight to the upper and lower bounding widths.
if explicitExpandedState
frameVisibleWidth += width
else
if leftMargin < MIN_RESIZE
# When the width hits its threshold of MIN_RESIZE,
# the left margin continues to push the sidebar off screen.
# So it's the best indicator of width when we get below that threshold.
# Note: when we hit the right edge, it will be -0
frameVisibleWidth += -leftMargin
else
frameVisibleWidth += width
# Since we have added logic on if this is an explicit show/hide
# and applied proper width to the visible value above, we can infer
# expanded state on that width value vs the lower bound
expanded = frameVisibleWidth > toolbarWidth
@onLayoutChange({
expanded: expanded,
width: if expanded then frameVisibleWidth else toolbarWidth,
height: rect.height,
})
onPan: (event) =>
switch event.type
......@@ -176,6 +238,8 @@ module.exports = class Sidebar extends Host
if @options.showHighlights == 'whenSidebarOpen'
@setVisibleHighlights(true)
this._notifyOfLayoutChange(true)
hide: ->
@frame.css 'margin-left': ''
@frame.addClass 'annotator-collapsed'
......@@ -188,6 +252,8 @@ module.exports = class Sidebar extends Host
if @options.showHighlights == 'whenSidebarOpen'
@setVisibleHighlights(false)
this._notifyOfLayoutChange(false)
isOpen: ->
!@frame.hasClass('annotator-collapsed')
......
events = require('../../shared/bridge-events')
proxyquire = require('proxyquire')
Sidebar = proxyquire('../sidebar', {})
rafStub = (fn) ->
fn()
Sidebar = proxyquire('../sidebar', { raf: rafStub })
describe 'Sidebar', ->
sandbox = sinon.sandbox.create()
......@@ -253,3 +257,67 @@ describe 'Sidebar', ->
assert.calledWith(fakeCrossFrame.call, 'setVisibleHighlights', true)
assert.calledWith(sidebar.publish, 'setVisibleHighlights', true)
describe 'layout change notifier', ->
layoutChangeHandlerSpy = null
sidebar = null
frame = null
DEFAULT_WIDTH = 350
DEFAULT_HEIGHT = 600
assertLayoutValues = (args, expectations) ->
expected = Object.assign {
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
expanded: true
}, expectations
assert.deepEqual args, expected
beforeEach ->
layoutChangeHandlerSpy = sandbox.spy()
sidebar = createSidebar { onLayoutChange: layoutChangeHandlerSpy, sidebarAppUrl: '/' }
# remove info about call that happens on creation of sidebar
layoutChangeHandlerSpy.reset()
frame = sidebar.frame[0]
Object.assign frame.style, {
display: 'block',
width: DEFAULT_WIDTH + 'px',
height: DEFAULT_HEIGHT + 'px',
# width is based on left position of the window,
# we need to apply the css that puts the frame in the
# correct position
position: 'fixed',
top: 0,
left: '100%',
}
document.body.appendChild frame
afterEach ->
frame.remove()
it 'notifies when sidebar changes expanded state', ->
sidebar.show()
assert.calledOnce layoutChangeHandlerSpy
assertLayoutValues layoutChangeHandlerSpy.lastCall.args[0], {expanded: true}
sidebar.hide()
assert.calledTwice layoutChangeHandlerSpy
assertLayoutValues layoutChangeHandlerSpy.lastCall.args[0], {
expanded: false,
width: 0,
}
it 'notifies when sidebar is panned left', ->
sidebar.gestureState = { initial: -DEFAULT_WIDTH }
sidebar.onPan({type: 'panleft', deltaX: -50})
assertLayoutValues layoutChangeHandlerSpy.lastCall.args[0], { width: 400 }
it 'notifies when sidebar is panned right', ->
sidebar.gestureState = { initial: -DEFAULT_WIDTH }
sidebar.onPan({type: 'panright', deltaX: 50})
assertLayoutValues layoutChangeHandlerSpy.lastCall.args[0], { width: 300 }
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