Skip to content

Commit

Permalink
Merge pull request #499 from hypothesis/on-layout-change-support
Browse files Browse the repository at this point in the history
Adding support for onLayoutChange notifier configuration
  • Loading branch information
robertknight authored Jul 17, 2017
2 parents 2cb95be + 0f2f8b0 commit 0cdccd0
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 19 deletions.
19 changes: 8 additions & 11 deletions src/annotator/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
};
}

Expand Down
80 changes: 73 additions & 7 deletions src/annotator/sidebar.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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: ->
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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'
Expand All @@ -188,6 +252,8 @@ module.exports = class Sidebar extends Host
if @options.showHighlights == 'whenSidebarOpen'
@setVisibleHighlights(false)

this._notifyOfLayoutChange(false)

isOpen: ->
!@frame.hasClass('annotator-collapsed')

Expand Down
70 changes: 69 additions & 1 deletion src/annotator/test/sidebar-test.coffee
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
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()
Expand Down Expand Up @@ -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 }

0 comments on commit 0cdccd0

Please sign in to comment.