Skip to content
Extraits de code Groupes Projets
Valider 2c405aed rédigé par Sorin Davidoi's avatar Sorin Davidoi Validation de Eugen Rochko
Parcourir les fichiers

Performance improvements (#3168)

* refactor(components/status_list): Avoid quering scrollTop if not necessary

* refactor(components/dropdown_menu): Do not render items if not expanded

* refactor: Cherry-pick react-motion imports

* refactor(compose/privacy_dropdown): Do not render options if not open

* refactor(components/column_collapsable): Do not render children if collapsed
parent da0a18a3
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
Affichage de
avec 73 ajouts et 20 suppressions
import React from 'react'; import React from 'react';
import { Motion, spring } from 'react-motion'; import Motion from 'react-motion/lib/Motion';
import spring from 'react-motion/lib/spring';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const Collapsable = ({ fullHeight, isVisible, children }) => ( const Collapsable = ({ fullHeight, isVisible, children }) => (
......
...@@ -36,7 +36,7 @@ class ColumnCollapsable extends React.PureComponent { ...@@ -36,7 +36,7 @@ class ColumnCollapsable extends React.PureComponent {
</div> </div>
<div className='column-collapsable__content' style={{ height: `${fullHeight}px` }}> <div className='column-collapsable__content' style={{ height: `${fullHeight}px` }}>
{children} {!collapsed && children}
</div> </div>
</div> </div>
); );
......
...@@ -21,7 +21,8 @@ class DropdownMenu extends React.PureComponent { ...@@ -21,7 +21,8 @@ class DropdownMenu extends React.PureComponent {
}; };
state = { state = {
direction: 'left' direction: 'left',
expanded: false,
}; };
setRef = (c) => { setRef = (c) => {
...@@ -43,6 +44,10 @@ class DropdownMenu extends React.PureComponent { ...@@ -43,6 +44,10 @@ class DropdownMenu extends React.PureComponent {
this.dropdown.hide(); this.dropdown.hide();
} }
handleShow = () => this.setState({ expanded: true })
handleHide = () => this.setState({ expanded: false })
renderItem = (item, i) => { renderItem = (item, i) => {
if (item === null) { if (item === null) {
return <li key={ 'sep' + i } className='dropdown__sep' />; return <li key={ 'sep' + i } className='dropdown__sep' />;
...@@ -61,18 +66,23 @@ class DropdownMenu extends React.PureComponent { ...@@ -61,18 +66,23 @@ class DropdownMenu extends React.PureComponent {
render () { render () {
const { icon, items, size, direction, ariaLabel } = this.props; const { icon, items, size, direction, ariaLabel } = this.props;
const { expanded } = this.state;
const directionClass = (direction === "left") ? "dropdown__left" : "dropdown__right"; const directionClass = (direction === "left") ? "dropdown__left" : "dropdown__right";
const dropdownItems = expanded && (
<ul className='dropdown__content-list'>
{items.map(this.renderItem)}
</ul>
);
return ( return (
<Dropdown ref={this.setRef}> <Dropdown ref={this.setRef} onShow={this.handleShow} onHide={this.handleHide}>
<DropdownTrigger className='icon-button' style={{ fontSize: `${size}px`, width: `${size}px`, lineHeight: `${size}px` }} aria-label={ariaLabel}> <DropdownTrigger className='icon-button' style={{ fontSize: `${size}px`, width: `${size}px`, lineHeight: `${size}px` }} aria-label={ariaLabel}>
<i className={ `fa fa-fw fa-${icon} dropdown__icon` } aria-hidden={true} /> <i className={ `fa fa-fw fa-${icon} dropdown__icon` } aria-hidden={true} />
</DropdownTrigger> </DropdownTrigger>
<DropdownContent className={directionClass}> <DropdownContent className={directionClass}>
<ul className='dropdown__content-list'> {dropdownItems}
{items.map(this.renderItem)}
</ul>
</DropdownContent> </DropdownContent>
</Dropdown> </Dropdown>
); );
......
import React from 'react'; import React from 'react';
import { Motion, spring } from 'react-motion'; import Motion from 'react-motion/lib/Motion';
import spring from 'react-motion/lib/spring';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
class IconButton extends React.PureComponent { class IconButton extends React.PureComponent {
......
...@@ -46,7 +46,7 @@ class StatusList extends ImmutablePureComponent { ...@@ -46,7 +46,7 @@ class StatusList extends ImmutablePureComponent {
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
if (this.node.scrollTop > 0 && (prevProps.statusIds.size < this.props.statusIds.size && prevProps.statusIds.first() !== this.props.statusIds.first() && !!this._oldScrollPosition)) { if ((prevProps.statusIds.size < this.props.statusIds.size && prevProps.statusIds.first() !== this.props.statusIds.first() && !!this._oldScrollPosition) && this.node.scrollTop > 0) {
this.node.scrollTop = this.node.scrollHeight - this._oldScrollPosition; this.node.scrollTop = this.node.scrollHeight - this._oldScrollPosition;
} }
} }
......
...@@ -5,7 +5,8 @@ import emojify from '../../../emoji'; ...@@ -5,7 +5,8 @@ import emojify from '../../../emoji';
import escapeTextContentForBrowser from 'escape-html'; import escapeTextContentForBrowser from 'escape-html';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import IconButton from '../../../components/icon_button'; import IconButton from '../../../components/icon_button';
import { Motion, spring } from 'react-motion'; import Motion from 'react-motion/lib/Motion';
import spring from 'react-motion/lib/spring';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
......
...@@ -80,7 +80,7 @@ class PrivacyDropdown extends React.PureComponent { ...@@ -80,7 +80,7 @@ class PrivacyDropdown extends React.PureComponent {
<div ref={this.setRef} className={`privacy-dropdown ${open ? 'active' : ''}`}> <div ref={this.setRef} className={`privacy-dropdown ${open ? 'active' : ''}`}>
<div className='privacy-dropdown__value'><IconButton className='privacy-dropdown__value-icon' icon={valueOption.icon} title={intl.formatMessage(messages.change_privacy)} size={18} active={open} inverted onClick={this.handleToggle} style={iconStyle}/></div> <div className='privacy-dropdown__value'><IconButton className='privacy-dropdown__value-icon' icon={valueOption.icon} title={intl.formatMessage(messages.change_privacy)} size={18} active={open} inverted onClick={this.handleToggle} style={iconStyle}/></div>
<div className='privacy-dropdown__dropdown'> <div className='privacy-dropdown__dropdown'>
{options.map(item => {open && options.map(item =>
<div role='button' tabIndex='0' key={item.value} data-index={item.value} onClick={this.handleClick} className={`privacy-dropdown__option ${item.value === value ? 'active' : ''}`}> <div role='button' tabIndex='0' key={item.value} data-index={item.value} onClick={this.handleClick} className={`privacy-dropdown__option ${item.value === value ? 'active' : ''}`}>
<div className='privacy-dropdown__option__icon'><i className={`fa fa-fw fa-${item.icon}`} /></div> <div className='privacy-dropdown__option__icon'><i className={`fa fa-fw fa-${item.icon}`} /></div>
<div className='privacy-dropdown__option__content'> <div className='privacy-dropdown__option__content'>
......
...@@ -4,7 +4,8 @@ import PropTypes from 'prop-types'; ...@@ -4,7 +4,8 @@ import PropTypes from 'prop-types';
import IconButton from '../../../components/icon_button'; import IconButton from '../../../components/icon_button';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
import UploadProgressContainer from '../containers/upload_progress_container'; import UploadProgressContainer from '../containers/upload_progress_container';
import { Motion, spring } from 'react-motion'; import Motion from 'react-motion/lib/Motion';
import spring from 'react-motion/lib/spring';
const messages = defineMessages({ const messages = defineMessages({
undo: { id: 'upload_form.undo', defaultMessage: 'Undo' } undo: { id: 'upload_form.undo', defaultMessage: 'Undo' }
......
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Motion, spring } from 'react-motion'; import Motion from 'react-motion/lib/Motion';
import spring from 'react-motion/lib/spring';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
class UploadProgress extends React.PureComponent { class UploadProgress extends React.PureComponent {
......
...@@ -3,7 +3,8 @@ import { connect } from 'react-redux'; ...@@ -3,7 +3,8 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TextIconButton from '../components/text_icon_button'; import TextIconButton from '../components/text_icon_button';
import { changeComposeSensitivity } from '../../../actions/compose'; import { changeComposeSensitivity } from '../../../actions/compose';
import { Motion, spring } from 'react-motion'; import Motion from 'react-motion/lib/Motion';
import spring from 'react-motion/lib/spring';
import { injectIntl, defineMessages } from 'react-intl'; import { injectIntl, defineMessages } from 'react-intl';
const messages = defineMessages({ const messages = defineMessages({
......
...@@ -8,7 +8,8 @@ import { mountCompose, unmountCompose } from '../../actions/compose'; ...@@ -8,7 +8,8 @@ import { mountCompose, unmountCompose } from '../../actions/compose';
import Link from 'react-router/lib/Link'; import Link from 'react-router/lib/Link';
import { injectIntl, defineMessages } from 'react-intl'; import { injectIntl, defineMessages } from 'react-intl';
import SearchContainer from './containers/search_container'; import SearchContainer from './containers/search_container';
import { Motion, spring } from 'react-motion'; import Motion from 'react-motion/lib/Motion';
import spring from 'react-motion/lib/spring';
import SearchResultsContainer from './containers/search_results_container'; import SearchResultsContainer from './containers/search_results_container';
const messages = defineMessages({ const messages = defineMessages({
......
...@@ -5,7 +5,8 @@ import OnboardingModal from './onboarding_modal'; ...@@ -5,7 +5,8 @@ import OnboardingModal from './onboarding_modal';
import VideoModal from './video_modal'; import VideoModal from './video_modal';
import BoostModal from './boost_modal'; import BoostModal from './boost_modal';
import ConfirmationModal from './confirmation_modal'; import ConfirmationModal from './confirmation_modal';
import { TransitionMotion, spring } from 'react-motion'; import TransitionMotion from 'react-motion/lib/TransitionMotion';
import spring from 'react-motion/lib/spring';
const MODAL_COMPONENTS = { const MODAL_COMPONENTS = {
'MEDIA': MediaModal, 'MEDIA': MediaModal,
......
...@@ -4,7 +4,8 @@ import PropTypes from 'prop-types'; ...@@ -4,7 +4,8 @@ import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import Permalink from '../../../components/permalink'; import Permalink from '../../../components/permalink';
import { TransitionMotion, spring } from 'react-motion'; import TransitionMotion from 'react-motion/lib/TransitionMotion';
import spring from 'react-motion/lib/spring';
import ComposeForm from '../../compose/components/compose_form'; import ComposeForm from '../../compose/components/compose_form';
import Search from '../../compose/components/search'; import Search from '../../compose/components/search';
import NavigationBar from '../../compose/components/navigation_bar'; import NavigationBar from '../../compose/components/navigation_bar';
......
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Motion, spring } from 'react-motion'; import Motion from 'react-motion/lib/Motion';
import spring from 'react-motion/lib/spring';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
class UploadArea extends React.PureComponent { class UploadArea extends React.PureComponent {
......
...@@ -38,7 +38,40 @@ describe('<DropdownMenu />', () => { ...@@ -38,7 +38,40 @@ describe('<DropdownMenu />', () => {
expect(wrapper.find(DropdownTrigger).find('i')).to.have.className(`fa-${icon}`) expect(wrapper.find(DropdownTrigger).find('i')).to.have.className(`fa-${icon}`)
}); });
it('renders list elements for each props.items', () => { it('is not expanded by default', () => {
expect(wrapper.state('expanded')).to.be.equal(false);
})
it('does not render the list elements if not expanded', () => {
const lis = wrapper.find(DropdownContent).find('li');
expect(lis.length).to.be.equal(0);
})
it('sets expanded to true when clicking the trigger', () => {
const wrapper = mount(<DropdownMenu icon={icon} items={items} size={size} />);
wrapper.find(DropdownTrigger).first().simulate('click');
expect(wrapper.state('expanded')).to.be.equal(true);
})
// Error: ReactWrapper::state() can only be called on the root
/*it('sets expanded to false when clicking outside', () => {
const wrapper = mount((
<div>
<DropdownMenu icon={icon} items={items} size={size} />
<span />
</div>
));
wrapper.find(DropdownTrigger).first().simulate('click');
expect(wrapper.find(DropdownMenu).first().state('expanded')).to.be.equal(true);
wrapper.find('span').first().simulate('click');
expect(wrapper.find(DropdownMenu).first().state('expanded')).to.be.equal(false);
})*/
it('renders list elements for each props.items if expanded', () => {
const wrapper = mount(<DropdownMenu icon={icon} items={items} size={size} />);
wrapper.find(DropdownTrigger).first().simulate('click');
const lis = wrapper.find(DropdownContent).find('li'); const lis = wrapper.find(DropdownContent).find('li');
expect(lis.length).to.be.equal(items.length); expect(lis.length).to.be.equal(items.length);
}); });
...@@ -57,7 +90,7 @@ describe('<DropdownMenu />', () => { ...@@ -57,7 +90,7 @@ describe('<DropdownMenu />', () => {
it('uses the action passed in via props.items as click handler', () => { it('uses the action passed in via props.items as click handler', () => {
const wrapper = mount(<DropdownMenu icon={icon} items={items} size={size} />); const wrapper = mount(<DropdownMenu icon={icon} items={items} size={size} />);
wrapper.find(DropdownTrigger).first().simulate('click');
wrapper.find(DropdownContent).find('li a').first().simulate('click'); wrapper.find(DropdownContent).find('li a').first().simulate('click');
expect(action.calledOnce).to.equal(true); expect(action.calledOnce).to.equal(true);
}); });
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter