Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
a27d83b9
Unverified
Commit
a27d83b9
authored
Nov 10, 2017
by
Robert Knight
Committed by
GitHub
Nov 10, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #555 from hypothesis/elife-ui
Change UI for Elife.
parents
4e442391
396fce0e
Changes
33
Show whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
453 additions
and
53 deletions
+453
-53
index.js
src/annotator/config/index.js
+8
-0
settings.js
src/annotator/config/settings.js
+4
-0
settings-test.js
src/annotator/config/test/settings-test.js
+24
-9
host.coffee
src/annotator/host.coffee
+5
-1
main.js
src/annotator/main.js
+4
-0
toolbar.coffee
src/annotator/plugin/toolbar.coffee
+41
-0
sidebar.coffee
src/annotator/sidebar.coffee
+34
-23
host-test.coffee
src/annotator/test/host-test.coffee
+4
-0
sidebar-test.coffee
src/annotator/test/sidebar-test.coffee
+42
-0
app.js
src/sidebar/app.js
+1
-0
annotation-header.js
src/sidebar/components/annotation-header.js
+6
-0
new-note-btn.js
src/sidebar/components/new-note-btn.js
+22
-0
selection-tabs.js
src/sidebar/components/selection-tabs.js
+3
-1
annotation-header-test.js
src/sidebar/components/test/annotation-header-test.js
+27
-1
new-note-btn-test.js
src/sidebar/components/test/new-note-btn-test.js
+54
-0
selection-tabs-test.js
src/sidebar/components/test/selection-tabs-test.js
+49
-0
thread-list-test.js
src/sidebar/components/test/thread-list-test.js
+13
-0
top-bar-test.js
src/sidebar/components/test/top-bar-test.js
+14
-1
thread-list.js
src/sidebar/components/thread-list.js
+3
-1
top-bar.js
src/sidebar/components/top-bar.js
+8
-0
host-config.js
src/sidebar/host-config.js
+6
-0
annotation-header.html
src/sidebar/templates/annotation-header.html
+6
-1
new-note-btn.html
src/sidebar/templates/new-note-btn.html
+3
-0
selection-tabs.html
src/sidebar/templates/selection-tabs.html
+5
-1
sidebar-content.html
src/sidebar/templates/sidebar-content.html
+0
-1
thread-list.html
src/sidebar/templates/thread-list.html
+16
-12
top-bar.html
src/sidebar/templates/top-bar.html
+2
-1
inject.scss
src/styles/annotator/inject.scss
+14
-0
app.scss
src/styles/app.scss
+1
-0
new-note.scss
src/styles/new-note.scss
+12
-0
selection-tabs.scss
src/styles/selection-tabs.scss
+4
-0
thread-list.scss
src/styles/thread-list.scss
+14
-0
top-bar.scss
src/styles/top-bar.scss
+4
-0
No files found.
src/annotator/config/index.js
View file @
a27d83b9
...
...
@@ -18,6 +18,14 @@ function configFrom(window_) {
// URL of the client's boot script. Used when injecting the client into
// child iframes.
clientUrl
:
settings
.
clientUrl
,
disableToolbarCloseBtn
:
settings
.
hostPageSetting
(
'disableToolbarCloseBtn'
,
{
defaultValue
:
true
}),
disableToolbarMinimizeBtn
:
settings
.
hostPageSetting
(
'disableToolbarMinimizeBtn'
),
disableToolbarHighlightsBtn
:
settings
.
hostPageSetting
(
'disableToolbarHighlightsBtn'
),
disableToolbarNewNoteBtn
:
settings
.
hostPageSetting
(
'disableToolbarNewNoteBtn'
),
disableBucketBar
:
settings
.
hostPageSetting
(
'disableBucketBar'
),
enableSidebarDropShadow
:
settings
.
hostPageSetting
(
'enableSidebarDropShadow'
),
theme
:
settings
.
hostPageSetting
(
'theme'
),
usernameUrl
:
settings
.
hostPageSetting
(
'usernameUrl'
),
// Temporary feature flag override for 1st-party OAuth
oauthEnabled
:
settings
.
hostPageSetting
(
'oauthEnabled'
),
onLayoutChange
:
settings
.
hostPageSetting
(
'onLayoutChange'
),
...
...
src/annotator/config/settings.js
View file @
a27d83b9
...
...
@@ -152,6 +152,10 @@ function settingsFrom(window_) {
return
jsonConfigs
[
name
];
}
if
(
typeof
options
.
defaultValue
!==
'undefined'
)
{
return
options
.
defaultValue
;
}
return
null
;
}
...
...
src/annotator/config/test/settings-test.js
View file @
a27d83b9
...
...
@@ -487,14 +487,6 @@ describe('annotator.config.settingsFrom', function() {
jsonSettings
:
{
foo
:
'jsonValue'
},
expected
:
undefined
,
},
{
when
:
'the client is embedded in a web page'
,
specify
:
"it returns null if the setting isn't defined anywhere"
,
isBrowserExtension
:
false
,
configFuncSettings
:
{},
jsonSettings
:
{},
expected
:
null
,
},
{
when
:
'the client is in a browser extension'
,
specify
:
'it always returns null'
,
...
...
@@ -521,6 +513,26 @@ describe('annotator.config.settingsFrom', function() {
jsonSettings
:
{
foo
:
'jsonValue'
},
expected
:
'jsonValue'
,
},
{
when
:
'the defaultValue is null'
,
specify
:
'it returns null'
,
isBrowserExtension
:
false
,
allowInBrowserExt
:
false
,
configFuncSettings
:
{},
jsonSettings
:
{},
defaultValue
:
null
,
expected
:
null
,
},
{
when
:
'the defaultValue is specified'
,
specify
:
'it returns that default value'
,
isBrowserExtension
:
false
,
allowInBrowserExt
:
false
,
configFuncSettings
:
{},
jsonSettings
:
{},
defaultValue
:
'test value'
,
expected
:
'test value'
,
},
].
forEach
(
function
(
test
)
{
context
(
test
.
when
,
function
()
{
specify
(
test
.
specify
,
function
()
{
...
...
@@ -531,7 +543,10 @@ describe('annotator.config.settingsFrom', function() {
var
setting
=
settings
.
hostPageSetting
(
'foo'
,
{
allowInBrowserExt
:
test
.
allowInBrowserExt
||
false
}
{
allowInBrowserExt
:
test
.
allowInBrowserExt
||
false
,
defaultValue
:
test
.
defaultValue
||
null
,
}
);
assert
.
strictEqual
(
setting
,
test
.
expected
);
...
...
src/annotator/host.coffee
View file @
a27d83b9
...
...
@@ -44,7 +44,11 @@ module.exports = class Host extends Guest
@
frame
=
$
(
'<div></div>'
)
.
css
(
'display'
,
'none'
)
.
addClass
(
'annotator-frame annotator-outer'
)
.
appendTo
(
element
)
if
config
.
enableSidebarDropShadow
@
frame
.
addClass
(
'annotator-frame--drop-shadow-enabled'
)
@
frame
.
appendTo
(
element
)
super
...
...
src/annotator/main.js
View file @
a27d83b9
...
...
@@ -59,6 +59,10 @@ $.noConflict(true)(function() {
window
.
__hypothesis_frame
=
true
;
}
if
(
config
.
disableBucketBar
)
{
delete
pluginClasses
.
BucketBar
;
}
config
.
pluginClasses
=
pluginClasses
;
var
annotator
=
new
Klass
(
document
.
body
,
config
);
...
...
src/annotator/plugin/toolbar.coffee
View file @
a27d83b9
...
...
@@ -28,6 +28,16 @@ module.exports = class Toolbar extends Plugin
$
(
@
element
).
append
@
toolbar
items
=
[
"title"
:
"Close Sidebar"
"class"
:
"annotator-frame-button--sidebar_close h-icon-close"
"name"
:
"sidebar-close"
"on"
:
"click"
:
(
event
)
=>
event
.
preventDefault
()
event
.
stopPropagation
()
@
annotator
.
hide
()
@
toolbar
.
find
(
'[name=sidebar-close]'
).
hide
();
,
"title"
:
"Toggle or Resize Sidebar"
"class"
:
"annotator-frame-button--sidebar_toggle h-icon-chevron-left"
"name"
:
"sidebar-toggle"
...
...
@@ -83,3 +93,34 @@ module.exports = class Toolbar extends Plugin
.
removeClass
(
'h-icon-visibility'
)
.
addClass
(
'h-icon-visibility-off'
)
.
prop
(
'title'
,
'Show Highlights'
);
disableMinimizeBtn
:
()
->
$
(
'[name=sidebar-toggle]'
).
remove
();
disableHighlightsBtn
:
()
->
$
(
'[name=highlight-visibility]'
).
remove
();
disableNewNoteBtn
:
()
->
$
(
'[name=insert-comment]'
).
remove
();
disableCloseBtn
:
()
->
$
(
'[name=sidebar-close]'
).
remove
();
getWidth
:
()
->
return
parseInt
(
window
.
getComputedStyle
(
this
.
toolbar
[
0
]).
width
)
hideCloseBtn
:
()
->
$
(
'[name=sidebar-close]'
).
hide
();
showCloseBtn
:
()
->
$
(
'[name=sidebar-close]'
).
show
();
showCollapseSidebarBtn
:
()
->
$
(
'[name=sidebar-toggle]'
)
.
removeClass
(
'h-icon-chevron-left'
)
.
addClass
(
'h-icon-chevron-right'
)
showExpandSidebarBtn
:
()
->
$
(
'[name=sidebar-toggle]'
)
.
removeClass
(
'h-icon-chevron-right'
)
.
addClass
(
'h-icon-chevron-left'
)
src/annotator/sidebar.coffee
View file @
a27d83b9
...
...
@@ -35,7 +35,17 @@ 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
)
@
toolbarWidth
=
@
plugins
.
Toolbar
.
getWidth
()
if
config
.
disableToolbarMinimizeBtn
@
plugins
.
Toolbar
.
disableMinimizeBtn
()
if
config
.
disableToolbarHighlightsBtn
@
plugins
.
Toolbar
.
disableHighlightsBtn
()
if
config
.
disableToolbarNewNoteBtn
@
plugins
.
Toolbar
.
disableNewNoteBtn
()
if
config
.
disableToolbarCloseBtn
@
plugins
.
Toolbar
.
disableCloseBtn
()
this
.
_setupGestures
()
# The partner-provided callback functions.
...
...
@@ -87,6 +97,7 @@ module.exports = class Sidebar extends Host
_setupGestures
:
->
$toggle
=
@
toolbar
.
find
(
'[name=sidebar-toggle]'
)
if
$toggle
[
0
]
# Prevent any default gestures on the handle
$toggle
.
on
(
'touchmove'
,
(
event
)
->
event
.
preventDefault
())
...
...
@@ -230,10 +241,10 @@ module.exports = class Sidebar extends Host
@
frame
.
css
'margin-left'
:
"
#{
-
1
*
@
frame
.
width
()
}
px"
@
frame
.
removeClass
'annotator-collapsed'
if
@
t
oolbar
?
@
toolbar
.
find
(
'[name=sidebar-toggle]'
)
.
removeClass
(
'h-icon-chevron-left'
)
.
addClass
(
'h-icon-chevron-right'
)
if
@
plugins
.
T
oolbar
?
@
plugins
.
Toolbar
.
showCollapseSidebarBtn
();
@
plugins
.
Toolbar
.
showCloseBtn
();
if
@
options
.
showHighlights
==
'whenSidebarOpen'
@
setVisibleHighlights
(
true
)
...
...
@@ -244,10 +255,10 @@ module.exports = class Sidebar extends Host
@
frame
.
css
'margin-left'
:
''
@
frame
.
addClass
'annotator-collapsed'
if
@
toolbar
?
@
toolbar
.
find
(
'[name=sidebar-toggle]'
)
.
removeClass
(
'h-icon-chevron-right'
)
.
addClass
(
'h-icon-chevron-left'
)
@
plugins
.
Toolbar
.
hideCloseBtn
();
if
@
plugins
.
Toolbar
?
@
plugins
.
Toolbar
.
showExpandSidebarBtn
();
if
@
options
.
showHighlights
==
'whenSidebarOpen'
@
setVisibleHighlights
(
false
)
...
...
src/annotator/test/host-test.coffee
View file @
a27d83b9
...
...
@@ -80,3 +80,7 @@ describe 'Host', ->
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 in enableSidebarDropShadow'
,
->
host
=
createHost
({
enableSidebarDropShadow
:
true
})
assert
.
isTrue
(
host
.
frame
.
hasClass
(
'annotator-frame--drop-shadow-enabled'
))
src/annotator/test/sidebar-test.coffee
View file @
a27d83b9
...
...
@@ -19,15 +19,34 @@ describe 'Sidebar', ->
return
new
Sidebar
(
element
,
config
)
beforeEach
->
sandbox
.
stub
(
Sidebar
.
prototype
,
'_setupGestures'
)
fakeCrossFrame
=
{}
fakeCrossFrame
.
onConnect
=
sandbox
.
stub
().
returns
(
fakeCrossFrame
)
fakeCrossFrame
.
on
=
sandbox
.
stub
().
returns
(
fakeCrossFrame
)
fakeCrossFrame
.
call
=
sandbox
.
spy
()
fakeCrossFrame
.
destroy
=
sandbox
.
stub
()
fakeToolbar
=
{}
fakeToolbar
.
disableMinimizeBtn
=
sandbox
.
spy
()
fakeToolbar
.
disableHighlightsBtn
=
sandbox
.
spy
()
fakeToolbar
.
disableNewNoteBtn
=
sandbox
.
spy
()
fakeToolbar
.
disableCloseBtn
=
sandbox
.
spy
()
fakeToolbar
.
hideCloseBtn
=
sandbox
.
spy
()
fakeToolbar
.
showCloseBtn
=
sandbox
.
spy
()
fakeToolbar
.
showExpandSidebarBtn
=
sandbox
.
spy
()
fakeToolbar
.
showCollapseSidebarBtn
=
sandbox
.
spy
()
fakeToolbar
.
getWidth
=
sandbox
.
stub
()
fakeToolbar
.
destroy
=
sandbox
.
stub
()
CrossFrame
=
sandbox
.
stub
()
CrossFrame
.
returns
(
fakeCrossFrame
)
Toolbar
=
sandbox
.
stub
()
Toolbar
.
returns
(
fakeToolbar
)
sidebarConfig
.
pluginClasses
[
'CrossFrame'
]
=
CrossFrame
sidebarConfig
.
pluginClasses
[
'Toolbar'
]
=
Toolbar
afterEach
->
sandbox
.
restore
()
...
...
@@ -257,6 +276,29 @@ describe 'Sidebar', ->
assert
.
calledWith
(
fakeCrossFrame
.
call
,
'setVisibleHighlights'
,
true
)
assert
.
calledWith
(
sidebar
.
publish
,
'setVisibleHighlights'
,
true
)
context
'Hide toolbar buttons'
,
->
it
'disables minimize btn'
,
->
sidebar
=
createSidebar
(
config
=
{
disableToolbarMinimizeBtn
:
true
})
assert
.
called
(
sidebar
.
plugins
.
Toolbar
.
disableMinimizeBtn
)
it
'disables minimize btn'
,
->
sidebar
=
createSidebar
(
config
=
{
disableToolbarHighlightsBtn
:
true
})
assert
.
called
(
sidebar
.
plugins
.
Toolbar
.
disableHighlightsBtn
)
it
'disables minimize btn'
,
->
sidebar
=
createSidebar
(
config
=
{
disableToolbarNewNoteBtn
:
true
})
assert
.
called
(
sidebar
.
plugins
.
Toolbar
.
disableNewNoteBtn
)
it
'disables minimize btn'
,
->
sidebar
=
createSidebar
(
config
=
{
disableToolbarCloseBtn
:
true
})
assert
.
called
(
sidebar
.
plugins
.
Toolbar
.
disableCloseBtn
)
describe
'layout change notifier'
,
->
layoutChangeHandlerSpy
=
null
...
...
src/sidebar/app.js
View file @
a27d83b9
...
...
@@ -145,6 +145,7 @@ module.exports = angular.module('h', [
.
component
(
'loginControl'
,
require
(
'./components/login-control'
))
.
component
(
'markdown'
,
require
(
'./components/markdown'
))
.
component
(
'moderationBanner'
,
require
(
'./components/moderation-banner'
))
.
component
(
'newNoteBtn'
,
require
(
'./components/new-note-btn'
))
.
component
(
'publishAnnotationBtn'
,
require
(
'./components/publish-annotation-btn'
))
.
component
(
'searchInput'
,
require
(
'./components/search-input'
))
.
component
(
'searchStatusBar'
,
require
(
'./components/search-status-bar'
))
...
...
src/sidebar/components/annotation-header.js
View file @
a27d83b9
...
...
@@ -24,6 +24,12 @@ function AnnotationHeaderController(groups, settings, serviceUrl) {
return
persona
.
isThirdPartyUser
(
self
.
annotation
.
user
,
settings
.
authDomain
);
};
this
.
thirdPartyUsernameLink
=
function
()
{
return
settings
.
usernameUrl
?
settings
.
usernameUrl
+
persona
.
username
(
this
.
annotation
.
user
):
null
;
};
this
.
serviceUrl
=
serviceUrl
;
this
.
group
=
function
()
{
...
...
src/sidebar/components/new-note-btn.js
0 → 100644
View file @
a27d83b9
'use strict'
;
var
events
=
require
(
'../events'
);
module
.
exports
=
{
controllerAs
:
'vm'
,
//@ngInject
controller
:
function
(
$rootScope
,
annotationUI
)
{
this
.
onNewNoteBtnClick
=
function
(){
var
topLevelFrame
=
annotationUI
.
frames
().
find
(
f
=>!
f
.
id
);
var
annot
=
{
target
:
[],
uri
:
topLevelFrame
.
uri
,
};
$rootScope
.
$broadcast
(
events
.
BEFORE_ANNOTATION_CREATED
,
annot
);
};
},
bindings
:
{
},
template
:
require
(
'../templates/new-note-btn.html'
),
};
src/sidebar/components/selection-tabs.js
View file @
a27d83b9
...
...
@@ -5,11 +5,13 @@ var uiConstants = require('../ui-constants');
module
.
exports
=
{
controllerAs
:
'vm'
,
//@ngInject
controller
:
function
(
$element
,
annotationUI
,
features
)
{
controller
:
function
(
$element
,
annotationUI
,
features
,
settings
)
{
this
.
TAB_ANNOTATIONS
=
uiConstants
.
TAB_ANNOTATIONS
;
this
.
TAB_NOTES
=
uiConstants
.
TAB_NOTES
;
this
.
TAB_ORPHANS
=
uiConstants
.
TAB_ORPHANS
;
this
.
isThemeClean
=
settings
.
theme
===
'clean'
;
this
.
selectTab
=
function
(
type
)
{
annotationUI
.
clearSelectedAnnotations
();
annotationUI
.
selectTab
(
type
);
...
...
src/sidebar/components/test/annotation-header-test.js
View file @
a27d83b9
...
...
@@ -14,7 +14,7 @@ var fakeDocumentMeta = {
describe
(
'sidebar.components.annotation-header'
,
function
()
{
var
$componentController
;
var
fakeGroups
;
var
fakeSettings
;
var
fakeSettings
=
{
usernameUrl
:
'http://www.example.org/'
}
;
var
fakeServiceUrl
;
before
(
function
()
{
...
...
@@ -93,5 +93,31 @@ describe('sidebar.components.annotation-header', function () {
assert
.
deepEqual
(
ctrl
.
displayName
(),
'Bill Jones'
);
});
});
describe
(
'#thirdPartyUsernameLink'
,
()
=>
{
it
(
'returns the custom username link if set'
,
()
=>
{
var
ann
;
var
ctrl
;
fakeSettings
.
usernameUrl
=
'http://www.example.org/'
;
ann
=
fixtures
.
defaultAnnotation
();
ctrl
=
$componentController
(
'annotationHeader'
,
{},
{
annotation
:
ann
,
});
assert
.
deepEqual
(
ctrl
.
thirdPartyUsernameLink
(),
'http://www.example.org/bill'
);
});
it
(
'returns null if no custom username link is set in the settings object'
,
()
=>
{
var
ann
;
var
ctrl
;
fakeSettings
.
usernameUrl
=
null
;
ann
=
fixtures
.
defaultAnnotation
();
ctrl
=
$componentController
(
'annotationHeader'
,
{},
{
annotation
:
ann
,
});
assert
.
deepEqual
(
ctrl
.
thirdPartyUsernameLink
(),
null
);
});
});
});
});
src/sidebar/components/test/new-note-btn-test.js
0 → 100644
View file @
a27d83b9
'use strict'
;
var
angular
=
require
(
'angular'
);
var
events
=
require
(
'../../events'
);
var
util
=
require
(
'../../directive/test/util'
);
describe
(
'newNoteBtn'
,
function
()
{
var
$rootScope
;
var
sandbox
=
sinon
.
sandbox
.
create
();
var
fakeAnnotationUI
=
{
frames
:
sinon
.
stub
().
returns
([{
id
:
null
,
uri
:
'www.example.org'
},
{
id
:
'1'
,
uri
:
'www.example.org'
}]),
};
before
(
function
()
{
angular
.
module
(
'app'
,
[])
.
component
(
'selectionTabs'
,
require
(
'../selection-tabs'
))
.
component
(
'newNoteBtn'
,
require
(
'../new-note-btn'
));
});
beforeEach
(
function
()
{
var
fakeFeatures
=
{
flagEnabled
:
sinon
.
stub
().
returns
(
true
),
};
var
fakeSettings
=
{
theme
:
'clean'
};
angular
.
mock
.
module
(
'app'
,
{
annotationUI
:
fakeAnnotationUI
,
features
:
fakeFeatures
,
settings
:
fakeSettings
,
});
angular
.
mock
.
inject
(
function
(
_$componentController_
,
_$rootScope_
)
{
$rootScope
=
_$rootScope_
;
});
});
afterEach
(
function
()
{
sandbox
.
restore
();
});
it
(
'should broadcast BEFORE_ANNOTATION_CREATED event when the new note button is clicked'
,
function
()
{
var
annot
=
{
target
:
[],
uri
:
'www.example.org'
,
};
var
elem
=
util
.
createDirective
(
document
,
'newNoteBtn'
,
{
annotationUI
:
fakeAnnotationUI
,
});
sandbox
.
spy
(
$rootScope
,
'$broadcast'
);
elem
.
ctrl
.
onNewNoteBtnClick
();
assert
.
calledWith
(
$rootScope
.
$broadcast
,
events
.
BEFORE_ANNOTATION_CREATED
,
annot
);
});
});
src/sidebar/components/test/selection-tabs-test.js
View file @
a27d83b9
...
...
@@ -15,10 +15,12 @@ describe('selectionTabs', function () {
var
fakeFeatures
=
{
flagEnabled
:
sinon
.
stub
().
returns
(
true
),
};
var
fakeSettings
=
{};
angular
.
mock
.
module
(
'app'
,
{
annotationUI
:
fakeAnnotationUI
,
features
:
fakeFeatures
,
settings
:
fakeSettings
,
});
});
...
...
@@ -59,5 +61,52 @@ describe('selectionTabs', function () {
var
tabs
=
elem
[
0
].
querySelectorAll
(
'a'
);
assert
.
isTrue
(
tabs
[
1
].
classList
.
contains
(
'is-selected'
));
});
it
(
'should not show the clean theme when settings does not contain the clean theme option'
,
function
()
{
var
elem
=
util
.
createDirective
(
document
,
'selectionTabs'
,
{
selectedTab
:
'annotation'
,
totalAnnotations
:
'123'
,
totalNotes
:
'456'
,
});
assert
.
isFalse
(
elem
[
0
].
querySelectorAll
(
'.selection-tabs'
)[
0
].
classList
.
contains
(
'selection-tabs--theme-clean'
));
});
it
(
'should show the clean theme when settings contains the clean theme option'
,
function
()
{
angular
.
mock
.
module
(
'app'
,
{
annotationUI
:
{},
features
:
{
flagEnabled
:
sinon
.
stub
().
returns
(
true
),
},
settings
:
{
theme
:
'clean'
},
});
var
elem
=
util
.
createDirective
(
document
,
'selectionTabs'
,
{
selectedTab
:
'annotation'
,
totalAnnotations
:
'123'
,
totalNotes
:
'456'
,
});
assert
.
isTrue
(
elem
[
0
].
querySelectorAll
(
'.selection-tabs'
)[
0
].
classList
.
contains
(
'selection-tabs--theme-clean'
));
});
it
(
'should display the new note button when the notes tab is active'
,
function
()
{
var
elem
=
util
.
createDirective
(
document
,
'selectionTabs'
,
{
selectedTab
:
'note'
,
totalAnnotations
:
'123'
,
totalNotes
:
'456'
,
});
var
newNoteElem
=
elem
[
0
].
querySelectorAll
(
'new-note-btn'
);
assert
.
equal
(
newNoteElem
.
length
,
1
);
});
it
(
'should not display the new new note button when the annotations tab is active'
,
function
()
{
var
elem
=
util
.
createDirective
(
document
,
'selectionTabs'
,
{
selectedTab
:
'annotation'
,
totalAnnotations
:
'123'
,
totalNotes
:
'456'
,
});
var
newNoteElem
=
elem
[
0
].
querySelectorAll
(
'new-note-btn'
);
assert
.
equal
(
newNoteElem
.
length
,
0
);
});
});
});
src/sidebar/components/test/thread-list-test.js
View file @
a27d83b9
...
...
@@ -41,6 +41,7 @@ var threadFixtures = immutable({
});
var
fakeVirtualThread
;
var
fakeSettings
=
{};
function
FakeVirtualThreadList
(
$scope
,
$window
,
rootThread
,
options
)
{
...
...
@@ -119,6 +120,7 @@ describe('threadList', function () {
beforeEach
(
function
()
{
angular
.
mock
.
module
(
'app'
,
{
VirtualThreadList
:
FakeVirtualThreadList
,
settings
:
fakeSettings
,
});
threadListContainers
=
[];
});
...
...
@@ -129,6 +131,17 @@ describe('threadList', function () {
});
});
it
(
'shows the clean theme when settings contains the clean theme option'
,
function
()
{
angular
.
mock
.
module
(
'app'
,
{
VirtualThreadList
:
FakeVirtualThreadList
,
settings
:
{
theme
:
'clean'
},
});
var
element
=
createThreadList
();
fakeVirtualThread
.
notify
();
element
.
scope
.
$digest
();
assert
.
equal
(
element
[
0
].
querySelectorAll
(
'.thread-list__card--theme-clean'
).
length
,
element
[
0
].
querySelectorAll
(
'annotation-thread'
).
length
);
});
it
(
'displays the children of the root thread'
,
function
()
{
var
element
=
createThreadList
();
fakeVirtualThread
.
notify
();
...
...
src/sidebar/components/test/top-bar-test.js
View file @
a27d83b9
...
...
@@ -5,6 +5,8 @@ var angular = require('angular');
var
util
=
require
(
'../../directive/test/util'
);
describe
(
'topBar'
,
function
()
{
var
fakeSettings
=
{};
before
(
function
()
{
angular
.
module
(
'app'
,
[])
.
component
(
'topBar'
,
require
(
'../top-bar'
))
...
...
@@ -20,7 +22,9 @@ describe('topBar', function () {
});
beforeEach
(
function
()
{
angular
.
mock
.
module
(
'app'
);
angular
.
mock
.
module
(
'app'
,
{
settings
:
fakeSettings
,
});
});
function
applyUpdateBtn
(
el
)
{
...
...
@@ -125,4 +129,13 @@ describe('topBar', function () {
sortDropdown
.
onChangeSortKey
({
sortKey
:
'Oldest'
});
assert
.
calledWith
(
onChangeSortKey
,
'Oldest'
);
});
it
(
'shows the clean theme when settings contains the clean theme option'
,
function
()
{
angular
.
mock
.
module
(
'app'
,
{
settings
:
{
theme
:
'clean'
},
});
var
el
=
createTopBar
();
assert
.
ok
(
el
[
0
].
querySelector
(
'.top-bar--theme-clean'
));
});
});
src/sidebar/components/thread-list.js
View file @
a27d83b9
...
...
@@ -49,7 +49,7 @@ var virtualThreadOptions = {
};
// @ngInject
function
ThreadListController
(
$element
,
$scope
,
VirtualThreadList
)
{
function
ThreadListController
(
$element
,
$scope
,
settings
,
VirtualThreadList
)
{
// `visibleThreads` keeps track of the subset of all threads matching the
// current filters which are in or near the viewport and the view then renders
// only those threads, using placeholders above and below the visible threads
...
...
@@ -65,6 +65,8 @@ function ThreadListController($element, $scope, VirtualThreadList) {
// Firefox. See https://github.com/hypothesis/client/issues/341
this
.
scrollRoot
=
document
.
querySelector
(
'.js-thread-list-scroll-root'
);
this
.
isThemeClean
=
settings
.
theme
===
'clean'
;
var
options
=
Object
.
assign
({
scrollRoot
:
this
.
scrollRoot
,
},
virtualThreadOptions
);
...
...
src/sidebar/components/top-bar.js
View file @
a27d83b9
...
...
@@ -2,6 +2,14 @@
module
.
exports
=
{
controllerAs
:
'vm'
,
//@ngInject
controller
:
function
(
settings
)
{
if
(
settings
.
theme
&&
settings
.
theme
===
'clean'
)
{
this
.
isThemeClean
=
true
;
}
else
{
this
.
isThemeClean
=
false
;
}
},
bindings
:
{
auth
:
'<'
,
isSidebar
:
'<'
,
...
...
src/sidebar/host-config.js
View file @
a27d83b9
...
...
@@ -34,6 +34,12 @@ function hostPageConfig(window) {
// OAuth feature flag override.
// This should be removed once OAuth is enabled for first party accounts.
'oauthEnabled'
,
// Theme which can either be specified as 'clean'.
// If nothing is the specified the classic look is applied.
'theme'
,
'usernameUrl'
,
];
return
Object
.
keys
(
config
).
reduce
(
function
(
result
,
key
)
{
...
...
src/sidebar/templates/annotation-header.html
View file @
a27d83b9
...
...
@@ -6,8 +6,13 @@
ng-if=
"!vm.isThirdPartyUser()"
ng-href=
"{{vm.serviceUrl('user',{user:vm.user()})}}"
>
{{vm.displayName()}}
</a>
<a
class=
"annotation-header__user"
target=
"_blank"
ng-if=
"vm.isThirdPartyUser() && vm.thirdPartyUsernameLink()"
href=
"{{ vm.thirdPartyUsernameLink() }}"
>
{{vm.displayName()}}
</a>
<span
class=
"annotation-header__user"
ng-if=
"vm.isThirdPartyUser()"
ng-if=
"vm.isThirdPartyUser()
&& !vm.thirdPartyUsernameLink()
"
>
{{vm.displayName()}}
</span>
<span
class=
"annotation-collapsed-replies"
>
<a
class=
"annotation-link"
href=
""
...
...
src/sidebar/templates/new-note-btn.html
0 → 100644
View file @
a27d83b9
<button
class=
"new-note__create"
ng-click=
"vm.onNewNoteBtnClick()"
h-branding=
"ctaBackgroundColor"
>
+ New note
</button>
src/sidebar/templates/selection-tabs.html
View file @
a27d83b9
<!-- Tabbed display of annotations and notes. -->
<div
class=
"selection-tabs"
>
<div
class=
"selection-tabs"
ng-class=
"{'selection-tabs--theme-clean' : vm.isThemeClean }"
>
<a
class=
"selection-tabs__type"
href=
"#"
ng-class=
"{'is-selected': vm.selectedTab === vm.TAB_ANNOTATIONS}"
...
...
@@ -32,6 +33,9 @@
</span>
</a>
</div>
<new-note-btn
ng-if=
"vm.selectedTab === vm.TAB_NOTES"
>
</new-note-btn>
<div
ng-if=
"!vm.isLoading()"
class=
"selection-tabs__empty-message"
>
<div
ng-if=
"vm.showNotesUnavailableMessage()"
class=
"annotation-unavailable-message"
>
<p
class=
"annotation-unavailable-message__label"
>
...
...
src/sidebar/templates/sidebar-content.html
View file @
a27d83b9
...
...
@@ -37,7 +37,6 @@
</span>
</p>
</div>
<thread-list
on-change-collapsed=
"vm.setCollapsed(id, collapsed)"
on-clear-selection=
"vm.clearSelection()"
...
...
src/sidebar/templates/thread-list.html
View file @
a27d83b9
<ul
class=
"thread-list"
>
<li
class=
"thread-list__spacer"
ng-style=
"{height: vm.virtualThreadList.offscreenUpperHeight}"
></li>
<li
id=
"{{child.id}}"
<li
ng-repeat=
"child in vm.virtualThreadList.visibleThreads track by child.id"
>
<div
id=
"{{child.id}}"
class=
"thread-list__card"
ng-mouseenter=
"vm.onFocus({annotation: child.annotation})"
ng-class=
"{'thread-list__card--theme-clean' : vm.isThemeClean }"
ng-click=
"vm.onSelect({annotation: child.annotation})"
ng-mouseleave=
"vm.onFocus({annotation: null})"
ng-repeat=
"child in vm.virtualThreadList.visibleThreads track by child.id"
>
ng-mouseleave=
"vm.onFocus({annotation: null})"
>
<annotation-thread
thread=
"child"
show-document-info=
"vm.showDocumentInfo"
on-change-collapsed=
"vm.onChangeCollapsed({id: id, collapsed: collapsed})"
on-force-visible=
"vm.onForceVisible({thread: thread})"
>
</annotation-thread>
</div>
<hr
ng-if=
"vm.isThemeClean"
class=
"thread-list__separator--theme-clean"
/>
</li>
<li
id=
"{{child.id}}"
ng-show=
"false"
...
...
src/sidebar/templates/top-bar.html
View file @
a27d83b9
<!-- top bar for the sidebar and the stream.
!-->
<div
class=
"top-bar"
>
<div
class=
"top-bar"
ng-class=
"{'top-bar--theme-clean' : vm.isThemeClean }"
>
<!-- Legacy design for top bar, as used in the stream !-->
<div
class=
"top-bar__inner content"
ng-if=
"::!vm.isSidebar"
>
<search-input
...
...
src/styles/annotator/inject.scss
View file @
a27d83b9
...
...
@@ -148,6 +148,20 @@ $base-font-size: 14px;
// to match the height of the top bar
height
:
40px
;
}
.annotator-frame-button--sidebar_close
{
box-shadow
:
0px
1px
4px
0px
rgba
(
0
,
0
,
0
,
0
.5
);
border-radius
:
0px
;
border-style
:
solid
none
solid
solid
;
width
:
27px
;
margin-top
:
140px
;
margin-left
:
6px
;
height
:
27px
;
}
}
.annotator-frame--drop-shadow-enabled
{
box-shadow
:
0px
2px
4px
0px
rgba
(
0
,
0
,
0
,
0
.5
);
}
.annotator-placeholder
{
...
...
src/styles/app.scss
View file @
a27d83b9
...
...
@@ -19,6 +19,7 @@ $base-line-height: 20px;
@import
'./login-control'
;
@import
'./markdown'
;
@import
'./moderation-banner'
;
@import
'./new-note'
;
@import
'./primary-action-btn'
;
@import
'./publish-annotation-btn'
;
@import
'./search-status-bar'
;
...
...
src/styles/new-note.scss
0 → 100644
View file @
a27d83b9
.new-note__create
{
background-color
:
$color-dove-gray
;
border
:
none
;
border-radius
:
3px
;
color
:
#fff
;
display
:
flex
;
font-weight
:
500
;
margin-left
:
auto
;
margin-right
:
14px
;
margin-bottom
:
10px
;
text-align
:
center
;
}
src/styles/selection-tabs.scss
View file @
a27d83b9
...
...
@@ -12,6 +12,10 @@
padding-bottom
:
10px
;
}
.selection-tabs--theme-clean
{
margin-left
:
15px
;
}
.selection-tabs__type
{
color
:
$grey-6
;
margin-right
:
20px
;
...
...
src/styles/thread-list.scss
View file @
a27d83b9
...
...
@@ -17,9 +17,23 @@
}
}
.thread-list__card--theme-clean
{
box-shadow
:
none
;
&
:hover
{
box-shadow
:
none
;
}
}
.thread-list__spacer
{
// This is a hidden element which is used to reserve space for off-screen
// threads, so it should not occupy any space other than that set via its
// 'height' inline style property.
margin
:
0
;
}
.thread-list__separator--theme-clean
{
border
:
0
;
border-top
:
1px
solid
#E1E1E1
;
margin
:
-8px
15px
10px
15px
;
}
src/styles/top-bar.scss
View file @
a27d83b9
...
...
@@ -17,6 +17,10 @@
transform
:
translate3d
(
0
,
0
,
0
);
}
.top-bar--theme-clean
{
border-bottom
:
none
;
}
.top-bar__inner
{
// the edges of the top-bar's contents should be aligned
// with the edges of annotation cards displayed below
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment