polish(account-dropdown): Use CSS (vs styles) to hide/show switcher, animate with CSS

This commit is contained in:
Ben Gotow 2015-09-24 19:07:50 -07:00
parent b1ab8ad792
commit db0ce9e63e
4 changed files with 124 additions and 121 deletions

View file

@ -21,59 +21,48 @@ class AccountSwitcher extends React.Component
render: =>
return false unless @state.account
<div id="account-switcher" tabIndex={-1} onBlur={@_onBlur} ref="button">
{@_renderAccount(@state.account, true)}
classnames = ""
classnames += "open" if @state.showing
<div id="account-switcher"
tabIndex={-1}
onBlur={@_onBlur}
ref="button"
className={classnames}>
{@_renderPrimaryItem()}
{@_renderDropdown()}
</div>
_renderAccount: (account, isPrimaryItem) =>
classes = classNames
"account": true
"item": true
"dropdown-item-padding": not isPrimaryItem
"active": account is @state.account
"bg-color-hover": not isPrimaryItem
"primary-item": isPrimaryItem
"account-option": not isPrimaryItem
email = account.emailAddress.trim().toLowerCase()
if isPrimaryItem
dropdownClasses = classNames
"account-switcher-dropdown": true,
"account-switcher-dropdown-hidden": @state.showing
dropdownArrow = <div style={float: 'right', marginTop: -2}>
<RetinaImg className={dropdownClasses} name="account-switcher-dropdown.png"
mode={RetinaImg.Mode.ContentPreserve} />
</div>
onClick = @_toggleDropdown
else
onClick = =>
@_onSwitchAccount account
<div className={classes}
onClick={onClick}
key={email}>
<div style={float: 'left'}>
<div className="gravatar" style={backgroundImage: @_gravatarUrl(email)}></div>
<RetinaImg name={"ic-settings-account-#{account.provider}@2x.png"}
style={width: 28, height: 28, marginTop: -10}
fallback="ic-settings-account-imap.png"
_renderPrimaryItem: =>
<div className="item primary-item" onClick={@_toggleDropdown}>
{@_renderGravatarForAccount(@state.account)}
<div style={float: 'right', marginTop: -2}>
<RetinaImg className="toggle"
name="account-switcher-dropdown.png"
mode={RetinaImg.Mode.ContentPreserve} />
</div>
{dropdownArrow}
<div className="name" style={lineHeight: "110%"}>
{email}
</div>
<div style={clear: "both"}>
{@state.account.emailAddress.trim().toLowerCase()}
</div>
<div style={clear: "both"}></div>
</div>
_renderAccount: (account) =>
email = account.emailAddress.trim().toLowerCase()
classes = classNames
"active": account is @state.account
"item": true
"secondary-item": true
<div className={classes} onClick={ => @_onSwitchAccount(account)} key={email}>
{@_renderGravatarForAccount(account)}
<div className="name" style={lineHeight: "110%"}>{email}</div>
<div style={clear: "both"}></div>
</div>
_renderNewAccountOption: =>
<div className="account item dropdown-item-padding bg-color-hover new-account-option"
<div className="item secondary-item new-account-option"
onClick={@_onAddAccount}
tabIndex={999}>
<div style={float: 'left'}>
@ -85,31 +74,33 @@ class AccountSwitcher extends React.Component
<div className="name" style={lineHeight: "110%", textTransform: 'none'}>
Add account&hellip;
</div>
<div style={clear: "both"}>
</div>
<div style={clear: "both"}></div>
</div>
_renderDropdown: =>
display = if @state.showing then "block" else "none"
# display = "block"
<div className="dropdown">
<div className="inner">
{@state.accounts.map(@_renderAccount)}
{@_renderNewAccountOption()}
</div>
</div>
accounts = @state.accounts.map (a) =>
@_renderAccount(a)
_renderGravatarForAccount: (account) =>
email = account.emailAddress.trim().toLowerCase()
hash = crypto.createHash('md5').update(email, 'utf8').digest('hex')
url = "url(http://www.gravatar.com/avatar/#{hash}?d=blank&s=56)"
<div style={display: display}
ref="account-switcher-dropdown"
className="dropdown dropdown-positioning dropdown-colors">
{accounts}
{@_renderNewAccountOption()}
<div style={float: 'left'}>
<div className="gravatar" style={backgroundImage:url}></div>
<RetinaImg name={"ic-settings-account-#{account.provider}@2x.png"}
style={width: 28, height: 28, marginTop: -10}
fallback="ic-settings-account-imap.png"
mode={RetinaImg.Mode.ContentPreserve} />
</div>
_toggleDropdown: =>
@setState showing: !@state.showing
_gravatarUrl: (email) =>
hash = crypto.createHash('md5').update(email, 'utf8').digest('hex')
"url(http://www.gravatar.com/avatar/#{hash}?d=blank&s=56)"
_onStoreChange: =>
@setState @_getStateFromStores()
@ -132,5 +123,4 @@ class AccountSwitcher extends React.Component
accounts: AccountStore.items()
account: AccountStore.current()
module.exports = AccountSwitcher

