]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8844 Fix project tags selector (#1857)
authorGrégoire Aubert <gregaubert@users.noreply.github.com>
Mon, 27 Mar 2017 10:06:05 +0000 (12:06 +0200)
committerGitHub <noreply@github.com>
Mon, 27 Mar 2017 10:06:05 +0000 (12:06 +0200)
* Limit list size to 10 elements max
* Fix long tags display

server/sonar-web/src/main/js/apps/overview/meta/Meta.js
server/sonar-web/src/main/js/apps/overview/meta/MetaTags.js
server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaTags-test.js
server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.js.snap
server/sonar-web/src/main/js/apps/projects/components/ProjectTagsSelectorContainer.js
server/sonar-web/src/main/js/components/common/MultiSelect.js
server/sonar-web/src/main/js/components/tags/TagsList.css
server/sonar-web/src/main/js/components/tags/TagsSelector.js
server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsSelector-test.js.snap
server/sonar-web/src/main/less/components/menu.less

index 3fdaa3bcdcc1b083847d3e492fe1ebf6e0b3caad..9f67196b36885ecc676b6779072c9ba95d28917f 100644 (file)
@@ -53,7 +53,7 @@ const Meta = ({ component, measures, areThereCustomOrganizations }) => {
 
       <MetaSize component={component} measures={measures} />
 
-      <MetaTags component={component} />
+      {isProject && <MetaTags component={component} />}
 
       {shouldShowQualityGate && <MetaQualityGate gate={qualityGate} />}
 
index 04c6650341fe9ab0ef7ebc78f328c46dacd956ed..61301114fe97b29308fee1b355602780720018d0 100644 (file)
@@ -112,7 +112,6 @@ export default class MetaTags extends React.PureComponent {
             <TagsList
               tags={tags.length ? tags : [translate('no_tags')]}
               allowUpdate={true}
-              allowMultiLine={true}
             />
           </button>
           {popupOpen &&
index eaf3dc5b669b51650fcf122517241f2e1d513a71..85e4c744e92901e10bf4554cb9d3880d8aac2927 100644 (file)
@@ -46,7 +46,6 @@ it('should render with tags and admin rights', () => {
   expect(shallow(<MetaTags component={componentWithTags} />)).toMatchSnapshot();
 });
 
-
 it('should open the tag selector on click', () => {
   const wrapper = shallow(<MetaTags component={componentWithTags} />);
   expect(wrapper).toMatchSnapshot();
index fdf142c3fc851050e3f1c1aac09e780d05eadccd..a92d8b33fb13e754c9f461454731fa11c27fee31 100644 (file)
@@ -5,7 +5,7 @@ exports[`test should open the tag selector on click 1`] = `
     className="button-link"
     onClick={[Function]}>
     <TagsList
-      allowMultiLine={true}
+      allowMultiLine={false}
       allowUpdate={true}
       tags={
         Array [
@@ -24,7 +24,7 @@ exports[`test should open the tag selector on click 2`] = `
     className="button-link"
     onClick={[Function]}>
     <TagsList
-      allowMultiLine={true}
+      allowMultiLine={false}
       allowUpdate={true}
       tags={
         Array [
@@ -59,7 +59,7 @@ exports[`test should open the tag selector on click 3`] = `
     className="button-link"
     onClick={[Function]}>
     <TagsList
-      allowMultiLine={true}
+      allowMultiLine={false}
       allowUpdate={true}
       tags={
         Array [
@@ -78,7 +78,7 @@ exports[`test should render with tags and admin rights 1`] = `
     className="button-link"
     onClick={[Function]}>
     <TagsList
-      allowMultiLine={true}
+      allowMultiLine={false}
       allowUpdate={true}
       tags={
         Array [
index 19f2fa34533de024141d08da48f1f03873af9d2f..fae4f79b62e5941b6528e48fcf55f4beb15b4cfc 100644 (file)
@@ -37,7 +37,7 @@ type State = {
   searchResult: Array<string>
 };
 
-const PAGE_SIZE = 20;
+const LIST_SIZE = 10;
 
 class ProjectTagsSelectorContainer extends React.PureComponent {
   props: Props;
@@ -55,7 +55,10 @@ class ProjectTagsSelectorContainer extends React.PureComponent {
   }
 
   onSearch = (query: string) => {
-    searchProjectTags({ q: query || '', ps: PAGE_SIZE }).then(result => {
+    searchProjectTags({
+      q: query || '',
+      ps: Math.min(this.props.selectedTags.length - 1 + LIST_SIZE, 100)
+    }).then(result => {
       this.setState({
         searchResult: result.tags
       });
@@ -77,6 +80,7 @@ class ProjectTagsSelectorContainer extends React.PureComponent {
         position={this.props.position}
         tags={this.state.searchResult}
         selectedTags={this.props.selectedTags}
+        listSize={LIST_SIZE}
         onSearch={this.onSearch}
         onSelect={this.onSelect}
         onUnselect={this.onUnselect}
index 842695b0732981848993b67b613500c0bb2a0f51..d6ba67ca473debd8eb71ffeb2f42f6f3d5367bd6 100644 (file)
@@ -26,6 +26,7 @@ import { translate } from '../../helpers/l10n';
 type Props = {
   selectedElements: Array<string>,
   elements: Array<string>,
+  listSize: number,
   onSearch: (string) => void,
   onSelect: (string) => void,
   onUnselect: (string) => void,
@@ -51,6 +52,7 @@ export default class MultiSelect extends React.PureComponent {
   };
 
   static defaultProps = {
+    listSize: 10,
     validateSearchInput: (value: string) => value
   };
 
@@ -152,8 +154,17 @@ export default class MultiSelect extends React.PureComponent {
   }
 
   updateUnselectedElements(props: Props) {
-    this.setState({
-      unselectedElements: difference(props.elements, props.selectedElements)
+    this.setState((state: State) => {
+      if (props.listSize < state.selectedElements.length) {
+        return { unselectedElements: [] };
+      } else {
+        return {
+          unselectedElements: difference(props.elements, props.selectedElements).slice(
+            0,
+            props.listSize - state.selectedElements.length
+          )
+        };
+      }
     });
   }
 
index 5963611a6838509cd45df4ca7bf1c749117ff150..886a83ff628346b116ec7c940b0fa75e995fee96 100644 (file)
@@ -1,3 +1,7 @@
+.tags-list {
+  white-space: nowrap;
+}
+
 .tags-list i::before {
   font-size: 12px;
 }
index f43e091981ee8805b3f52c8a9872a590f100de79..e013d23161a420730ab8e4b5444e79435be79439 100644 (file)
@@ -27,6 +27,7 @@ type Props = {
   position: {},
   tags: Array<string>,
   selectedTags: Array<string>,
+  listSize: number,
   onSearch: (string) => void,
   onSelect: (string) => void,
   onUnselect: (string) => void
@@ -45,11 +46,12 @@ export default class TagsSelector extends React.PureComponent {
     return (
       <BubblePopup
         position={this.props.position}
-        customClass="bubble-popup-bottom-right bubble-popup-menu"
+        customClass="bubble-popup-bottom-right bubble-popup-menu abs-width-300"
       >
         <MultiSelect
           elements={this.props.tags}
           selectedElements={this.props.selectedTags}
+          listSize={this.props.listSize}
           onSearch={this.props.onSearch}
           onSelect={this.props.onSelect}
           onUnselect={this.props.onUnselect}
index 56264e5b0bec85f967a0a095175e4aaae1bec648..232e5b51eb5828bc5c4df9021b1ad1ca35e45034 100644 (file)
@@ -1,6 +1,6 @@
 exports[`test should render with selected tags 1`] = `
 <BubblePopup
-  customClass="bubble-popup-bottom-right bubble-popup-menu"
+  customClass="bubble-popup-bottom-right bubble-popup-menu abs-width-300"
   position={
     Object {
       "left": 0,
@@ -15,6 +15,7 @@ exports[`test should render with selected tags 1`] = `
         "baz",
       ]
     }
+    listSize={10}
     onSearch={[Function]}
     onSelect={[Function]}
     onUnselect={[Function]}
@@ -29,7 +30,7 @@ exports[`test should render with selected tags 1`] = `
 
 exports[`test should render without tags at all 1`] = `
 <BubblePopup
-  customClass="bubble-popup-bottom-right bubble-popup-menu"
+  customClass="bubble-popup-bottom-right bubble-popup-menu abs-width-300"
   position={
     Object {
       "left": 0,
@@ -38,6 +39,7 @@ exports[`test should render without tags at all 1`] = `
   }>
   <MultiSelect
     elements={Array []}
+    listSize={10}
     onSearch={[Function]}
     onSelect={[Function]}
     onUnselect={[Function]}
index 75b2b312391bf58119562da9bd185ca470df4cd6..1d75e2cef98d55baa52eb6857f4f9aadcc3eb7ab 100644 (file)
@@ -78,6 +78,8 @@
   padding: 4px 16px 0;
 
   .search-box-input { font-size: @smallFontSize; }
+  
+  .search-box-submit { vertical-align: baseline; }
 }
 
 .menu-search ~ .menu {