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
		
		Reference in a new issue