View file

@ -22,39 +22,30 @@ describe "AccountSidebar", ->
)
it "doesn't render the dropdown if nothing clicked", ->
dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
dropdownNode = React.findDOMNode dropdown, "account-switcher-dropdown"
openDropdown = TestUtils.scryRenderedDOMComponentsWithClass sidebar, 'open'
expect(openDropdown.length).toBe 0
expect(dropdownNode.style.display).toBe "none"
it "renders the dropdown if clicking the arrow btn", ->
it "shows the dropdown on click", ->
toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
TestUtils.Simulate.click toggler
dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
dropdownNode = React.findDOMNode dropdown, "account-switcher-dropdown"
openDropdown = TestUtils.scryRenderedDOMComponentsWithClass sidebar, 'open'
expect(openDropdown.length).toBe 1
expect(dropdownNode.style.display).toBe "block"
it "removes the dropdown when clicking elsewhere", ->
it "hides the dropdown on blur", ->
toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
TestUtils.Simulate.click toggler
toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
TestUtils.Simulate.blur toggler
dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
dropdownNode = React.findDOMNode dropdown, "account-switcher-dropdown"
openDropdown = TestUtils.scryRenderedDOMComponentsWithClass sidebar, 'open'
expect(openDropdown.length).toBe 0
expect(dropdownNode.style.display).toBe "none"
it "shows all the accounts in the dropdown", ->
it "shows other accounts and the 'Add Account' button", ->
toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
TestUtils.Simulate.click toggler
dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
accounts = TestUtils.scryRenderedDOMComponentsWithClass dropdown, "account-option"
items = TestUtils.scryRenderedDOMComponentsWithClass dropdown, "secondary-item"
newAccountButton = TestUtils.scryRenderedDOMComponentsWithClass dropdown, "new-account-option"
expect(accounts.length).toBe 2
it "shows the 'Add Account' button too", ->
toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
TestUtils.Simulate.click toggler
dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
accounts = TestUtils.scryRenderedDOMComponentsWithClass dropdown, "new-account-option"
expect(accounts.length).toBe 1
expect(items.length).toBe 3
expect(newAccountButton.length).toBe 1

View file

@ -86,7 +86,47 @@
padding-bottom: @padding-base-vertical;
}
.account {
.name {
text-transform: lowercase;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.toggle {
transform: rotateX(0deg);
}
.dropdown {
opacity: 0;
pointer-events: none;
transform:scale(1, 0.2);
transform-origin: top;
transition: transform 125ms cubic-bezier(0.18, 0.89, 0.32, 1.12), opacity 100ms linear;
.inner {
opacity: 0;
transition: opacity 25ms linear;
transition-delay: 0;
}
margin-top: -7px;
background: lighten(@source-list-bg, 5%);
border: 1px solid @border-color-divider;
border-radius: @border-radius-base;
border-top-left-radius: 0;
border-top-right-radius: 0;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.21);
position: absolute;
top: 54px;
width: 100%;
z-index: 999;
.account .gravatar {
top: 6px;
}
}
.item {
position: relative;
margin-bottom: 0;
display:block;
@ -100,41 +140,12 @@
top: -2px;
}
}
.name {
text-transform: lowercase;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.account-switcher-dropdown {
margin-top: -7px;
transform: scale(1, -1);
&.account-switcher-dropdown-hidden {
transform: scale(1, 1);
.secondary-item {
&:hover {
background: @list-hover-bg;
}
}
.dropdown-positioning {
display: block;
position: absolute;
top: 45px;
width: 100%;
z-index: 999;
.account .gravatar {
top: 6px;
}
}
.dropdown-colors {
background: lighten(@source-list-bg, 5%);
border: 1px solid @border-color-divider;
border-radius: @border-radius-base;
box-shadow: @standard-shadow;
}
.dropdown-item-padding {
padding: 6px 5px 0 14px;
&:first-child {
@ -148,11 +159,22 @@
border-bottom-right-radius: @list-border-radius;
}
}
}
.bg-color-hover {
&:hover {
background: @list-hover-bg;
#account-switcher.open {
.dropdown {
opacity: 1;
pointer-events: initial;
transform:scale(1, 1);
transform-origin: top;
.inner {
opacity: 1;
transition: opacity 50ms linear;
transition-delay: 50ms;
}
}
.toggle {
transform: rotateX(180deg);
}
}

View file

@ -138,7 +138,7 @@ describe 'TokenizingTextField', ->
ReactTestUtils.Simulate.dragStart(token, dragStartEvent)
expect(dragStartEventData).toEqual({
'nylas-token-item': '{"client_id":"123","server_id":"2","name":"Nylas Burger Basket","email":"burgers@nylas.com","id":"2","__constructorName":"Contact"}'
'nylas-token-item': '{"client_id":"123","server_id":"2","name":"Nylas Burger Basket","email":"burgers@nylas.com","thirdPartyData":{},"id":"2","__constructorName":"Contact"}'
'text/plain': 'Nylas Burger Basket <burgers@nylas.com>'
})