Refactor Avatar and AvatarOverlay to have 'account' as prop instead of src and staticSrc (#4526)
* Refactored Avatar and AvatarOverlay (DRY) to have 'account' as prop. Also removed animate attribute from compose navigation bar, which should have never been there. Added test for avatar overlay. * fix broken tests * god dammit another bug in tests! travis please let this pass * formatting in avatar overlay
This commit is contained in:
		
					parent
					
						
							
								22db947225
							
						
					
				
			
			
				commit
				
					
						5942347407
					
				
			
		
					 13 changed files with 82 additions and 23 deletions
				
			
		|  | @ -70,7 +70,7 @@ export default class Account extends ImmutablePureComponent { | |||
|       <div className='account'> | ||||
|         <div className='account__wrapper'> | ||||
|           <Permalink key={account.get('id')} className='account__display-name' href={account.get('url')} to={`/accounts/${account.get('id')}`}> | ||||
|             <div className='account__avatar-wrapper'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={36} /></div> | ||||
|             <div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div> | ||||
|             <DisplayName account={account} /> | ||||
|           </Permalink> | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,11 +1,11 @@ | |||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 
 | ||||
| export default class Avatar extends React.PureComponent { | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     src: PropTypes.string.isRequired, | ||||
|     staticSrc: PropTypes.string, | ||||
|     account: ImmutablePropTypes.map.isRequired, | ||||
|     size: PropTypes.number.isRequired, | ||||
|     style: PropTypes.object, | ||||
|     animate: PropTypes.bool, | ||||
|  | @ -33,9 +33,12 @@ export default class Avatar extends React.PureComponent { | |||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { src, size, staticSrc, animate, inline } = this.props; | ||||
|     const { account, size, animate, inline } = this.props; | ||||
|     const { hovering } = this.state; | ||||
| 
 | ||||
|     const src = account.get('avatar'); | ||||
|     const staticSrc = account.get('avatar_static'); | ||||
| 
 | ||||
|     let className = 'account__avatar'; | ||||
| 
 | ||||
|     if (inline) { | ||||
|  |  | |||
|  | @ -1,22 +1,22 @@ | |||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 
 | ||||
| export default class AvatarOverlay extends React.PureComponent { | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     staticSrc: PropTypes.string.isRequired, | ||||
|     overlaySrc: PropTypes.string.isRequired, | ||||
|     account: ImmutablePropTypes.map.isRequired, | ||||
|     friend: ImmutablePropTypes.map.isRequired, | ||||
|   }; | ||||
| 
 | ||||
|   render() { | ||||
|     const { staticSrc, overlaySrc } = this.props; | ||||
|     const { account, friend } = this.props; | ||||
| 
 | ||||
|     const baseStyle = { | ||||
|       backgroundImage: `url(${staticSrc})`, | ||||
|       backgroundImage: `url(${account.get('avatar_static')})`, | ||||
|     }; | ||||
| 
 | ||||
|     const overlayStyle = { | ||||
|       backgroundImage: `url(${overlaySrc})`, | ||||
|       backgroundImage: `url(${friend.get('avatar_static')})`, | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|  |  | |||
|  | @ -228,9 +228,9 @@ export default class Status extends ImmutablePureComponent { | |||
|     } | ||||
| 
 | ||||
|     if (account === undefined || account === null) { | ||||
|       statusAvatar = <Avatar src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} size={48} />; | ||||
|       statusAvatar = <Avatar account={status.get('account')} size={48} />; | ||||
|     }else{ | ||||
|       statusAvatar = <AvatarOverlay staticSrc={status.getIn(['account', 'avatar_static'])} overlaySrc={account.get('avatar_static')} />; | ||||
|       statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />; | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ export default class AutosuggestAccount extends ImmutablePureComponent { | |||
| 
 | ||||
|     return ( | ||||
|       <div className='autosuggest-account'> | ||||
|         <div className='autosuggest-account-icon'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={18} /></div> | ||||
|         <div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div> | ||||
|         <DisplayName account={account} /> | ||||
|       </div> | ||||
|     ); | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ export default class NavigationBar extends ImmutablePureComponent { | |||
|       <div className='navigation-bar'> | ||||
|         <Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}> | ||||
|           <span style={{ display: 'none' }}>{this.props.account.get('acct')}</span> | ||||
|           <Avatar src={this.props.account.get('avatar')} animate size={40} /> | ||||
|           <Avatar account={this.props.account} size={40} /> | ||||
|         </Permalink> | ||||
| 
 | ||||
|         <div className='navigation-bar__profile'> | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ export default class ReplyIndicator extends ImmutablePureComponent { | |||
|           <div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} /></div> | ||||
| 
 | ||||
|           <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='reply-indicator__display-name'> | ||||
|             <div className='reply-indicator__display-avatar'><Avatar size={24} src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} /></div> | ||||
|             <div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div> | ||||
|             <DisplayName account={status.get('account')} /> | ||||
|           </a> | ||||
|         </div> | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ export default class AccountAuthorize extends ImmutablePureComponent { | |||
|       <div className='account-authorize__wrapper'> | ||||
|         <div className='account-authorize'> | ||||
|           <Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'> | ||||
|             <div className='account-authorize__avatar'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={48} /></div> | ||||
|             <div className='account-authorize__avatar'><Avatar account={account} size={48} /></div> | ||||
|             <DisplayName account={account} /> | ||||
|           </Permalink> | ||||
| 
 | ||||
|  |  | |||
|  | @ -59,7 +59,7 @@ export default class DetailedStatus extends ImmutablePureComponent { | |||
|     return ( | ||||
|       <div className='detailed-status'> | ||||
|         <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'> | ||||
|           <div className='detailed-status__display-avatar'><Avatar src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} size={48} /></div> | ||||
|           <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div> | ||||
|           <DisplayName account={status.get('account')} /> | ||||
|         </a> | ||||
| 
 | ||||
|  |  | |||
|  | @ -46,7 +46,7 @@ export default class ActionsModal extends ImmutablePureComponent { | |||
| 
 | ||||
|           <a href={this.props.status.getIn(['account', 'url'])} className='status__display-name'> | ||||
|             <div className='status__avatar'> | ||||
|               <Avatar src={this.props.status.getIn(['account', 'avatar'])} staticSrc={this.props.status.getIn(['account', 'avatar_static'])} size={48} /> | ||||
|               <Avatar account={this.props.status.get('account')} size={48} /> | ||||
|             </div> | ||||
| 
 | ||||
|             <DisplayName account={this.props.status.get('account')} /> | ||||
|  |  | |||
|  | @ -62,7 +62,7 @@ export default class BoostModal extends ImmutablePureComponent { | |||
| 
 | ||||
|               <a onClick={this.handleAccountClick} href={status.getIn(['account', 'url'])} className='status__display-name'> | ||||
|                 <div className='status__avatar'> | ||||
|                   <Avatar src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} size={48} /> | ||||
|                   <Avatar account={status.get('account')} size={48} /> | ||||
|                 </div> | ||||
| 
 | ||||
|                 <DisplayName account={status.get('account')} /> | ||||
|  |  | |||
|  | @ -1,20 +1,42 @@ | |||
| import { expect } from 'chai'; | ||||
| import { render } from 'enzyme'; | ||||
| import { fromJS }  from 'immutable'; | ||||
| import React from 'react'; | ||||
| import Avatar from '../../../app/javascript/mastodon/components/avatar'; | ||||
| 
 | ||||
| describe('<Avatar />', () => { | ||||
|   const src = '/path/to/image.jpg'; | ||||
|   const account = fromJS({ | ||||
|     username: 'alice', | ||||
|     acct: 'alice', | ||||
|     display_name: 'Alice', | ||||
|     avatar: '/animated/alice.gif', | ||||
|     avatar_static: '/static/alice.jpg', | ||||
|   }); | ||||
|   const size = 100; | ||||
|   const wrapper = render(<Avatar src={src} animate size={size} />); | ||||
|   const animated = render(<Avatar account={account} animate size={size} />); | ||||
|   const still = render(<Avatar account={account} size={size} />); | ||||
| 
 | ||||
|   // Autoplay
 | ||||
|   it('renders a div element with the given src as background', () => { | ||||
|     expect(wrapper.find('div')).to.have.style('background-image', `url(${src})`); | ||||
|     expect(animated.find('div')).to.have.style('background-image', `url(${account.get('avatar')})`); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders a div element of the given size', () => { | ||||
|     ['width', 'height'].map((attr) => { | ||||
|       expect(wrapper.find('div')).to.have.style(attr, `${size}px`); | ||||
|       expect(animated.find('div')).to.have.style(attr, `${size}px`); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   // Still
 | ||||
|   it('renders a div element with the given static src as background if not autoplay', () => { | ||||
|     expect(still.find('div')).to.have.style('background-image', `url(${account.get('avatar_static')})`); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders a div element of the given size if not autoplay', () => { | ||||
|     ['width', 'height'].map((attr) => { | ||||
|       expect(still.find('div')).to.have.style(attr, `${size}px`); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   // TODO add autoplay test if possible
 | ||||
| }); | ||||
|  |  | |||
							
								
								
									
										34
									
								
								spec/javascript/components/avatar_overlay.test.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								spec/javascript/components/avatar_overlay.test.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| import { expect } from 'chai'; | ||||
| import { render } from 'enzyme'; | ||||
| import { fromJS }  from 'immutable'; | ||||
| import React from 'react'; | ||||
| import AvatarOverlay from '../../../app/javascript/mastodon/components/avatar_overlay'; | ||||
| 
 | ||||
| describe('<Avatar />', () => { | ||||
|   const account = fromJS({ | ||||
|     username: 'alice', | ||||
|     acct: 'alice', | ||||
|     display_name: 'Alice', | ||||
|     avatar: '/animated/alice.gif', | ||||
|     avatar_static: '/static/alice.jpg', | ||||
|   }); | ||||
|   const friend = fromJS({ | ||||
|     username: 'eve', | ||||
|     acct: 'eve@blackhat.lair', | ||||
|     display_name: 'Evelyn', | ||||
|     avatar: '/animated/eve.gif', | ||||
|     avatar_static: '/static/eve.jpg', | ||||
|   }); | ||||
| 
 | ||||
|   const overlay = render(<AvatarOverlay account={account} friend={friend} />); | ||||
| 
 | ||||
|   it('renders account static src as base of overlay avatar', () => { | ||||
|     expect(overlay.find('.account__avatar-overlay-base')) | ||||
|       .to.have.style('background-image', `url(${account.get('avatar_static')})`); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders friend static src as overlay of overlay avatar', () => { | ||||
|     expect(overlay.find('.account__avatar-overlay-overlay')) | ||||
|       .to.have.style('background-image', `url(${friend.get('avatar_static')})`); | ||||
|   }); | ||||
| }); | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue