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 overlaygh/stable
parent
22db947225
commit
5942347407
|
@ -70,7 +70,7 @@ export default class Account extends ImmutablePureComponent {
|
||||||
<div className='account'>
|
<div className='account'>
|
||||||
<div className='account__wrapper'>
|
<div className='account__wrapper'>
|
||||||
<Permalink key={account.get('id')} className='account__display-name' href={account.get('url')} to={`/accounts/${account.get('id')}`}>
|
<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} />
|
<DisplayName account={account} />
|
||||||
</Permalink>
|
</Permalink>
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
export default class Avatar extends React.PureComponent {
|
export default class Avatar extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
src: PropTypes.string.isRequired,
|
account: ImmutablePropTypes.map.isRequired,
|
||||||
staticSrc: PropTypes.string,
|
|
||||||
size: PropTypes.number.isRequired,
|
size: PropTypes.number.isRequired,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
animate: PropTypes.bool,
|
animate: PropTypes.bool,
|
||||||
|
@ -33,9 +33,12 @@ export default class Avatar extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { src, size, staticSrc, animate, inline } = this.props;
|
const { account, size, animate, inline } = this.props;
|
||||||
const { hovering } = this.state;
|
const { hovering } = this.state;
|
||||||
|
|
||||||
|
const src = account.get('avatar');
|
||||||
|
const staticSrc = account.get('avatar_static');
|
||||||
|
|
||||||
let className = 'account__avatar';
|
let className = 'account__avatar';
|
||||||
|
|
||||||
if (inline) {
|
if (inline) {
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
export default class AvatarOverlay extends React.PureComponent {
|
export default class AvatarOverlay extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
staticSrc: PropTypes.string.isRequired,
|
account: ImmutablePropTypes.map.isRequired,
|
||||||
overlaySrc: PropTypes.string.isRequired,
|
friend: ImmutablePropTypes.map.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { staticSrc, overlaySrc } = this.props;
|
const { account, friend } = this.props;
|
||||||
|
|
||||||
const baseStyle = {
|
const baseStyle = {
|
||||||
backgroundImage: `url(${staticSrc})`,
|
backgroundImage: `url(${account.get('avatar_static')})`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const overlayStyle = {
|
const overlayStyle = {
|
||||||
backgroundImage: `url(${overlaySrc})`,
|
backgroundImage: `url(${friend.get('avatar_static')})`,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -228,9 +228,9 @@ export default class Status extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account === undefined || account === null) {
|
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{
|
}else{
|
||||||
statusAvatar = <AvatarOverlay staticSrc={status.getIn(['account', 'avatar_static'])} overlaySrc={account.get('avatar_static')} />;
|
statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default class AutosuggestAccount extends ImmutablePureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='autosuggest-account'>
|
<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} />
|
<DisplayName account={account} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,7 +19,7 @@ export default class NavigationBar extends ImmutablePureComponent {
|
||||||
<div className='navigation-bar'>
|
<div className='navigation-bar'>
|
||||||
<Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
|
<Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
|
||||||
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
|
<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>
|
</Permalink>
|
||||||
|
|
||||||
<div className='navigation-bar__profile'>
|
<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>
|
<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'>
|
<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')} />
|
<DisplayName account={status.get('account')} />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,7 +32,7 @@ export default class AccountAuthorize extends ImmutablePureComponent {
|
||||||
<div className='account-authorize__wrapper'>
|
<div className='account-authorize__wrapper'>
|
||||||
<div className='account-authorize'>
|
<div className='account-authorize'>
|
||||||
<Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
|
<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} />
|
<DisplayName account={account} />
|
||||||
</Permalink>
|
</Permalink>
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||||
return (
|
return (
|
||||||
<div className='detailed-status'>
|
<div className='detailed-status'>
|
||||||
<a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
|
<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')} />
|
<DisplayName account={status.get('account')} />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ export default class ActionsModal extends ImmutablePureComponent {
|
||||||
|
|
||||||
<a href={this.props.status.getIn(['account', 'url'])} className='status__display-name'>
|
<a href={this.props.status.getIn(['account', 'url'])} className='status__display-name'>
|
||||||
<div className='status__avatar'>
|
<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>
|
</div>
|
||||||
|
|
||||||
<DisplayName account={this.props.status.get('account')} />
|
<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'>
|
<a onClick={this.handleAccountClick} href={status.getIn(['account', 'url'])} className='status__display-name'>
|
||||||
<div className='status__avatar'>
|
<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>
|
</div>
|
||||||
|
|
||||||
<DisplayName account={status.get('account')} />
|
<DisplayName account={status.get('account')} />
|
||||||
|
|
|
@ -1,20 +1,42 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { render } from 'enzyme';
|
import { render } from 'enzyme';
|
||||||
|
import { fromJS } from 'immutable';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Avatar from '../../../app/javascript/mastodon/components/avatar';
|
import Avatar from '../../../app/javascript/mastodon/components/avatar';
|
||||||
|
|
||||||
describe('<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 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', () => {
|
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', () => {
|
it('renders a div element of the given size', () => {
|
||||||
['width', 'height'].map((attr) => {
|
['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
|
||||||
});
|
});
|
||||||
|
|
|
@ -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')})`);
|
||||||
|
});
|
||||||
|
});
|
Reference in New Issue