]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8876 Language facet searchbox on projects page (#1741)
authorAubert Grégoire <gregaubert@users.noreply.github.com>
Mon, 6 Mar 2017 13:42:29 +0000 (14:42 +0100)
committerGitHub <noreply@github.com>
Mon, 6 Mar 2017 13:42:29 +0000 (14:42 +0100)
server/sonar-web/src/main/js/apps/projects/filters/Filter.js
server/sonar-web/src/main/js/apps/projects/filters/LanguageFilter.js
server/sonar-web/src/main/js/apps/projects/filters/LanguageFilterFooter.js [new file with mode: 0644]
server/sonar-web/src/main/less/components/search-navigator.less

index b10b4162d63c24dfb813d785006ded11aa6ec797..5380638906550f8fa5628cbae3a26f4cb7687249 100644 (file)
@@ -33,6 +33,7 @@ export default class Filter extends React.Component {
 
     renderName: React.PropTypes.func.isRequired,
     renderOption: React.PropTypes.func.isRequired,
+    renderFooter: React.PropTypes.func,
 
     getFacetValueForOption: React.PropTypes.func,
 
@@ -130,11 +131,23 @@ export default class Filter extends React.Component {
     }
   }
 
+  renderFooter () {
+    if (!this.props.renderFooter) {
+      return null;
+    }
+    return (
+      <div className="search-navigator-facet-footer projects-facet-footer">
+        {this.props.renderFooter()}
+      </div>
+    );
+  }
+
   render () {
     return (
         <div className="search-navigator-facet-box" data-key={this.props.property}>
           {this.renderHeader()}
           {this.renderOptions()}
+          {this.renderFooter()}
         </div>
     );
   }
index 4e3e10921f517a819cac7b9439bcb29697f33d70..0e3d5e51706f7a5d6f241ab836f890b45fb66af5 100644 (file)
@@ -21,6 +21,7 @@ import React from 'react';
 import sortBy from 'lodash/sortBy';
 import FilterContainer from './FilterContainer';
 import LanguageFilterOption from './LanguageFilterOption';
+import LanguageFilterFooter from './LanguageFilterFooter';
 
 export default class LanguageFilter extends React.Component {
   static propTypes = {
@@ -39,6 +40,14 @@ export default class LanguageFilter extends React.Component {
     return sortBy(Object.keys(facet), [option => -facet[option]]);
   }
 
+  renderFooter = () => (
+    <LanguageFilterFooter
+        property="language"
+        query={this.props.query}
+        isFavorite={this.props.isFavorite}
+        organization={this.props.organization}/>
+  );
+
   getFacetValueForOption = (facet, option) => facet[option];
 
   render () {
@@ -48,6 +57,7 @@ export default class LanguageFilter extends React.Component {
           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}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/LanguageFilterFooter.js b/server/sonar-web/src/main/js/apps/projects/filters/LanguageFilterFooter.js
new file mode 100644 (file)
index 0000000..9db6ebe
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import React from 'react';
+import { connect } from 'react-redux';
+import { withRouter } from 'react-router';
+import Select from 'react-select';
+import difference from 'lodash/difference';
+import isNil from 'lodash/isNil';
+import omitBy from 'lodash/omitBy';
+import { getProjectsAppFacetByProperty, getLanguages } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
+
+class LanguageFilterFooter extends React.Component {
+  static propTypes = {
+    property: React.PropTypes.string.isRequired,
+    query: React.PropTypes.object.isRequired,
+    isFavorite: React.PropTypes.bool,
+    organization: React.PropTypes.object,
+    languages: React.PropTypes.object,
+    value: React.PropTypes.any,
+    facet: React.PropTypes.object,
+    getFilterUrl: React.PropTypes.func.isRequired
+  }
+
+  handleLanguageChange = ({ value }) => {
+    const urlOptions = (this.props.value || []).concat(value).join(',');
+    const path = this.props.getFilterUrl({ [this.props.property]: urlOptions });
+    this.props.router.push(path);
+  }
+
+  getOptions () {
+    const { languages, facet } = this.props;
+    let options = Object.keys(languages);
+    if (facet) {
+      options = difference(options, Object.keys(facet));
+    }
+    return options.map(key => ({ label: languages[key].name, value: key }));
+  }
+
+  render () {
+    return (
+      <Select
+          onChange={this.handleLanguageChange}
+          className="input-super-large"
+          options={this.getOptions()}
+          placeholder={translate('search_verb')}
+          clearable={false}
+          searchable={true}/>
+    );
+  }
+}
+
+const mapStateToProps = (state, ownProps) => ({
+  languages: getLanguages(state),
+  value: ownProps.query[ownProps.property],
+  facet: getProjectsAppFacetByProperty(state, ownProps.property),
+  getFilterUrl: part => {
+    const basePathName = ownProps.organization ?
+        `/organizations/${ownProps.organization.key}/projects` :
+        '/projects';
+    const pathname = basePathName + (ownProps.isFavorite ? '/favorite' : '');
+    const query = omitBy({ ...ownProps.query, ...part }, isNil);
+    return { pathname, query };
+  }
+});
+
+export default connect(mapStateToProps)(withRouter(LanguageFilterFooter));
index 0a17c8ac4dcc6ed587c81cd0177ba966f7cb49c9..197bdfef290c8c08f9de5552dea520aa9cf12559 100644 (file)
   &:hover {
     border: 1px solid @blue;
   }
-  
+
   .facet-name {
     line-height: 16px;
     background-color: @barBackgroundColor;
   white-space: nowrap;
 }
 
+.search-navigator-facet-footer {
+  display: block;
+  padding: 6px 10px;
+  border-bottom: none;
+}
+
 .search-navigator-facet-list-align-right {
 
   .facet-name {
   margin-right: 4px;
   border-bottom: none;
   color: @baseFontColor;
-  
+
   > .icon-list {
     position: relative;
     top: -2px;
-  } 
+  }
 }
 
 .search-navigator-filters-actions {