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
6489d7e3
Commit
6489d7e3
authored
Jun 04, 2019
by
Lyza Danger Gardner
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add preact UserMenu component
parent
4d12a798
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
339 additions
and
0 deletions
+339
-0
user-menu-test.js
src/sidebar/components/test/user-menu-test.js
+247
-0
user-menu.js
src/sidebar/components/user-menu.js
+88
-0
index.js
src/sidebar/index.js
+4
-0
No files found.
src/sidebar/components/test/user-menu-test.js
0 → 100644
View file @
6489d7e3
'use strict'
;
const
{
createElement
}
=
require
(
'preact'
);
const
{
shallow
}
=
require
(
'enzyme'
);
const
UserMenu
=
require
(
'../user-menu'
);
const
MenuItem
=
require
(
'../menu-item'
);
describe
(
'UserMenu'
,
()
=>
{
let
fakeAuth
;
let
fakeBridge
;
let
fakeIsThirdPartyUser
;
let
fakeOnLogout
;
let
fakeProfileBridgeEvent
;
let
fakeServiceConfig
;
let
fakeServiceUrl
;
let
fakeSettings
;
const
createUserMenu
=
()
=>
{
return
shallow
(
<
UserMenu
auth
=
{
fakeAuth
}
bridge
=
{
fakeBridge
}
onLogout
=
{
fakeOnLogout
}
serviceUrl
=
{
fakeServiceUrl
}
settings
=
{
fakeSettings
}
/
>
).
dive
();
// Dive needed because this component uses `withServices`
};
const
findMenuItem
=
(
wrapper
,
labelText
)
=>
{
return
wrapper
.
find
(
MenuItem
)
.
filterWhere
(
n
=>
n
.
prop
(
'label'
)
===
labelText
);
};
beforeEach
(()
=>
{
fakeAuth
=
{
displayName
:
'Eleanor Fishtail'
,
status
:
'logged-in'
,
userid
:
'acct:eleanorFishtail@hypothes.is'
,
username
:
'eleanorFishy'
,
};
fakeBridge
=
{
call
:
sinon
.
stub
()
};
fakeIsThirdPartyUser
=
sinon
.
stub
();
fakeOnLogout
=
sinon
.
stub
();
fakeProfileBridgeEvent
=
'profile-requested'
;
fakeServiceConfig
=
sinon
.
stub
();
fakeServiceUrl
=
sinon
.
stub
();
fakeSettings
=
{
authDomain
:
'hypothes.is'
,
};
UserMenu
.
$imports
.
$mock
({
'../util/account-id'
:
{
isThirdPartyUser
:
fakeIsThirdPartyUser
,
},
'../service-config'
:
fakeServiceConfig
,
'../../shared/bridge-events'
:
{
PROFILE_REQUESTED
:
fakeProfileBridgeEvent
,
},
});
});
afterEach
(()
=>
{
UserMenu
.
$imports
.
$restore
();
});
describe
(
'profile menu item'
,
()
=>
{
context
(
'first-party user'
,
()
=>
{
beforeEach
(()
=>
{
fakeIsThirdPartyUser
.
returns
(
false
);
fakeServiceUrl
.
returns
(
'profile-link'
);
});
it
(
'should be enabled'
,
()
=>
{
const
wrapper
=
createUserMenu
();
const
profileMenuItem
=
findMenuItem
(
wrapper
,
fakeAuth
.
displayName
);
assert
.
notOk
(
profileMenuItem
.
prop
(
'isDisabled'
));
});
it
(
'should have a link (href)'
,
()
=>
{
const
wrapper
=
createUserMenu
();
const
profileMenuItem
=
findMenuItem
(
wrapper
,
fakeAuth
.
displayName
);
assert
.
equal
(
profileMenuItem
.
prop
(
'href'
),
'profile-link'
);
});
it
(
'should have a callback'
,
()
=>
{
const
wrapper
=
createUserMenu
();
const
profileMenuItem
=
findMenuItem
(
wrapper
,
fakeAuth
.
displayName
);
assert
.
isFunction
(
profileMenuItem
.
prop
(
'onClick'
));
});
});
context
(
'third-party user'
,
()
=>
{
beforeEach
(()
=>
{
fakeIsThirdPartyUser
.
returns
(
true
);
});
it
(
'should be disabled if no service configured'
,
()
=>
{
fakeServiceConfig
.
returns
(
null
);
const
wrapper
=
createUserMenu
();
const
profileMenuItem
=
findMenuItem
(
wrapper
,
fakeAuth
.
displayName
);
assert
.
isTrue
(
profileMenuItem
.
prop
(
'isDisabled'
));
});
it
(
'should be disabled if service feature not supported'
,
()
=>
{
fakeServiceConfig
.
returns
({
onProfileRequestProvided
:
false
});
const
wrapper
=
createUserMenu
();
const
profileMenuItem
=
findMenuItem
(
wrapper
,
fakeAuth
.
displayName
);
assert
.
isTrue
(
profileMenuItem
.
prop
(
'isDisabled'
));
});
it
(
'should be enabled if service feature support'
,
()
=>
{
fakeServiceConfig
.
returns
({
onProfileRequestProvided
:
true
});
const
wrapper
=
createUserMenu
();
const
profileMenuItem
=
findMenuItem
(
wrapper
,
fakeAuth
.
displayName
);
assert
.
notOk
(
profileMenuItem
.
prop
(
'isDisabled'
));
});
it
(
'should have a callback if enabled'
,
()
=>
{
fakeServiceConfig
.
returns
({
onProfileRequestProvided
:
true
});
const
wrapper
=
createUserMenu
();
const
profileMenuItem
=
findMenuItem
(
wrapper
,
fakeAuth
.
displayName
);
assert
.
isFunction
(
profileMenuItem
.
prop
(
'onClick'
));
});
});
describe
(
'profile-selected callback'
,
()
=>
{
it
(
'should fire profile event for third-party user'
,
()
=>
{
fakeServiceConfig
.
returns
({
onProfileRequestProvided
:
true
});
fakeIsThirdPartyUser
.
returns
(
true
);
const
wrapper
=
createUserMenu
();
const
profileMenuItem
=
findMenuItem
(
wrapper
,
fakeAuth
.
displayName
);
const
onProfileSelected
=
profileMenuItem
.
prop
(
'onClick'
);
onProfileSelected
();
assert
.
equal
(
fakeBridge
.
call
.
callCount
,
1
);
assert
.
calledWith
(
fakeBridge
.
call
,
fakeProfileBridgeEvent
);
});
it
(
'should not fire profile event for first-party user'
,
()
=>
{
fakeIsThirdPartyUser
.
returns
(
false
);
const
wrapper
=
createUserMenu
();
const
profileMenuItem
=
findMenuItem
(
wrapper
,
fakeAuth
.
displayName
);
const
onProfileSelected
=
profileMenuItem
.
prop
(
'onClick'
);
onProfileSelected
();
assert
.
equal
(
fakeBridge
.
call
.
callCount
,
0
);
});
});
});
describe
(
'account settings menu item'
,
()
=>
{
it
(
'should be present if first-party user'
,
()
=>
{
fakeIsThirdPartyUser
.
returns
(
false
);
const
wrapper
=
createUserMenu
();
const
accountMenuItem
=
findMenuItem
(
wrapper
,
'Account settings'
);
assert
.
isTrue
(
accountMenuItem
.
exists
());
assert
.
calledWith
(
fakeServiceUrl
,
'account.settings'
);
});
it
(
'should not be present if third-party user'
,
()
=>
{
fakeIsThirdPartyUser
.
returns
(
true
);
const
wrapper
=
createUserMenu
();
const
accountMenuItem
=
findMenuItem
(
wrapper
,
'Account settings'
);
assert
.
isFalse
(
accountMenuItem
.
exists
());
});
});
describe
(
'log out menu item'
,
()
=>
{
const
tests
=
[
{
it
:
'should be present for first-party user if no service configured'
,
isThirdParty
:
false
,
serviceConfigReturns
:
null
,
expected
:
true
,
},
{
it
:
'should be present for first-party user if service supports `onLogoutRequest`'
,
isThirdParty
:
false
,
serviceConfigReturns
:
{
onLogoutRequestProvided
:
true
},
expected
:
true
,
},
{
it
:
'should be present for first-party user if service does not support `onLogoutRequest`'
,
isThirdParty
:
false
,
serviceConfigReturns
:
{
onLogoutRequestProvided
:
false
},
expected
:
true
,
},
{
it
:
'should be absent for third-party user if no service configured'
,
isThirdParty
:
true
,
serviceConfigReturns
:
null
,
expected
:
false
,
},
{
it
:
'should be present for third-party user if service supports `onLogoutRequest`'
,
isThirdParty
:
true
,
serviceConfigReturns
:
{
onLogoutRequestProvided
:
true
},
expected
:
true
,
},
{
it
:
'should be absent for third-party user if `onLogoutRequest` not supported'
,
isThirdParty
:
true
,
serviceConfigReturns
:
{
onLogoutRequestProvided
:
false
},
expected
:
false
,
},
];
tests
.
forEach
(
test
=>
{
it
(
test
.
it
,
()
=>
{
fakeIsThirdPartyUser
.
returns
(
test
.
isThirdParty
);
fakeServiceConfig
.
returns
(
test
.
serviceConfigReturns
);
const
wrapper
=
createUserMenu
();
const
logOutMenuItem
=
findMenuItem
(
wrapper
,
'Log out'
);
assert
.
equal
(
logOutMenuItem
.
exists
(),
test
.
expected
);
if
(
test
.
expected
)
{
assert
.
equal
(
logOutMenuItem
.
prop
(
'onClick'
),
fakeOnLogout
);
}
});
});
});
});
src/sidebar/components/user-menu.js
0 → 100644
View file @
6489d7e3
'use strict'
;
const
{
createElement
}
=
require
(
'preact'
);
const
propTypes
=
require
(
'prop-types'
);
const
bridgeEvents
=
require
(
'../../shared/bridge-events'
);
const
{
isThirdPartyUser
}
=
require
(
'../util/account-id'
);
const
serviceConfig
=
require
(
'../service-config'
);
const
{
withServices
}
=
require
(
'../util/service-context'
);
const
Menu
=
require
(
'./menu'
);
const
MenuSection
=
require
(
'./menu-section'
);
const
MenuItem
=
require
(
'./menu-item'
);
/**
* A menu with user and account links.
*
* This menu will contain different items depending on service configuration,
* context and whether the user is first- or third-party.
*/
function
UserMenu
({
auth
,
bridge
,
onLogout
,
serviceUrl
,
settings
})
{
const
isThirdParty
=
isThirdPartyUser
(
auth
.
userid
,
settings
.
authDomain
);
const
service
=
serviceConfig
(
settings
);
const
serviceSupports
=
feature
=>
service
&&
!!
service
[
feature
];
const
isSelectableProfile
=
!
isThirdParty
||
serviceSupports
(
'onProfileRequestProvided'
);
const
isLogoutEnabled
=
!
isThirdParty
||
serviceSupports
(
'onLogoutRequestProvided'
);
const
onProfileSelected
=
()
=>
isThirdParty
&&
bridge
.
call
(
bridgeEvents
.
PROFILE_REQUESTED
);
// Generate dynamic props for the profile <MenuItem> component
const
profileItemProps
=
(()
=>
{
const
props
=
{};
if
(
isSelectableProfile
)
{
if
(
!
isThirdParty
)
{
props
.
href
=
serviceUrl
(
'user'
,
{
user
:
auth
.
username
});
}
props
.
onClick
=
onProfileSelected
;
}
return
props
;
})();
const
menuLabel
=
<
i
className
=
"h-icon-account top-bar__btn"
/>
;
return
(
<
div
className
=
"user-menu"
>
<
Menu
label
=
{
menuLabel
}
title
=
{
auth
.
displayName
}
align
=
"right"
>
<
MenuSection
>
<
MenuItem
label
=
{
auth
.
displayName
}
isDisabled
=
{
!
isSelectableProfile
}
{...
profileItemProps
}
/
>
{
!
isThirdParty
&&
(
<
MenuItem
label
=
"Account settings"
href
=
{
serviceUrl
(
'account.settings'
)}
/
>
)}
<
/MenuSection
>
{
isLogoutEnabled
&&
(
<
MenuSection
>
<
MenuItem
label
=
"Log out"
onClick
=
{
onLogout
}
/
>
<
/MenuSection
>
)}
<
/Menu
>
<
/div
>
);
}
UserMenu
.
propTypes
=
{
/* object representing authenticated user and auth status */
auth
:
propTypes
.
object
.
isRequired
,
/* onClick callback for the "log out" button */
onLogout
:
propTypes
.
func
.
isRequired
,
/* services */
bridge
:
propTypes
.
object
.
isRequired
,
serviceUrl
:
propTypes
.
func
.
isRequired
,
settings
:
propTypes
.
object
.
isRequired
,
};
UserMenu
.
injectedProps
=
[
'bridge'
,
'serviceUrl'
,
'settings'
];
module
.
exports
=
withServices
(
UserMenu
);
src/sidebar/index.js
View file @
6489d7e3
...
...
@@ -198,6 +198,10 @@ function startAngularApp(config) {
wrapReactComponent
(
require
(
'./components/timestamp'
))
)
.
component
(
'topBar'
,
require
(
'./components/top-bar'
))
.
component
(
'userMenu'
,
wrapReactComponent
(
require
(
'./components/user-menu'
))
)
.
directive
(
'hAutofocus'
,
require
(
'./directive/h-autofocus'
))
.
directive
(
'hBranding'
,
require
(
'./directive/h-branding'
))
...
...
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