diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2017-03-09 16:44:28 +0100 |
---|---|---|
committer | Grégoire Aubert <gregaubert@users.noreply.github.com> | 2017-03-10 14:43:39 +0100 |
commit | 788f8da4f8fb7cefd7bb6e72e4df924aea327503 (patch) | |
tree | e7eb67bc3952864802490f1ece6309b616b0d2da /server/sonar-web/src/main/js | |
parent | b0ff07c83a71e67eac12634b730ded55de46352a (diff) | |
download | sonarqube-788f8da4f8fb7cefd7bb6e72e4df924aea327503.tar.gz sonarqube-788f8da4f8fb7cefd7bb6e72e4df924aea327503.zip |
improve web performace of projects page
Diffstat (limited to 'server/sonar-web/src/main/js')
17 files changed, 188 insertions, 148 deletions
diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js index b3540396a2b..cf9378ad610 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js +++ b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js @@ -30,7 +30,7 @@ import LanguageFilter from '../filters/LanguageFilter'; import SearchFilter from '../filters/SearchFilter'; import { translate } from '../../../helpers/l10n'; -export default class PageSidebar extends React.Component { +export default class PageSidebar extends React.PureComponent { static propTypes = { query: React.PropTypes.object.isRequired, isFavorite: React.PropTypes.bool.isRequired, @@ -58,7 +58,7 @@ export default class PageSidebar extends React.Component { <h3>{translate('filters')}</h3> <SearchFilter - query={this.props.query} + query={this.props.query.search} isFavorite={this.props.isFavorite} organization={this.props.organization}/> </div> diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js index a9fa6a95109..6a63633b480 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js @@ -28,7 +28,7 @@ import FavoriteContainer from '../../../components/controls/FavoriteContainer'; import Organization from '../../../components/shared/Organization'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -export default class ProjectCard extends React.Component { +export default class ProjectCard extends React.PureComponent { props: { measures: { [string]: string }, organization?: {}, diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardContainer.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardContainer.js index f03bd24cc57..d3908234509 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardContainer.js +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardContainer.js @@ -21,9 +21,7 @@ import { connect } from 'react-redux'; import ProjectCard from './ProjectCard'; import { getComponent, getComponentMeasures } from '../../../store/rootReducer'; -export default connect( - (state, ownProps) => ({ - project: getComponent(state, ownProps.projectKey), - measures: getComponentMeasures(state, ownProps.projectKey) - }) -)(ProjectCard); +export default connect((state, ownProps) => ({ + project: getComponent(state, ownProps.projectKey), + measures: getComponentMeasures(state, ownProps.projectKey) +}))(ProjectCard); diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardMeasures.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardMeasures.js index 56d4acc7114..94d8c5fdaad 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardMeasures.js +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardMeasures.js @@ -26,7 +26,7 @@ import DuplicationsRating from '../../../components/ui/DuplicationsRating'; import SizeRating from '../../../components/ui/SizeRating'; import { translate } from '../../../helpers/l10n'; -export default class ProjectCardMeasures extends React.Component { +export default class ProjectCardMeasures extends React.PureComponent { static propTypes = { measures: React.PropTypes.object }; diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.js index 46c46d36dc0..cd12fc07ae5 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.js +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.js @@ -21,7 +21,7 @@ import React from 'react'; import Level from '../../../components/ui/Level'; import { translate } from '../../../helpers/l10n'; -export default class ProjectCardQualityGate extends React.Component { +export default class ProjectCardQualityGate extends React.PureComponent { static propTypes = { status: React.PropTypes.string }; diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.js index 06dc8c2537b..93ba8a35c69 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.js +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.js @@ -23,7 +23,7 @@ import NoProjects from './NoProjects'; import NoFavoriteProjects from './NoFavoriteProjects'; import EmptyInstance from './EmptyInstance'; -export default class ProjectsList extends React.Component { +export default class ProjectsList extends React.PureComponent { static propTypes = { projects: React.PropTypes.arrayOf(React.PropTypes.string), isFavorite: React.PropTypes.bool.isRequired, diff --git a/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js index f13699a0807..e6bc17ee881 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js @@ -23,54 +23,61 @@ import SortingFilter from './SortingFilter'; import CoverageRating from '../../../components/ui/CoverageRating'; import { getCoverageRatingLabel, getCoverageRatingAverageValue } from '../../../helpers/ratings'; -export default class CoverageFilter extends React.Component { +export default class CoverageFilter extends React.PureComponent { static propTypes = { query: React.PropTypes.object.isRequired, isFavorite: React.PropTypes.bool, organization: React.PropTypes.object - } + }; property = 'coverage'; renderOption = (option, selected) => { return ( - <span> - <CoverageRating value={getCoverageRatingAverageValue(option)} size="small" muted={!selected}/> - <span className="spacer-left"> - {getCoverageRatingLabel(option)} - </span> + <span> + <CoverageRating + value={getCoverageRatingAverageValue(option)} + size="small" + muted={!selected}/> + <span className="spacer-left"> + {getCoverageRatingLabel(option)} </span> + </span> ); }; renderSort = () => { return ( - <SortingFilter - property={this.property} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization} - sortDesc="right"/> + <SortingFilter + property={this.property} + query={this.props.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization} + sortDesc="right"/> ); - } + }; getFacetValueForOption = (facet, option) => { const map = ['80.0-*', '70.0-80.0', '50.0-70.0', '30.0-50.0', '*-30.0']; return facet[map[option - 1]]; }; + getOptions = () => [1, 2, 3, 4, 5]; + + renderName = () => 'Coverage'; + render () { return ( - <FilterContainer - property={this.property} - getOptions={() => [1, 2, 3, 4, 5]} - renderName={() => 'Coverage'} - renderOption={this.renderOption} - renderSort={this.renderSort} - getFacetValueForOption={this.getFacetValueForOption} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization}/> + <FilterContainer + property={this.property} + getOptions={this.getOptions} + renderName={this.renderName} + renderOption={this.renderOption} + renderSort={this.renderSort} + getFacetValueForOption={this.getFacetValueForOption} + query={this.props.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization}/> ); } } diff --git a/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js index 818e80e6eca..d5faaccd712 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js @@ -21,55 +21,65 @@ import React from 'react'; import FilterContainer from './FilterContainer'; import SortingFilter from './SortingFilter'; import DuplicationsRating from '../../../components/ui/DuplicationsRating'; -import { getDuplicationsRatingLabel, getDuplicationsRatingAverageValue } from '../../../helpers/ratings'; +import { + getDuplicationsRatingLabel, + getDuplicationsRatingAverageValue +} from '../../../helpers/ratings'; -export default class DuplicationsFilter extends React.Component { +export default class DuplicationsFilter extends React.PureComponent { static propTypes = { query: React.PropTypes.object.isRequired, isFavorite: React.PropTypes.bool, organization: React.PropTypes.object - } + }; property = 'duplications'; renderOption = (option, selected) => { return ( - <span> - <DuplicationsRating value={getDuplicationsRatingAverageValue(option)} size="small" muted={!selected}/> - <span className="spacer-left"> - {getDuplicationsRatingLabel(option)} - </span> + <span> + <DuplicationsRating + value={getDuplicationsRatingAverageValue(option)} + size="small" + muted={!selected}/> + <span className="spacer-left"> + {getDuplicationsRatingLabel(option)} </span> + </span> ); }; renderSort = () => { return ( - <SortingFilter - property={this.property} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization}/> + <SortingFilter + property={this.property} + query={this.props.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization}/> ); - } + }; getFacetValueForOption = (facet, option) => { const map = ['*-3.0', '3.0-5.0', '5.0-10.0', '10.0-20.0', '20.0-*']; return facet[map[option - 1]]; }; + getOptions = () => [1, 2, 3, 4, 5]; + + renderName = () => 'Duplications'; + render () { return ( - <FilterContainer - property={this.property} - getOptions={() => [1, 2, 3, 4, 5]} - renderName={() => 'Duplications'} - renderOption={this.renderOption} - renderSort={this.renderSort} - getFacetValueForOption={this.getFacetValueForOption} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization}/> + <FilterContainer + property={this.property} + getOptions={this.getOptions} + renderName={this.renderName} + renderOption={this.renderOption} + renderSort={this.renderSort} + getFacetValueForOption={this.getFacetValueForOption} + query={this.props.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization}/> ); } } diff --git a/server/sonar-web/src/main/js/apps/projects/filters/Filter.js b/server/sonar-web/src/main/js/apps/projects/filters/Filter.js index d8470030565..0c99e74dfff 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/Filter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/Filter.js @@ -24,7 +24,7 @@ import { getFilterUrl } from './utils'; import { formatMeasure } from '../../../helpers/measures'; import { translate } from '../../../helpers/l10n'; -export default class Filter extends React.Component { +export default class Filter extends React.PureComponent { static propTypes = { value: React.PropTypes.any, property: React.PropTypes.string.isRequired, diff --git a/server/sonar-web/src/main/js/apps/projects/filters/FilterContainer.js b/server/sonar-web/src/main/js/apps/projects/filters/FilterContainer.js index d690997ab40..5a9655c9bfe 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/FilterContainer.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/FilterContainer.js @@ -25,7 +25,9 @@ import { getProjectsAppFacetByProperty, getProjectsAppMaxFacetValue } from '../. const mapStateToProps = (state, ownProps) => ({ value: ownProps.query[ownProps.property], facet: getProjectsAppFacetByProperty(state, ownProps.property), - maxFacetValue: getProjectsAppMaxFacetValue(state) + maxFacetValue: getProjectsAppMaxFacetValue(state), + // override query value to avoid re-rendering + query: undefined }); export default connect(mapStateToProps)(withRouter(Filter)); diff --git a/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js index 5f1c9073634..6330edaf39b 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js @@ -22,14 +22,14 @@ import FilterContainer from './FilterContainer'; import SortingFilter from './SortingFilter'; import Rating from '../../../components/ui/Rating'; -export default class IssuesFilter extends React.Component { +export default class IssuesFilter extends React.PureComponent { static propTypes = { property: React.PropTypes.string.isRequired, name: React.PropTypes.string.isRequired, query: React.PropTypes.object.isRequired, isFavorite: React.PropTypes.bool, organization: React.PropTypes.object - } + }; renderOption = (option, selected, value) => { const isUnderSelectedOption = this.highlightUnder(value) && option > value; @@ -48,13 +48,13 @@ export default class IssuesFilter extends React.Component { renderSort = () => { return ( - <SortingFilter - property={this.props.property} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization}/> + <SortingFilter + property={this.props.property} + query={this.props.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization}/> ); - } + }; highlightUnder (option) { return option !== null && option > 1; @@ -64,12 +64,16 @@ export default class IssuesFilter extends React.Component { return facet[option]; }; + getOptions = () => [1, 2, 3, 4, 5]; + + renderName = () => this.props.name; + render () { return ( <FilterContainer property={this.props.property} - getOptions={() => [1, 2, 3, 4, 5]} - renderName={() => this.props.name} + getOptions={this.getOptions} + renderName={this.renderName} renderOption={this.renderOption} renderSort={this.renderSort} highlightUnder={this.highlightUnder} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/LanguageFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/LanguageFilter.js index 549d50eb6ac..b1f45ee06b0 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/LanguageFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/LanguageFilter.js @@ -23,19 +23,17 @@ import FilterContainer from './FilterContainer'; import LanguageFilterOption from './LanguageFilterOption'; import LanguageFilterFooter from './LanguageFilterFooter'; -export default class LanguageFilter extends React.Component { +export default class LanguageFilter extends React.PureComponent { static propTypes = { query: React.PropTypes.object.isRequired, isFavorite: React.PropTypes.bool, organization: React.PropTypes.object - } + }; property = 'languages'; renderOption = option => { - return ( - <LanguageFilterOption languageKey={option}/> - ); + return <LanguageFilterOption languageKey={option}/>; }; getSortedOptions (facet) { @@ -44,26 +42,30 @@ export default class LanguageFilter extends React.Component { renderFooter = () => ( <LanguageFilterFooter - property={this.property} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization}/> + property={this.property} + query={this.props.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization}/> ); getFacetValueForOption = (facet, option) => facet[option]; + getOptions = facet => facet ? this.getSortedOptions(facet) : []; + + renderName = () => 'Languages'; + render () { return ( <FilterContainer - property={this.property} - getOptions={facet => facet ? this.getSortedOptions(facet) : []} - renderName={() => 'Languages'} - renderOption={this.renderOption} - renderFooter={this.renderFooter} - getFacetValueForOption={this.getFacetValueForOption} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization}/> + property={this.property} + getOptions={this.getOptions} + renderName={this.renderName} + renderOption={this.renderOption} + renderFooter={this.renderFooter} + getFacetValueForOption={this.getFacetValueForOption} + query={this.props.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization}/> ); } } diff --git a/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.js index 4adf70bb5e8..62707a38591 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.js @@ -21,28 +21,30 @@ import React from 'react'; import FilterContainer from './FilterContainer'; import Level from '../../../components/ui/Level'; -export default class QualityGateFilter extends React.Component { +export default class QualityGateFilter extends React.PureComponent { renderOption = (option, selected) => { - return ( - <Level level={option} small={true} muted={!selected}/> - ); + return <Level level={option} small={true} muted={!selected}/>; }; getFacetValueForOption = (facet, option) => { return facet[option]; }; + getOptions = () => ['OK', 'WARN', 'ERROR']; + + renderName = () => 'Quality Gate'; + render () { return ( - <FilterContainer - property="gate" - getOptions={() => ['OK', 'WARN', 'ERROR']} - renderName={() => 'Quality Gate'} - renderOption={this.renderOption} - getFacetValueForOption={this.getFacetValueForOption} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization}/> + <FilterContainer + property="gate" + getOptions={this.getOptions} + renderName={this.renderName} + renderOption={this.renderOption} + getFacetValueForOption={this.getFacetValueForOption} + query={this.props.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization}/> ); } } diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.js index 222bfb86f9e..8fe85b240f0 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { withRouter } from 'react-router'; import classNames from 'classnames'; @@ -24,27 +25,31 @@ import debounce from 'lodash/debounce'; import { getFilterUrl } from './utils'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -class SearchFilter extends React.Component { - static propTypes = { - query: React.PropTypes.object.isRequired, - router: React.PropTypes.object.isRequired, - isFavorite: React.PropTypes.bool, - organization: React.PropTypes.object - } +type Props = { + query?: string, + router: { push: (string) => void }, + isFavorite?: boolean, + organization?: {} +}; + +type State = { + userQuery?: string +}; + +class SearchFilter extends React.PureComponent { + handleSearch: (string) => void; + props: Props; + state: State; - constructor (props) { + constructor (props: Props) { super(props); - this.state = { - userQuery: props.query.search - }; + this.state = { userQuery: props.query }; this.handleSearch = debounce(this.handleSearch.bind(this), 250); } - componentWillReceiveProps (nextProps) { - if (this.props.query.search === this.state.userQuery && nextProps.query.search !== this.props.query.search) { - this.setState({ - userQuery: nextProps.query.search || '' - }); + componentWillReceiveProps (nextProps: Props) { + if (this.props.query === this.state.userQuery && nextProps.query !== this.props.query) { + this.setState({ userQuery: nextProps.query || '' }); } } @@ -63,7 +68,7 @@ class SearchFilter extends React.Component { render () { const { userQuery } = this.state; const inputClassName = classNames('input-super-large', { - 'touched': userQuery && userQuery.length < 2 + touched: userQuery && userQuery.length < 2 }); return ( diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js index 0bc240dd12d..53e081a4df0 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js @@ -24,55 +24,65 @@ import SizeRating from '../../../components/ui/SizeRating'; import { translate } from '../../../helpers/l10n'; import { getSizeRatingLabel, getSizeRatingAverageValue } from '../../../helpers/ratings'; -export default class SizeFilter extends React.Component { +export default class SizeFilter extends React.PureComponent { static propTypes = { query: React.PropTypes.object.isRequired, isFavorite: React.PropTypes.bool, organization: React.PropTypes.object - } + }; property = 'size'; renderOption = (option, selected) => { return ( - <span> - <SizeRating value={getSizeRatingAverageValue(option)} small={true} muted={!selected}/> - <span className="spacer-left"> - {getSizeRatingLabel(option)} - </span> + <span> + <SizeRating value={getSizeRatingAverageValue(option)} small={true} muted={!selected}/> + <span className="spacer-left"> + {getSizeRatingLabel(option)} </span> + </span> ); }; renderSort = () => { return ( - <SortingFilter - property={this.property} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization} - leftText={translate('biggest')} - rightText={translate('smallest')}/> + <SortingFilter + property={this.property} + query={this.props.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization} + leftText={translate('biggest')} + rightText={translate('smallest')}/> ); - } + }; getFacetValueForOption = (facet, option) => { - const map = ['*-1000.0', '1000.0-10000.0', '10000.0-100000.0', '100000.0-500000.0', '500000.0-*']; + const map = [ + '*-1000.0', + '1000.0-10000.0', + '10000.0-100000.0', + '100000.0-500000.0', + '500000.0-*' + ]; return facet[map[option - 1]]; }; + getOptions = () => [1, 2, 3, 4, 5]; + + renderName = () => 'Size'; + render () { return ( - <FilterContainer - property={this.property} - getOptions={() => [1, 2, 3, 4, 5]} - renderName={() => 'Size'} - renderOption={this.renderOption} - renderSort={this.renderSort} - getFacetValueForOption={this.getFacetValueForOption} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization}/> + <FilterContainer + property={this.property} + getOptions={this.getOptions} + renderName={this.renderName} + renderOption={this.renderOption} + renderSort={this.renderSort} + getFacetValueForOption={this.getFacetValueForOption} + query={this.props.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization}/> ); } } diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SortingFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/SortingFilter.js index 842a3596f4f..7bf9cd829f8 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/SortingFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/SortingFilter.js @@ -23,7 +23,7 @@ import { Link } from 'react-router'; import { getFilterUrl } from './utils'; import { translate } from '../../../helpers/l10n'; -export default class SortingFilter extends React.Component { +export default class SortingFilter extends React.PureComponent { static propTypes = { property: React.PropTypes.string.isRequired, query: React.PropTypes.object.isRequired, diff --git a/server/sonar-web/src/main/js/components/controls/ListFooter.js b/server/sonar-web/src/main/js/components/controls/ListFooter.js index 504bd99accd..08335b06d60 100644 --- a/server/sonar-web/src/main/js/components/controls/ListFooter.js +++ b/server/sonar-web/src/main/js/components/controls/ListFooter.js @@ -22,7 +22,7 @@ import React from 'react'; import { translate, translateWithParameters } from '../../helpers/l10n'; import { formatMeasure } from '../../helpers/measures'; -export default class ListFooter extends React.Component { +export default class ListFooter extends React.PureComponent { static propTypes = { count: React.PropTypes.number.isRequired, total: React.PropTypes.number.isRequired, |