]> source.dussan.org Git - sonarqube.git/commitdiff
Generalize facet components for both issues and measures
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Thu, 27 Jul 2017 10:18:59 +0000 (12:18 +0200)
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>
Mon, 14 Aug 2017 09:44:44 +0000 (11:44 +0200)
71 files changed:
server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js
server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetBox.js [deleted file]
server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetHeader.js [deleted file]
server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItem.js [deleted file]
server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItemMeasureValue.js [deleted file]
server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItemsList.js [deleted file]
server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetMeasureValue.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.js
server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetHeader-test.js [deleted file]
server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetItem-test.js [deleted file]
server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetItemMeasureValue-test.js [deleted file]
server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetMeasureValue-test.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/DomainFacet-test.js.snap
server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetHeader-test.js.snap [deleted file]
server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetItem-test.js.snap [deleted file]
server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetItemMeasureValue-test.js.snap [deleted file]
server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetMeasureValue-test.js.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.js
server/sonar-web/src/main/js/apps/issues/components/SearchSelect.js [deleted file]
server/sonar-web/src/main/js/apps/issues/components/__tests__/SearchSelect-test.js [deleted file]
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/SearchSelect-test.js.snap [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/AuthorFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/DirectoryFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/FacetMode.js
server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/LanguageFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/ModuleFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/ProjectFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/ResolutionFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/RuleFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/TagFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.js
server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.js.snap
server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetBox.js [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetFooter.js [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetHeader.js [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetItem.js [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetItemsList.js [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetBox-test.js [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetFooter-test.js [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetHeader-test.js [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetItem-test.js [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetItemsList-test.js [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetBox-test.js.snap [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetFooter-test.js.snap [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetHeader-test.js.snap [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetItem-test.js.snap [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetItemsList-test.js.snap [deleted file]
server/sonar-web/src/main/js/apps/issues/utils.js
server/sonar-web/src/main/js/components/controls/SearchSelect.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/controls/__tests__/SearchSelect-test.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/SearchSelect-test.js.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/FacetBox.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/FacetFooter.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/FacetHeader.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/FacetItem.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/FacetItemsList.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/FacetBox-test.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/FacetFooter-test.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/FacetHeader-test.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/FacetItem-test.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/FacetItemsList-test.js [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetBox-test.js.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetFooter-test.js.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetHeader-test.js.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetItem-test.js.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetItemsList-test.js.snap [new file with mode: 0644]

index ac2083422d1792d519850c0065b86dcf0b08d328..c7e8dd68b2141b55bde4ed8d529c674f2613772b 100644 (file)
  */
 // @flow
 import React from 'react';
-import FacetBox from './FacetBox';
-import FacetHeader from './FacetHeader';
-import FacetItem from './FacetItem';
-import FacetItemsList from './FacetItemsList';
-import FacetItemMeasureValue from './FacetItemMeasureValue';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
+import FacetMeasureValue from './FacetMeasureValue';
 import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
 import Tooltip from '../../../components/controls/Tooltip';
 import { filterMeasures, sortMeasures } from '../utils';
@@ -71,7 +71,7 @@ export default class DomainFacet extends React.PureComponent {
                   </Tooltip>
                 }
                 onClick={this.props.onChange}
-                stat={<FacetItemMeasureValue measure={measure} />}
+                stat={<FacetMeasureValue measure={measure} />}
                 value={measure.metric.key}
               />
             )}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetBox.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetBox.js
deleted file mode 100644 (file)
index 92b7afb..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-
-type Props = {|
-  children?: React.Element<*>
-|};
-
-export default function FacetBox(props: Props) {
-  return (
-    <div className="search-navigator-facet-box">
-      {props.children}
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetHeader.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetHeader.js
deleted file mode 100644 (file)
index 2cc9d19..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.
- */
-// @flow
-/* eslint-disable max-len */
-import React from 'react';
-
-type Props = {|
-  name: string,
-  onClick?: () => void,
-  open: boolean,
-  values?: number
-|};
-
-export default class FacetHeader extends React.PureComponent {
-  props: Props;
-
-  static defaultProps = {
-    open: true
-  };
-
-  handleClick = (event: Event & { currentTarget: HTMLElement }) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    if (this.props.onClick) {
-      this.props.onClick();
-    }
-  };
-
-  renderCheckbox() {
-    return (
-      <svg viewBox="0 0 1792 1792" width="10" height="10" style={{ paddingTop: 3 }}>
-        {this.props.open
-          ? <path
-              style={{ fill: 'currentColor ' }}
-              d="M1683 808l-742 741q-19 19-45 19t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"
-            />
-          : <path
-              style={{ fill: 'currentColor ' }}
-              d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"
-            />}
-      </svg>
-    );
-  }
-
-  renderValueIndicator() {
-    if (this.props.open || !this.props.values) {
-      return null;
-    }
-    return (
-      <span className="spacer-left badge is-rounded">
-        {this.props.values}
-      </span>
-    );
-  }
-
-  render() {
-    return (
-      <div>
-        {this.props.onClick
-          ? <a className="search-navigator-facet-header" href="#" onClick={this.handleClick}>
-              {this.renderCheckbox()} {this.props.name} {this.renderValueIndicator()}
-            </a>
-          : <span className="search-navigator-facet-header">
-              {this.props.name}
-            </span>}
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItem.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItem.js
deleted file mode 100644 (file)
index e84480b..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import classNames from 'classnames';
-
-type Props = {|
-  active: boolean,
-  disabled: boolean,
-  halfWidth: boolean,
-  name: string | React.Element<*>,
-  onClick: string => void,
-  stat: string | React.Element<*>,
-  value: string
-|};
-
-export default class FacetItem extends React.PureComponent {
-  props: Props;
-
-  static defaultProps = {
-    disabled: false,
-    halfWidth: false
-  };
-
-  handleClick = (event: Event & { currentTarget: HTMLElement }) => {
-    event.preventDefault();
-    this.props.onClick(this.props.value);
-  };
-
-  render() {
-    const className = classNames('facet', 'search-navigator-facet', {
-      active: this.props.active,
-      'search-navigator-facet-half': this.props.halfWidth
-    });
-
-    return this.props.disabled
-      ? <span className={className}>
-          <span className="facet-name">
-            {this.props.name}
-          </span>
-          <span className="facet-stat">
-            {this.props.stat}
-          </span>
-        </span>
-      : <a className={className} href="#" onClick={this.handleClick}>
-          <span className="facet-name">
-            {this.props.name}
-          </span>
-          <span className="facet-stat">
-            {this.props.stat}
-          </span>
-        </a>;
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItemMeasureValue.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItemMeasureValue.js
deleted file mode 100644 (file)
index ede45d9..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import Measure from '../../../components/measure/Measure';
-import { isDiffMetric } from '../../../helpers/measures';
-import type { MeasureEnhanced } from '../../../components/measure/types';
-
-export default function FacetItemMeasureValue({ measure }: { measure: MeasureEnhanced }) {
-  if (isDiffMetric(measure.metric.key)) {
-    return (
-      <div
-        id={`measure-${measure.metric.key}-leak`}
-        className="domain-measures-value domain-measures-leak">
-        <Measure measure={measure} />
-      </div>
-    );
-  }
-
-  return (
-    <div id={`measure-${measure.metric.key}-value`} className="domain-measures-value">
-      <Measure measure={measure} />
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItemsList.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItemsList.js
deleted file mode 100644 (file)
index 5d36d99..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-
-type Props = {|
-  children?: Array<React.Element<*>>
-|};
-
-export default function FacetItemsList(props: Props) {
-  return (
-    <div className="search-navigator-facet-list">
-      {props.children}
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetMeasureValue.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetMeasureValue.js
new file mode 100644 (file)
index 0000000..eae569c
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+import Measure from '../../../components/measure/Measure';
+import { isDiffMetric } from '../../../helpers/measures';
+import type { MeasureEnhanced } from '../../../components/measure/types';
+
+export default function FacetMeasureValue({ measure }: { measure: MeasureEnhanced }) {
+  if (isDiffMetric(measure.metric.key)) {
+    return (
+      <div
+        id={`measure-${measure.metric.key}-leak`}
+        className="domain-measures-value domain-measures-leak">
+        <Measure measure={measure} />
+      </div>
+    );
+  }
+
+  return (
+    <div id={`measure-${measure.metric.key}-value`} className="domain-measures-value">
+      <Measure measure={measure} />
+    </div>
+  );
+}
index f707eb462a52bf83a769fadb72bac284d2a1990f..58c0326c9dcd757ddf109e53f0061ad35f0b5dd6 100644 (file)
@@ -31,8 +31,7 @@ type Props = {|
 |};
 
 type State = {|
-  closedFacets: { [string]: boolean },
-  measuresByDomains: Array<{ name: string, measures: Array<MeasureEnhanced> }>
+  closedFacets: { [string]: boolean }
 |};
 
 export default class Sidebar extends React.PureComponent {
@@ -41,16 +40,7 @@ export default class Sidebar extends React.PureComponent {
 
   constructor(props: Props) {
     super(props);
-    this.state = {
-      closedFacets: {},
-      measuresByDomains: groupByDomains(props.measures)
-    };
-  }
-
-  componentWillReceiveProps(nextProps: Props) {
-    if (nextProps.measures !== this.props.measures) {
-      this.setState({ measuresByDomains: groupByDomains(nextProps.measures) });
-    }
+    this.state = { closedFacets: {} };
   }
 
   toggleFacet = (name: string) => {
@@ -62,10 +52,9 @@ export default class Sidebar extends React.PureComponent {
   changeMetric = (metric: string) => this.props.updateQuery({ metric });
 
   render() {
-    const { measuresByDomains } = this.state;
     return (
       <div className="search-navigator-facets-list">
-        {measuresByDomains.map(domain =>
+        {groupByDomains(this.props.measures).map(domain =>
           <DomainFacet
             key={domain.name}
             domain={domain}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetHeader-test.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetHeader-test.js
deleted file mode 100644 (file)
index 0fe3347..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import { click } from '../../../../helpers/testUtils';
-import FacetHeader from '../FacetHeader';
-
-it('should render open facet with value', () => {
-  expect(
-    shallow(<FacetHeader name="foo" onClick={jest.fn()} open={true} values={1} />)
-  ).toMatchSnapshot();
-});
-
-it('should render open facet without value', () => {
-  expect(shallow(<FacetHeader name="foo" onClick={jest.fn()} open={true} />)).toMatchSnapshot();
-});
-
-it('should render closed facet with value', () => {
-  expect(
-    shallow(<FacetHeader name="foo" onClick={jest.fn()} open={false} values={1} />)
-  ).toMatchSnapshot();
-});
-
-it('should render closed facet without value', () => {
-  expect(shallow(<FacetHeader name="foo" onClick={jest.fn()} open={false} />)).toMatchSnapshot();
-});
-
-it('should render without link', () => {
-  expect(shallow(<FacetHeader name="foo" open={false} />)).toMatchSnapshot();
-});
-
-it('should call onClick', () => {
-  const onClick = jest.fn();
-  const wrapper = shallow(<FacetHeader name="foo" onClick={onClick} open={false} />);
-  click(wrapper.find('a'));
-  expect(onClick).toHaveBeenCalled();
-});
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetItem-test.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetItem-test.js
deleted file mode 100644 (file)
index e8c8bfe..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import { click } from '../../../../helpers/testUtils';
-import FacetItem from '../FacetItem';
-
-const renderFacetItem = (props: {}) =>
-  shallow(
-    <FacetItem active={false} name="foo" onClick={jest.fn()} stat={''} value="bar" {...props} />
-  );
-
-it('should render active', () => {
-  expect(renderFacetItem({ active: true })).toMatchSnapshot();
-});
-
-it('should render inactive', () => {
-  expect(renderFacetItem({ active: false })).toMatchSnapshot();
-});
-
-it('should render stat', () => {
-  expect(renderFacetItem({ stat: 13 })).toMatchSnapshot();
-});
-
-it('should render disabled', () => {
-  expect(renderFacetItem({ disabled: true })).toMatchSnapshot();
-});
-
-it('should render half width', () => {
-  expect(renderFacetItem({ halfWidth: true })).toMatchSnapshot();
-});
-
-it('should render effort stat', () => {
-  expect(renderFacetItem({ facetMode: 'effort', stat: 1234 })).toMatchSnapshot();
-});
-
-it('should call onClick', () => {
-  const onClick = jest.fn();
-  const wrapper = renderFacetItem({ onClick });
-  click(wrapper, { currentTarget: { dataset: { value: 'bar' } } });
-  expect(onClick).toHaveBeenCalled();
-});
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetItemMeasureValue-test.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetItemMeasureValue-test.js
deleted file mode 100644 (file)
index d2a6ec5..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import FacetItemMeasureValue from '../FacetItemMeasureValue';
-
-const MEASURE = {
-  metric: {
-    key: 'bugs',
-    type: 'INT',
-    name: 'Bugs',
-    domain: 'Reliability'
-  },
-  value: '5',
-  periods: [{ index: 1, value: '5' }],
-  leak: '5'
-};
-const LEAK_MEASURE = {
-  metric: {
-    key: 'new_bugs',
-    type: 'INT',
-    name: 'New Bugs',
-    domain: 'Reliability'
-  },
-  periods: [{ index: 1, value: '5' }],
-  leak: '5'
-};
-
-it('should display measure value', () => {
-  expect(shallow(<FacetItemMeasureValue measure={MEASURE} />)).toMatchSnapshot();
-});
-
-it('should display leak measure value', () => {
-  expect(shallow(<FacetItemMeasureValue measure={LEAK_MEASURE} />)).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetMeasureValue-test.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetMeasureValue-test.js
new file mode 100644 (file)
index 0000000..b92096c
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+import { shallow } from 'enzyme';
+import FacetMeasureValue from '../FacetMeasureValue';
+
+const MEASURE = {
+  metric: {
+    key: 'bugs',
+    type: 'INT',
+    name: 'Bugs',
+    domain: 'Reliability'
+  },
+  value: '5',
+  periods: [{ index: 1, value: '5' }],
+  leak: '5'
+};
+const LEAK_MEASURE = {
+  metric: {
+    key: 'new_bugs',
+    type: 'INT',
+    name: 'New Bugs',
+    domain: 'Reliability'
+  },
+  periods: [{ index: 1, value: '5' }],
+  leak: '5'
+};
+
+it('should display measure value', () => {
+  expect(shallow(<FacetMeasureValue measure={MEASURE} />)).toMatchSnapshot();
+});
+
+it('should display leak measure value', () => {
+  expect(shallow(<FacetMeasureValue measure={LEAK_MEASURE} />)).toMatchSnapshot();
+});
index 6ca87d36a3fb9970fbe5ddc32ca6d0f63c8b9bb3..a55b82c48d78d7200cd7b57af1283ffcc7425c7d 100644 (file)
@@ -32,7 +32,7 @@ exports[`should display facet item list 1`] = `
       }
       onClick={[Function]}
       stat={
-        <FacetItemMeasureValue
+        <FacetMeasureValue
           measure={
             Object {
               "leak": "5",
@@ -78,7 +78,7 @@ exports[`should display facet item list 1`] = `
       }
       onClick={[Function]}
       stat={
-        <FacetItemMeasureValue
+        <FacetMeasureValue
           measure={
             Object {
               "leak": "5",
@@ -136,7 +136,7 @@ exports[`should display facet item list with bugs selected 1`] = `
       }
       onClick={[Function]}
       stat={
-        <FacetItemMeasureValue
+        <FacetMeasureValue
           measure={
             Object {
               "leak": "5",
@@ -182,7 +182,7 @@ exports[`should display facet item list with bugs selected 1`] = `
       }
       onClick={[Function]}
       stat={
-        <FacetItemMeasureValue
+        <FacetMeasureValue
           measure={
             Object {
               "leak": "5",
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetHeader-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetHeader-test.js.snap
deleted file mode 100644 (file)
index bd6c0e5..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render closed facet with value 1`] = `
-<div>
-  <a
-    className="search-navigator-facet-header"
-    href="#"
-    onClick={[Function]}
-  >
-    <svg
-      height="10"
-      style={
-        Object {
-          "paddingTop": 3,
-        }
-      }
-      viewBox="0 0 1792 1792"
-      width="10"
-    >
-      <path
-        d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"
-        style={
-          Object {
-            "fill": "currentColor ",
-          }
-        }
-      />
-    </svg>
-     
-    foo
-     
-    <span
-      className="spacer-left badge is-rounded"
-    >
-      1
-    </span>
-  </a>
-</div>
-`;
-
-exports[`should render closed facet without value 1`] = `
-<div>
-  <a
-    className="search-navigator-facet-header"
-    href="#"
-    onClick={[Function]}
-  >
-    <svg
-      height="10"
-      style={
-        Object {
-          "paddingTop": 3,
-        }
-      }
-      viewBox="0 0 1792 1792"
-      width="10"
-    >
-      <path
-        d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"
-        style={
-          Object {
-            "fill": "currentColor ",
-          }
-        }
-      />
-    </svg>
-     
-    foo
-     
-  </a>
-</div>
-`;
-
-exports[`should render open facet with value 1`] = `
-<div>
-  <a
-    className="search-navigator-facet-header"
-    href="#"
-    onClick={[Function]}
-  >
-    <svg
-      height="10"
-      style={
-        Object {
-          "paddingTop": 3,
-        }
-      }
-      viewBox="0 0 1792 1792"
-      width="10"
-    >
-      <path
-        d="M1683 808l-742 741q-19 19-45 19t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"
-        style={
-          Object {
-            "fill": "currentColor ",
-          }
-        }
-      />
-    </svg>
-     
-    foo
-     
-  </a>
-</div>
-`;
-
-exports[`should render open facet without value 1`] = `
-<div>
-  <a
-    className="search-navigator-facet-header"
-    href="#"
-    onClick={[Function]}
-  >
-    <svg
-      height="10"
-      style={
-        Object {
-          "paddingTop": 3,
-        }
-      }
-      viewBox="0 0 1792 1792"
-      width="10"
-    >
-      <path
-        d="M1683 808l-742 741q-19 19-45 19t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"
-        style={
-          Object {
-            "fill": "currentColor ",
-          }
-        }
-      />
-    </svg>
-     
-    foo
-     
-  </a>
-</div>
-`;
-
-exports[`should render without link 1`] = `
-<div>
-  <span
-    className="search-navigator-facet-header"
-  >
-    foo
-  </span>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetItem-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetItem-test.js.snap
deleted file mode 100644 (file)
index 82e72d3..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render active 1`] = `
-<a
-  className="facet search-navigator-facet active"
-  href="#"
-  onClick={[Function]}
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-  <span
-    className="facet-stat"
-  />
-</a>
-`;
-
-exports[`should render disabled 1`] = `
-<span
-  className="facet search-navigator-facet"
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-  <span
-    className="facet-stat"
-  />
-</span>
-`;
-
-exports[`should render effort stat 1`] = `
-<a
-  className="facet search-navigator-facet"
-  href="#"
-  onClick={[Function]}
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-  <span
-    className="facet-stat"
-  >
-    1234
-  </span>
-</a>
-`;
-
-exports[`should render half width 1`] = `
-<a
-  className="facet search-navigator-facet search-navigator-facet-half"
-  href="#"
-  onClick={[Function]}
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-  <span
-    className="facet-stat"
-  />
-</a>
-`;
-
-exports[`should render inactive 1`] = `
-<a
-  className="facet search-navigator-facet"
-  href="#"
-  onClick={[Function]}
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-  <span
-    className="facet-stat"
-  />
-</a>
-`;
-
-exports[`should render stat 1`] = `
-<a
-  className="facet search-navigator-facet"
-  href="#"
-  onClick={[Function]}
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-  <span
-    className="facet-stat"
-  >
-    13
-  </span>
-</a>
-`;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetItemMeasureValue-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetItemMeasureValue-test.js.snap
deleted file mode 100644 (file)
index 9bd6a3e..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display leak measure value 1`] = `
-<div
-  className="domain-measures-value domain-measures-leak"
-  id="measure-new_bugs-leak"
->
-  <Measure
-    measure={
-      Object {
-        "leak": "5",
-        "metric": Object {
-          "domain": "Reliability",
-          "key": "new_bugs",
-          "name": "New Bugs",
-          "type": "INT",
-        },
-        "periods": Array [
-          Object {
-            "index": 1,
-            "value": "5",
-          },
-        ],
-      }
-    }
-  />
-</div>
-`;
-
-exports[`should display measure value 1`] = `
-<div
-  className="domain-measures-value"
-  id="measure-bugs-value"
->
-  <Measure
-    measure={
-      Object {
-        "leak": "5",
-        "metric": Object {
-          "domain": "Reliability",
-          "key": "bugs",
-          "name": "Bugs",
-          "type": "INT",
-        },
-        "periods": Array [
-          Object {
-            "index": 1,
-            "value": "5",
-          },
-        ],
-        "value": "5",
-      }
-    }
-  />
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetMeasureValue-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetMeasureValue-test.js.snap
new file mode 100644 (file)
index 0000000..9bd6a3e
--- /dev/null
@@ -0,0 +1,56 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display leak measure value 1`] = `
+<div
+  className="domain-measures-value domain-measures-leak"
+  id="measure-new_bugs-leak"
+>
+  <Measure
+    measure={
+      Object {
+        "leak": "5",
+        "metric": Object {
+          "domain": "Reliability",
+          "key": "new_bugs",
+          "name": "New Bugs",
+          "type": "INT",
+        },
+        "periods": Array [
+          Object {
+            "index": 1,
+            "value": "5",
+          },
+        ],
+      }
+    }
+  />
+</div>
+`;
+
+exports[`should display measure value 1`] = `
+<div
+  className="domain-measures-value"
+  id="measure-bugs-value"
+>
+  <Measure
+    measure={
+      Object {
+        "leak": "5",
+        "metric": Object {
+          "domain": "Reliability",
+          "key": "bugs",
+          "name": "Bugs",
+          "type": "INT",
+        },
+        "periods": Array [
+          Object {
+            "index": 1,
+            "value": "5",
+          },
+        ],
+        "value": "5",
+      }
+    }
+  />
+</div>
+`;
index 4340f1670bf8f6387bc56954456492a92285ae9d..250c8ed2a6dbd4c3916ff6600a241a006fcf1384 100644 (file)
@@ -22,7 +22,7 @@ import React from 'react';
 import Modal from 'react-modal';
 import Select from 'react-select';
 import { pickBy, sortBy } from 'lodash';
-import SearchSelect from './SearchSelect';
+import SearchSelect from '../../../components/controls/SearchSelect';
 import Checkbox from '../../../components/controls/Checkbox';
 import Tooltip from '../../../components/controls/Tooltip';
 import MarkdownTips from '../../../components/common/MarkdownTips';
diff --git a/server/sonar-web/src/main/js/apps/issues/components/SearchSelect.js b/server/sonar-web/src/main/js/apps/issues/components/SearchSelect.js
deleted file mode 100644 (file)
index 7d95fdd..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import Select from 'react-select';
-import { debounce } from 'lodash';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-
-type Option = { label: string, value: string };
-
-type Props = {|
-  autofocus: boolean,
-  minimumQueryLength: number,
-  onSearch: (query: string) => Promise<Array<Option>>,
-  onSelect: (value: string) => void,
-  renderOption?: (option: Object) => React.Element<*>,
-  resetOnBlur: boolean,
-  value?: string
-|};
-
-type State = {
-  loading: boolean,
-  options: Array<Option>,
-  query: string
-};
-
-export default class SearchSelect extends React.PureComponent {
-  mounted: boolean;
-  props: Props;
-  state: State;
-
-  static defaultProps = {
-    autofocus: true,
-    minimumQueryLength: 2,
-    resetOnBlur: true
-  };
-
-  constructor(props: Props) {
-    super(props);
-    this.state = { loading: false, options: [], query: '' };
-    this.search = debounce(this.search, 250);
-  }
-
-  componentDidMount() {
-    this.mounted = true;
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  search = (query: string) => {
-    this.props.onSearch(query).then(options => {
-      if (this.mounted) {
-        this.setState({ loading: false, options });
-      }
-    });
-  };
-
-  handleBlur = () => {
-    this.setState({ options: [], query: '' });
-  };
-
-  handleChange = (option: Option) => {
-    this.props.onSelect(option.value);
-  };
-
-  handleInputChange = (query: string = '') => {
-    if (query.length >= this.props.minimumQueryLength) {
-      this.setState({ loading: true, query });
-      this.search(query);
-    } else {
-      this.setState({ options: [], query });
-    }
-  };
-
-  // disable internal filtering
-  handleFilterOption = () => true;
-
-  render() {
-    return (
-      <Select
-        autofocus={this.props.autofocus}
-        cache={false}
-        className="input-super-large"
-        clearable={false}
-        filterOption={this.handleFilterOption}
-        isLoading={this.state.loading}
-        noResultsText={
-          this.state.query.length < this.props.minimumQueryLength
-            ? translateWithParameters('select2.tooShort', this.props.minimumQueryLength)
-            : translate('select2.noMatches')
-        }
-        onBlur={this.props.resetOnBlur ? this.handleBlur : undefined}
-        onChange={this.handleChange}
-        onInputChange={this.handleInputChange}
-        onOpen={this.props.minimumQueryLength === 0 ? this.handleInputChange : undefined}
-        optionRenderer={this.props.renderOption}
-        options={this.state.options}
-        placeholder={translate('search_verb')}
-        searchable={true}
-        value={this.props.value}
-        valueRenderer={this.props.renderOption}
-      />
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/SearchSelect-test.js b/server/sonar-web/src/main/js/apps/issues/components/__tests__/SearchSelect-test.js
deleted file mode 100644 (file)
index f4d46d4..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import SearchSelect from '../SearchSelect';
-
-jest.mock('lodash', () => ({
-  debounce: fn => fn
-}));
-
-it('should render Select', () => {
-  expect(shallow(<SearchSelect onSearch={jest.fn()} onSelect={jest.fn()} />)).toMatchSnapshot();
-});
-
-it('should call onSelect', () => {
-  const onSelect = jest.fn();
-  const wrapper = shallow(<SearchSelect onSearch={jest.fn()} onSelect={onSelect} />);
-  wrapper.prop('onChange')({ value: 'foo' });
-  expect(onSelect).lastCalledWith('foo');
-});
-
-it('should call onSearch', () => {
-  const onSearch = jest.fn().mockReturnValue(Promise.resolve([]));
-  const wrapper = shallow(
-    <SearchSelect minimumQueryLength={2} onSearch={onSearch} onSelect={jest.fn()} />
-  );
-  wrapper.prop('onInputChange')('f');
-  expect(onSearch).not.toHaveBeenCalled();
-  wrapper.prop('onInputChange')('foo');
-  expect(onSearch).lastCalledWith('foo');
-});
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/SearchSelect-test.js.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/SearchSelect-test.js.snap
deleted file mode 100644 (file)
index d3ea2ed..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render Select 1`] = `
-<Select
-  addLabelText="Add \\"{label}\\"?"
-  arrowRenderer={[Function]}
-  autofocus={true}
-  autosize={true}
-  backspaceRemoves={true}
-  backspaceToRemoveMessage="Press backspace to remove {label}"
-  cache={false}
-  className="input-super-large"
-  clearAllText="Clear all"
-  clearRenderer={[Function]}
-  clearValueText="Clear value"
-  clearable={false}
-  deleteRemoves={true}
-  delimiter=","
-  disabled={false}
-  escapeClearsValue={true}
-  filterOption={[Function]}
-  filterOptions={[Function]}
-  ignoreAccents={true}
-  ignoreCase={true}
-  inputProps={Object {}}
-  isLoading={false}
-  joinValues={false}
-  labelKey="label"
-  matchPos="any"
-  matchProp="any"
-  menuBuffer={0}
-  menuRenderer={[Function]}
-  multi={false}
-  noResultsText="select2.tooShort.2"
-  onBlur={[Function]}
-  onBlurResetsInput={true}
-  onChange={[Function]}
-  onCloseResetsInput={true}
-  onInputChange={[Function]}
-  optionComponent={[Function]}
-  options={Array []}
-  pageSize={5}
-  placeholder="search_verb"
-  required={false}
-  scrollMenuIntoView={true}
-  searchable={true}
-  simpleValue={false}
-  tabSelectsValue={true}
-  valueComponent={[Function]}
-  valueKey="value"
-/>
-`;
index 20b3387486fced66f50060ae568ae303bc0e9f36..94407c286b6a0ac33815544a4f9825b0f1d13f1e 100644 (file)
 import React from 'react';
 import { sortBy, uniq, without } from 'lodash';
 import Avatar from '../../../components/ui/Avatar';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
-import FacetFooter from './components/FacetFooter';
-import { searchAssignees } from '../utils';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
+import FacetFooter from '../../../components/facet/FacetFooter';
+import { searchAssignees, formatFacetStat } from '../utils';
 import { translate } from '../../../helpers/l10n';
 import type { ReferencedUser, Component } from '../utils';
 
@@ -154,11 +154,10 @@ export default class AssigneeFacet extends React.PureComponent {
         {assignees.map(assignee =>
           <FacetItem
             active={this.isAssigneeActive(assignee)}
-            facetMode={this.props.facetMode}
             key={assignee}
             name={this.getAssigneeName(assignee)}
             onClick={this.handleItemClick}
-            stat={this.getStat(assignee)}
+            stat={formatFacetStat(this.getStat(assignee), this.props.facetMode)}
             value={assignee}
           />
         )}
@@ -182,7 +181,7 @@ export default class AssigneeFacet extends React.PureComponent {
 
   render() {
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index 96f81dce317352de42c738ab304bacdcdc87bc7b..8cb5c1f05ab87f06915303b8c4327e4c3d15b66a 100644 (file)
 // @flow
 import React from 'react';
 import { sortBy, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
+import { formatFacetStat } from '../utils';
 import { translate } from '../../../helpers/l10n';
 
 type Props = {|
@@ -79,11 +80,10 @@ export default class AuthorFacet extends React.PureComponent {
         {authors.map(author =>
           <FacetItem
             active={this.props.authors.includes(author)}
-            facetMode={this.props.facetMode}
             key={author}
             name={author}
             onClick={this.handleItemClick}
-            stat={this.getStat(author)}
+            stat={formatFacetStat(this.getStat(author), this.props.facetMode)}
             value={author}
           />
         )}
@@ -93,7 +93,7 @@ export default class AuthorFacet extends React.PureComponent {
 
   render() {
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index ab491dd167b5d34452f7274e34054bc4fadf3b16..f7cc13735a93206b44d5e9f0aec7eb8a2b438085 100644 (file)
@@ -21,9 +21,9 @@
 import React from 'react';
 import moment from 'moment';
 import { max } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
 import { BarChart } from '../../../components/charts/bar-chart';
 import DateInput from '../../../components/controls/DateInput';
 import { translate } from '../../../helpers/l10n';
@@ -212,46 +212,36 @@ export default class CreationDateFacet extends React.PureComponent {
       <div className="spacer-top issues-predefined-periods">
         <FacetItem
           active={!this.hasValue()}
-          facetMode=""
           name={translate('issues.facet.createdAt.all')}
           onClick={this.handlePeriodClick}
-          stat={null}
           value=""
         />
         {component == null &&
           <FacetItem
             active={createdInLast === '1w'}
-            facetMode=""
             name={translate('issues.facet.createdAt.last_week')}
             onClick={this.handlePeriodClick}
-            stat={null}
             value="1w"
           />}
         {component == null &&
           <FacetItem
             active={createdInLast === '1m'}
-            facetMode=""
             name={translate('issues.facet.createdAt.last_month')}
             onClick={this.handlePeriodClick}
-            stat={null}
             value="1m"
           />}
         {component == null &&
           <FacetItem
             active={createdInLast === '1y'}
-            facetMode=""
             name={translate('issues.facet.createdAt.last_year')}
             onClick={this.handlePeriodClick}
-            stat={null}
             value="1y"
           />}
         {component != null &&
           <FacetItem
             active={sinceLeakPeriod}
-            facetMode=""
             name={translate('issues.leak_period')}
             onClick={this.handleLeakPeriodClick}
-            stat={null}
             value=""
           />}
       </div>
@@ -271,7 +261,7 @@ export default class CreationDateFacet extends React.PureComponent {
 
   render() {
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index 13cf51d17d71699debd702eb8ba79244c4bd1b55..35d8b70b2ac914afe5cab66da8b4b4b76dfeeac8 100644 (file)
 // @flow
 import React from 'react';
 import { sortBy, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
-import type { ReferencedComponent } from '../utils';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
 import QualifierIcon from '../../../components/shared/QualifierIcon';
 import { translate } from '../../../helpers/l10n';
+import { formatFacetStat } from '../utils';
+import type { ReferencedComponent } from '../utils';
 
 type Props = {|
   facetMode: string,
@@ -100,11 +101,10 @@ export default class DirectoryFacet extends React.PureComponent {
         {directories.map(directory =>
           <FacetItem
             active={this.props.directories.includes(directory)}
-            facetMode={this.props.facetMode}
             key={directory}
             name={this.renderName(directory)}
             onClick={this.handleItemClick}
-            stat={this.getStat(directory)}
+            stat={formatFacetStat(this.getStat(directory), this.props.facetMode)}
             value={directory}
           />
         )}
@@ -114,7 +114,7 @@ export default class DirectoryFacet extends React.PureComponent {
 
   render() {
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index 1c178bc887339bc5d1c4d6e7b924803691387860..64419abdf81d5543799c1d7b725f9220ffb9f0ae 100644 (file)
  */
 // @flow
 import React from 'react';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
 import { translate } from '../../../helpers/l10n';
 
 type Props = {|
@@ -44,19 +44,17 @@ export default class FacetMode extends React.PureComponent {
     const modes = ['count', 'effort'];
 
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader name={translate('issues.facet.mode')} />
 
         <FacetItemsList>
           {modes.map(mode =>
             <FacetItem
               active={facetMode === mode}
-              facetMode={this.props.facetMode}
               halfWidth={true}
               key={mode}
               name={translate('issues.facet.mode', mode)}
               onClick={this.handleItemClick}
-              stat={null}
               value={mode}
             />
           )}
index b6404f3fd3b347b4d156067e15772c0b1f28b49e..33af07a42122c1210f912e75a2967511d4cd2b26 100644 (file)
 // @flow
 import React from 'react';
 import { sortBy, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
-import type { ReferencedComponent } from '../utils';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
 import QualifierIcon from '../../../components/shared/QualifierIcon';
 import { translate } from '../../../helpers/l10n';
 import { collapsePath } from '../../../helpers/path';
+import { formatFacetStat } from '../utils';
+import type { ReferencedComponent } from '../utils';
 
 type Props = {|
   facetMode: string,
@@ -96,11 +97,10 @@ export default class FileFacet extends React.PureComponent {
         {files.map(file =>
           <FacetItem
             active={this.props.files.includes(file)}
-            facetMode={this.props.facetMode}
             key={file}
             name={this.renderName(file)}
             onClick={this.handleItemClick}
-            stat={this.getStat(file)}
+            stat={formatFacetStat(this.getStat(file), this.props.facetMode)}
             value={file}
           />
         )}
@@ -110,7 +110,7 @@ export default class FileFacet extends React.PureComponent {
 
   render() {
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index ec7ba416c86a303a1c40f80637b2f407c0008bc3..37406c5f00ee82095f2ee4a8e9c52a1afbd3adc8 100644 (file)
 // @flow
 import React from 'react';
 import { sortBy, uniq, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
 import LanguageFacetFooter from './LanguageFacetFooter';
-import type { ReferencedLanguage } from '../utils';
 import { translate } from '../../../helpers/l10n';
+import { formatFacetStat } from '../utils';
+import type { ReferencedLanguage } from '../utils';
 
 type Props = {|
   facetMode: string,
@@ -92,11 +93,10 @@ export default class LanguageFacet extends React.PureComponent {
         {languages.map(language =>
           <FacetItem
             active={this.props.languages.includes(language)}
-            facetMode={this.props.facetMode}
             key={language}
             name={this.getLanguageName(language)}
             onClick={this.handleItemClick}
-            stat={this.getStat(language)}
+            stat={formatFacetStat(this.getStat(language), this.props.facetMode)}
             value={language}
           />
         )}
@@ -114,7 +114,7 @@ export default class LanguageFacet extends React.PureComponent {
 
   render() {
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index c7eb7c8dc26badef25791c990dbb90759c1cc11f..8f7df010f83082987cd23198d2f3bf64b5ef0a04 100644 (file)
 // @flow
 import React from 'react';
 import { sortBy, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
-import type { ReferencedComponent } from '../utils';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
 import QualifierIcon from '../../../components/shared/QualifierIcon';
 import { translate } from '../../../helpers/l10n';
+import { formatFacetStat } from '../utils';
+import type { ReferencedComponent } from '../utils';
 
 type Props = {|
   facetMode: string,
@@ -93,11 +94,10 @@ export default class ModuleFacet extends React.PureComponent {
         {modules.map(module =>
           <FacetItem
             active={this.props.modules.includes(module)}
-            facetMode={this.props.facetMode}
             key={module}
             name={this.renderName(module)}
             onClick={this.handleItemClick}
-            stat={this.getStat(module)}
+            stat={formatFacetStat(this.getStat(module), this.props.facetMode)}
             value={module}
           />
         )}
@@ -107,7 +107,7 @@ export default class ModuleFacet extends React.PureComponent {
 
   render() {
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index 62a1af46084dac89dd57121d589ed59838707aa9..dbbf3f4784bfc58dc70c4cb276a84f535031bdf0 100644 (file)
 // @flow
 import React from 'react';
 import { sortBy, uniq, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
-import FacetFooter from './components/FacetFooter';
-import type { ReferencedComponent, Component } from '../utils';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
+import FacetFooter from '../../../components/facet/FacetFooter';
 import Organization from '../../../components/shared/Organization';
 import QualifierIcon from '../../../components/shared/QualifierIcon';
 import { searchProjects, getTree } from '../../../api/components';
 import { translate } from '../../../helpers/l10n';
+import { formatFacetStat } from '../utils';
+import type { ReferencedComponent, Component } from '../utils';
 
 type Props = {|
   component?: Component,
@@ -144,11 +145,10 @@ export default class ProjectFacet extends React.PureComponent {
         {projects.map(project =>
           <FacetItem
             active={this.props.projects.includes(project)}
-            facetMode={this.props.facetMode}
             key={project}
             name={this.renderName(project)}
             onClick={this.handleItemClick}
-            stat={this.getStat(project)}
+            stat={formatFacetStat(this.getStat(project), this.props.facetMode)}
             value={project}
           />
         )}
@@ -173,7 +173,7 @@ export default class ProjectFacet extends React.PureComponent {
 
   render() {
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index c905c98c2b8a87b40d551accd9af848b40727c21..c6a949b3fb5503a494663a862e4442b13eb3f436 100644 (file)
 // @flow
 import React from 'react';
 import { orderBy, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
 import { translate } from '../../../helpers/l10n';
+import { formatFacetStat } from '../utils';
 
 type Props = {|
   facetMode: string,
@@ -90,12 +91,11 @@ export default class ResolutionFacet extends React.PureComponent {
       <FacetItem
         active={active}
         disabled={stat === 0 && !active}
-        facetMode={this.props.facetMode}
         key={resolution}
         halfWidth={true}
         name={this.getFacetItemName(resolution)}
         onClick={this.handleItemClick}
-        stat={stat}
+        stat={formatFacetStat(stat, this.props.facetMode)}
         value={resolution}
       />
     );
@@ -105,7 +105,7 @@ export default class ResolutionFacet extends React.PureComponent {
     const resolutions = ['', 'FIXED', 'FALSE-POSITIVE', 'WONTFIX', 'REMOVED'];
 
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index d4d322eeb6d9b8fe11e5e40d258838cd7fe43da2..9cb922314ee09c5640c634f29d88e92b9261b2ed 100644 (file)
 // @flow
 import React from 'react';
 import { sortBy, uniq, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
-import FacetFooter from './components/FacetFooter';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
+import FacetFooter from '../../../components/facet/FacetFooter';
 import { searchRules } from '../../../api/rules';
 import { translate } from '../../../helpers/l10n';
+import { formatFacetStat } from '../utils';
 
 type Props = {|
   facetMode: string,
@@ -104,11 +105,10 @@ export default class RuleFacet extends React.PureComponent {
         {rules.map(rule =>
           <FacetItem
             active={this.props.rules.includes(rule)}
-            facetMode={this.props.facetMode}
             key={rule}
             name={this.getRuleName(rule)}
             onClick={this.handleItemClick}
-            stat={this.getStat(rule)}
+            stat={formatFacetStat(this.getStat(rule), this.props.facetMode)}
             value={rule}
           />
         )}
@@ -126,7 +126,7 @@ export default class RuleFacet extends React.PureComponent {
 
   render() {
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index 8b4edb49672b46d27b8ad24bf873400024270f01..69765762b8ed4d3b140fc84f48bf0bc6a8c1959f 100644 (file)
 // @flow
 import React from 'react';
 import { orderBy, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
 import SeverityHelper from '../../../components/shared/SeverityHelper';
 import { translate } from '../../../helpers/l10n';
+import { formatFacetStat } from '../utils';
 
 type Props = {|
   facetMode: string,
@@ -74,12 +75,11 @@ export default class SeverityFacet extends React.PureComponent {
       <FacetItem
         active={active}
         disabled={stat === 0 && !active}
-        facetMode={this.props.facetMode}
         halfWidth={true}
         key={severity}
         name={<SeverityHelper severity={severity} />}
         onClick={this.handleItemClick}
-        stat={stat}
+        stat={formatFacetStat(stat, this.props.facetMode)}
         value={severity}
       />
     );
@@ -89,7 +89,7 @@ export default class SeverityFacet extends React.PureComponent {
     const severities = ['BLOCKER', 'MINOR', 'CRITICAL', 'INFO', 'MAJOR'];
 
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index 8fc81051b1bb88bd748d5ab3a2d94be29a28e488..3ca01cf3c094c783c3bd5d70c76e7aca7110ff76 100644 (file)
 // @flow
 import React from 'react';
 import { orderBy, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
 import { translate } from '../../../helpers/l10n';
+import { formatFacetStat } from '../utils';
 
 type Props = {|
   facetMode: string,
@@ -81,12 +82,11 @@ export default class StatusFacet extends React.PureComponent {
       <FacetItem
         active={active}
         disabled={stat === 0 && !active}
-        facetMode={this.props.facetMode}
         halfWidth={true}
         key={status}
         name={this.renderStatus(status)}
         onClick={this.handleItemClick}
-        stat={stat}
+        stat={formatFacetStat(stat, this.props.facetMode)}
         value={status}
       />
     );
@@ -96,7 +96,7 @@ export default class StatusFacet extends React.PureComponent {
     const statuses = ['OPEN', 'RESOLVED', 'REOPENED', 'CLOSED', 'CONFIRMED'];
 
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index e979418cab00c29d78943620f5f54c0d8f1cd16b..f4f210c2ea65c4190d2e416af333894f2c940a02 100644 (file)
 // @flow
 import React from 'react';
 import { sortBy, uniq, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
-import FacetFooter from './components/FacetFooter';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
+import FacetFooter from '../../../components/facet/FacetFooter';
 import { searchIssueTags } from '../../../api/issues';
 import { translate } from '../../../helpers/l10n';
 import type { Component } from '../utils';
+import { formatFacetStat } from '../utils';
 
 type Props = {|
   component?: Component,
@@ -108,11 +109,10 @@ export default class TagFacet extends React.PureComponent {
         {tags.map(tag =>
           <FacetItem
             active={this.props.tags.includes(tag)}
-            facetMode={this.props.facetMode}
             key={tag}
             name={this.renderTag(tag)}
             onClick={this.handleItemClick}
-            stat={this.getStat(tag)}
+            stat={formatFacetStat(this.getStat(tag), this.props.facetMode)}
             value={tag}
           />
         )}
@@ -130,7 +130,7 @@ export default class TagFacet extends React.PureComponent {
 
   render() {
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index 362e690c997ef9e346c00bc917831d18b3077e72..92eb4914359ac4a9663eb0ceaeef6f1fb0d3f3cf 100644 (file)
 // @flow
 import React from 'react';
 import { orderBy, without } from 'lodash';
-import FacetBox from './components/FacetBox';
-import FacetHeader from './components/FacetHeader';
-import FacetItem from './components/FacetItem';
-import FacetItemsList from './components/FacetItemsList';
+import FacetBox from '../../../components/facet/FacetBox';
+import FacetHeader from '../../../components/facet/FacetHeader';
+import FacetItem from '../../../components/facet/FacetItem';
+import FacetItemsList from '../../../components/facet/FacetItemsList';
 import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
 import { translate } from '../../../helpers/l10n';
+import { formatFacetStat } from '../utils';
 
 type Props = {|
   facetMode: string,
@@ -74,7 +75,6 @@ export default class TypeFacet extends React.PureComponent {
       <FacetItem
         active={active}
         disabled={stat === 0 && !active}
-        facetMode={this.props.facetMode}
         key={type}
         name={
           <span>
@@ -82,7 +82,7 @@ export default class TypeFacet extends React.PureComponent {
           </span>
         }
         onClick={this.handleItemClick}
-        stat={stat}
+        stat={formatFacetStat(stat, this.props.facetMode)}
         value={type}
       />
     );
@@ -92,7 +92,7 @@ export default class TypeFacet extends React.PureComponent {
     const types = ['BUG', 'VULNERABILITY', 'CODE_SMELL'];
 
     return (
-      <FacetBox property={this.property}>
+      <FacetBox>
         <FacetHeader
           name={translate('issues.facet', this.property)}
           onClear={this.handleClear}
index f09143436c7fe6a0e0ed1801898ef0921e517f70..08badebe355ff42e2754d0a383b274a859a6b133 100644 (file)
@@ -1,9 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render 1`] = `
-<FacetBox
-  property="assignees"
->
+<FacetBox>
   <FacetHeader
     name="issues.facet.assignees"
     onClear={[Function]}
@@ -15,17 +13,15 @@ exports[`should render 1`] = `
     <FacetItem
       active={false}
       disabled={false}
-      facetMode="count"
       halfWidth={false}
       name="unassigned"
       onClick={[Function]}
-      stat={5}
+      stat="5"
       value=""
     />
     <FacetItem
       active={false}
       disabled={false}
-      facetMode="count"
       halfWidth={false}
       name={
         <span>
@@ -39,17 +35,16 @@ exports[`should render 1`] = `
         </span>
       }
       onClick={[Function]}
-      stat={13}
+      stat="13"
       value="foo"
     />
     <FacetItem
       active={false}
       disabled={false}
-      facetMode="count"
       halfWidth={false}
       name="bar"
       onClick={[Function]}
-      stat={7}
+      stat="7"
       value="bar"
     />
   </FacetItemsList>
@@ -74,9 +69,7 @@ exports[`should render footer select option 1`] = `
 `;
 
 exports[`should render without stats 1`] = `
-<FacetBox
-  property="assignees"
->
+<FacetBox>
   <FacetHeader
     name="issues.facet.assignees"
     onClear={[Function]}
@@ -88,9 +81,7 @@ exports[`should render without stats 1`] = `
 `;
 
 exports[`should select unassigned 1`] = `
-<FacetBox
-  property="assignees"
->
+<FacetBox>
   <FacetHeader
     name="issues.facet.assignees"
     onClear={[Function]}
@@ -102,17 +93,15 @@ exports[`should select unassigned 1`] = `
     <FacetItem
       active={true}
       disabled={false}
-      facetMode="count"
       halfWidth={false}
       name="unassigned"
       onClick={[Function]}
-      stat={5}
+      stat="5"
       value=""
     />
     <FacetItem
       active={false}
       disabled={false}
-      facetMode="count"
       halfWidth={false}
       name={
         <span>
@@ -126,17 +115,16 @@ exports[`should select unassigned 1`] = `
         </span>
       }
       onClick={[Function]}
-      stat={13}
+      stat="13"
       value="foo"
     />
     <FacetItem
       active={false}
       disabled={false}
-      facetMode="count"
       halfWidth={false}
       name="bar"
       onClick={[Function]}
-      stat={7}
+      stat="7"
       value="bar"
     />
   </FacetItemsList>
@@ -149,9 +137,7 @@ exports[`should select unassigned 1`] = `
 `;
 
 exports[`should select user 1`] = `
-<FacetBox
-  property="assignees"
->
+<FacetBox>
   <FacetHeader
     name="issues.facet.assignees"
     onClear={[Function]}
@@ -163,17 +149,15 @@ exports[`should select user 1`] = `
     <FacetItem
       active={false}
       disabled={false}
-      facetMode="count"
       halfWidth={false}
       name="unassigned"
       onClick={[Function]}
-      stat={5}
+      stat="5"
       value=""
     />
     <FacetItem
       active={true}
       disabled={false}
-      facetMode="count"
       halfWidth={false}
       name={
         <span>
@@ -187,17 +171,16 @@ exports[`should select user 1`] = `
         </span>
       }
       onClick={[Function]}
-      stat={13}
+      stat="13"
       value="foo"
     />
     <FacetItem
       active={false}
       disabled={false}
-      facetMode="count"
       halfWidth={false}
       name="bar"
       onClick={[Function]}
-      stat={7}
+      stat="7"
       value="bar"
     />
   </FacetItemsList>
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetBox.js b/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetBox.js
deleted file mode 100644 (file)
index 9f0c6cf..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-
-type Props = {|
-  children?: React.Element<*>,
-  property: string
-|};
-
-export default function FacetBox(props: Props) {
-  return (
-    <div className="search-navigator-facet-box" data-property={props.property}>
-      {props.children}
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetFooter.js b/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetFooter.js
deleted file mode 100644 (file)
index cc7d381..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import SearchSelect from '../../components/SearchSelect';
-
-type Option = { label: string, value: string };
-
-type Props = {|
-  minimumQueryLength?: number,
-  onSearch: (query: string) => Promise<Array<Option>>,
-  onSelect: (value: string) => void,
-  renderOption?: (option: Object) => React.Element<*>
-|};
-
-export default class FacetFooter extends React.PureComponent {
-  props: Props;
-
-  render() {
-    return (
-      <div className="search-navigator-facet-footer">
-        <SearchSelect autofocus={false} {...this.props} />
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetHeader.js b/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetHeader.js
deleted file mode 100644 (file)
index eb084d3..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.
- */
-// @flow
-/* eslint-disable max-len */
-import React from 'react';
-import { translate } from '../../../../helpers/l10n';
-
-type Props = {|
-  name: string,
-  onClear?: () => void,
-  onClick?: () => void,
-  open: boolean,
-  values?: number
-|};
-
-export default class FacetHeader extends React.PureComponent {
-  props: Props;
-
-  static defaultProps = {
-    open: true
-  };
-
-  handleClearClick = (event: Event & { currentTarget: HTMLElement }) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    if (this.props.onClear) {
-      this.props.onClear();
-    }
-  };
-
-  handleClick = (event: Event & { currentTarget: HTMLElement }) => {
-    event.preventDefault();
-    event.currentTarget.blur();
-    if (this.props.onClick) {
-      this.props.onClick();
-    }
-  };
-
-  renderCheckbox() {
-    return (
-      <svg viewBox="0 0 1792 1792" width="10" height="10" style={{ paddingTop: 3 }}>
-        {this.props.open
-          ? <path
-              style={{ fill: 'currentColor ' }}
-              d="M1683 808l-742 741q-19 19-45 19t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"
-            />
-          : <path
-              style={{ fill: 'currentColor ' }}
-              d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"
-            />}
-      </svg>
-    );
-  }
-
-  renderValueIndicator() {
-    if (this.props.open || !this.props.values) {
-      return null;
-    }
-    return (
-      <span className="spacer-left badge is-rounded">
-        {this.props.values}
-      </span>
-    );
-  }
-
-  render() {
-    const showClearButton: boolean = !!this.props.values && this.props.onClear != null;
-
-    return (
-      <div>
-        {showClearButton &&
-          <button
-            className="search-navigator-facet-header-button button-small button-red"
-            onClick={this.handleClearClick}>
-            {translate('clear')}
-          </button>}
-
-        {this.props.onClick
-          ? <a className="search-navigator-facet-header" href="#" onClick={this.handleClick}>
-              {this.renderCheckbox()} {this.props.name} {this.renderValueIndicator()}
-            </a>
-          : <span className="search-navigator-facet-header">
-              {this.props.name}
-            </span>}
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetItem.js b/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetItem.js
deleted file mode 100644 (file)
index 88c5a0e..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import classNames from 'classnames';
-import { formatMeasure } from '../../../../helpers/measures';
-
-type Props = {|
-  active: boolean,
-  disabled: boolean,
-  facetMode: string,
-  halfWidth: boolean,
-  name: string | React.Element<*>,
-  onClick: string => void,
-  stat: ?number,
-  value: string
-|};
-
-export default class FacetItem extends React.PureComponent {
-  props: Props;
-
-  static defaultProps = {
-    disabled: false,
-    halfWidth: false
-  };
-
-  handleClick = (event: Event & { currentTarget: HTMLElement }) => {
-    event.preventDefault();
-    const value = event.currentTarget.dataset.value;
-    this.props.onClick(value);
-  };
-
-  render() {
-    const { stat } = this.props;
-
-    const className = classNames('facet', 'search-navigator-facet', {
-      active: this.props.active,
-      'search-navigator-facet-half': this.props.halfWidth
-    });
-
-    const formattedStat =
-      stat &&
-      formatMeasure(stat, this.props.facetMode === 'effort' ? 'SHORT_WORK_DUR' : 'SHORT_INT');
-
-    return this.props.disabled
-      ? <span className={className}>
-          <span className="facet-name">
-            {this.props.name}
-          </span>
-          {formattedStat != null &&
-            <span className="facet-stat">
-              {formattedStat}
-            </span>}
-        </span>
-      : <a className={className} data-value={this.props.value} href="#" onClick={this.handleClick}>
-          <span className="facet-name">
-            {this.props.name}
-          </span>
-          {formattedStat != null &&
-            <span className="facet-stat">
-              {formattedStat}
-            </span>}
-        </a>;
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetItemsList.js b/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetItemsList.js
deleted file mode 100644 (file)
index 5d36d99..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-
-type Props = {|
-  children?: Array<React.Element<*>>
-|};
-
-export default function FacetItemsList(props: Props) {
-  return (
-    <div className="search-navigator-facet-list">
-      {props.children}
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetBox-test.js b/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetBox-test.js
deleted file mode 100644 (file)
index 2ebd4b3..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import FacetBox from '../FacetBox';
-
-it('should render', () => {
-  expect(
-    shallow(
-      <FacetBox property="foo">
-        <div />
-      </FacetBox>
-    )
-  ).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetFooter-test.js b/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetFooter-test.js
deleted file mode 100644 (file)
index 4dbf1cc..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import FacetFooter from '../FacetFooter';
-
-it('should render', () => {
-  expect(shallow(<FacetFooter onSearch={jest.fn()} onSelect={jest.fn()} />)).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetHeader-test.js b/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetHeader-test.js
deleted file mode 100644 (file)
index ed3f143..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import { click } from '../../../../../helpers/testUtils';
-import FacetHeader from '../FacetHeader';
-
-it('should render open facet with value', () => {
-  expect(
-    shallow(<FacetHeader name="foo" onClick={jest.fn()} open={true} values={1} />)
-  ).toMatchSnapshot();
-});
-
-it('should render open facet without value', () => {
-  expect(shallow(<FacetHeader name="foo" onClick={jest.fn()} open={true} />)).toMatchSnapshot();
-});
-
-it('should render closed facet with value', () => {
-  expect(
-    shallow(<FacetHeader name="foo" onClick={jest.fn()} open={false} values={1} />)
-  ).toMatchSnapshot();
-});
-
-it('should render closed facet without value', () => {
-  expect(shallow(<FacetHeader name="foo" onClick={jest.fn()} open={false} />)).toMatchSnapshot();
-});
-
-it('should render without link', () => {
-  expect(shallow(<FacetHeader name="foo" open={false} />)).toMatchSnapshot();
-});
-
-it('should call onClick', () => {
-  const onClick = jest.fn();
-  const wrapper = shallow(<FacetHeader name="foo" onClick={onClick} open={false} />);
-  click(wrapper.find('a'));
-  expect(onClick).toHaveBeenCalled();
-});
-
-it('should clear', () => {
-  const onClear = jest.fn();
-  const wrapper = shallow(
-    <FacetHeader name="foo" onClear={onClear} onClick={jest.fn()} open={false} values={3} />
-  );
-  expect(wrapper).toMatchSnapshot();
-  click(wrapper.find('.button-red'));
-  expect(onClear).toHaveBeenCalled();
-});
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetItem-test.js b/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetItem-test.js
deleted file mode 100644 (file)
index ddc84ec..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import { click } from '../../../../../helpers/testUtils';
-import FacetItem from '../FacetItem';
-
-const renderFacetItem = (props: {}) =>
-  shallow(
-    <FacetItem
-      active={false}
-      facetMode="count"
-      name="foo"
-      onClick={jest.fn()}
-      stat={null}
-      value="bar"
-      {...props}
-    />
-  );
-
-it('should render active', () => {
-  expect(renderFacetItem({ active: true })).toMatchSnapshot();
-});
-
-it('should render inactive', () => {
-  expect(renderFacetItem({ active: false })).toMatchSnapshot();
-});
-
-it('should render stat', () => {
-  expect(renderFacetItem({ stat: 13 })).toMatchSnapshot();
-});
-
-it('should render disabled', () => {
-  expect(renderFacetItem({ disabled: true })).toMatchSnapshot();
-});
-
-it('should render half width', () => {
-  expect(renderFacetItem({ halfWidth: true })).toMatchSnapshot();
-});
-
-it('should render effort stat', () => {
-  expect(renderFacetItem({ facetMode: 'effort', stat: 1234 })).toMatchSnapshot();
-});
-
-it('should call onClick', () => {
-  const onClick = jest.fn();
-  const wrapper = renderFacetItem({ onClick });
-  click(wrapper, { currentTarget: { dataset: { value: 'bar' } } });
-  expect(onClick).toHaveBeenCalled();
-});
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetItemsList-test.js b/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetItemsList-test.js
deleted file mode 100644 (file)
index 39fc1fb..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import FacetItemsList from '../FacetItemsList';
-
-it('should render', () => {
-  expect(
-    shallow(
-      <FacetItemsList>
-        <div />
-      </FacetItemsList>
-    )
-  ).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetBox-test.js.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetBox-test.js.snap
deleted file mode 100644 (file)
index 3173290..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render 1`] = `
-<div
-  className="search-navigator-facet-box"
-  data-property="foo"
->
-  <div />
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetFooter-test.js.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetFooter-test.js.snap
deleted file mode 100644 (file)
index e2475bd..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render 1`] = `
-<div
-  className="search-navigator-facet-footer"
->
-  <SearchSelect
-    autofocus={false}
-    minimumQueryLength={2}
-    onSearch={[Function]}
-    onSelect={[Function]}
-    resetOnBlur={true}
-  />
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetHeader-test.js.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetHeader-test.js.snap
deleted file mode 100644 (file)
index 8c92ac2..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should clear 1`] = `
-<div>
-  <button
-    className="search-navigator-facet-header-button button-small button-red"
-    onClick={[Function]}
-  >
-    clear
-  </button>
-  <a
-    className="search-navigator-facet-header"
-    href="#"
-    onClick={[Function]}
-  >
-    <svg
-      height="10"
-      style={
-        Object {
-          "paddingTop": 3,
-        }
-      }
-      viewBox="0 0 1792 1792"
-      width="10"
-    >
-      <path
-        d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"
-        style={
-          Object {
-            "fill": "currentColor ",
-          }
-        }
-      />
-    </svg>
-     
-    foo
-     
-    <span
-      className="spacer-left badge is-rounded"
-    >
-      3
-    </span>
-  </a>
-</div>
-`;
-
-exports[`should render closed facet with value 1`] = `
-<div>
-  <a
-    className="search-navigator-facet-header"
-    href="#"
-    onClick={[Function]}
-  >
-    <svg
-      height="10"
-      style={
-        Object {
-          "paddingTop": 3,
-        }
-      }
-      viewBox="0 0 1792 1792"
-      width="10"
-    >
-      <path
-        d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"
-        style={
-          Object {
-            "fill": "currentColor ",
-          }
-        }
-      />
-    </svg>
-     
-    foo
-     
-    <span
-      className="spacer-left badge is-rounded"
-    >
-      1
-    </span>
-  </a>
-</div>
-`;
-
-exports[`should render closed facet without value 1`] = `
-<div>
-  <a
-    className="search-navigator-facet-header"
-    href="#"
-    onClick={[Function]}
-  >
-    <svg
-      height="10"
-      style={
-        Object {
-          "paddingTop": 3,
-        }
-      }
-      viewBox="0 0 1792 1792"
-      width="10"
-    >
-      <path
-        d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"
-        style={
-          Object {
-            "fill": "currentColor ",
-          }
-        }
-      />
-    </svg>
-     
-    foo
-     
-  </a>
-</div>
-`;
-
-exports[`should render open facet with value 1`] = `
-<div>
-  <a
-    className="search-navigator-facet-header"
-    href="#"
-    onClick={[Function]}
-  >
-    <svg
-      height="10"
-      style={
-        Object {
-          "paddingTop": 3,
-        }
-      }
-      viewBox="0 0 1792 1792"
-      width="10"
-    >
-      <path
-        d="M1683 808l-742 741q-19 19-45 19t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"
-        style={
-          Object {
-            "fill": "currentColor ",
-          }
-        }
-      />
-    </svg>
-     
-    foo
-     
-  </a>
-</div>
-`;
-
-exports[`should render open facet without value 1`] = `
-<div>
-  <a
-    className="search-navigator-facet-header"
-    href="#"
-    onClick={[Function]}
-  >
-    <svg
-      height="10"
-      style={
-        Object {
-          "paddingTop": 3,
-        }
-      }
-      viewBox="0 0 1792 1792"
-      width="10"
-    >
-      <path
-        d="M1683 808l-742 741q-19 19-45 19t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"
-        style={
-          Object {
-            "fill": "currentColor ",
-          }
-        }
-      />
-    </svg>
-     
-    foo
-     
-  </a>
-</div>
-`;
-
-exports[`should render without link 1`] = `
-<div>
-  <span
-    className="search-navigator-facet-header"
-  >
-    foo
-  </span>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetItem-test.js.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetItem-test.js.snap
deleted file mode 100644 (file)
index b8db949..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render active 1`] = `
-<a
-  className="facet search-navigator-facet active"
-  data-value="bar"
-  href="#"
-  onClick={[Function]}
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-</a>
-`;
-
-exports[`should render disabled 1`] = `
-<span
-  className="facet search-navigator-facet"
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-</span>
-`;
-
-exports[`should render effort stat 1`] = `
-<a
-  className="facet search-navigator-facet"
-  data-value="bar"
-  href="#"
-  onClick={[Function]}
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-  <span
-    className="facet-stat"
-  >
-    work_duration.x_days.3
-  </span>
-</a>
-`;
-
-exports[`should render half width 1`] = `
-<a
-  className="facet search-navigator-facet search-navigator-facet-half"
-  data-value="bar"
-  href="#"
-  onClick={[Function]}
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-</a>
-`;
-
-exports[`should render inactive 1`] = `
-<a
-  className="facet search-navigator-facet"
-  data-value="bar"
-  href="#"
-  onClick={[Function]}
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-</a>
-`;
-
-exports[`should render stat 1`] = `
-<a
-  className="facet search-navigator-facet"
-  data-value="bar"
-  href="#"
-  onClick={[Function]}
->
-  <span
-    className="facet-name"
-  >
-    foo
-  </span>
-  <span
-    className="facet-stat"
-  >
-    13
-  </span>
-</a>
-`;
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetItemsList-test.js.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetItemsList-test.js.snap
deleted file mode 100644 (file)
index 9962cfc..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render 1`] = `
-<div
-  className="search-navigator-facet-list"
->
-  <div />
-</div>
-`;
index c3be4a5207baa81e19e44c32bcf6ce600c67d336..adf2d8aba6e2747b340f9bd722ed67e672afe9d9 100644 (file)
@@ -20,6 +20,7 @@
 // @flow
 import { searchMembers } from '../../api/organizations';
 import { searchUsers } from '../../api/users';
+import { formatMeasure } from '../../helpers/measures';
 import {
   queriesEqual,
   cleanQuery,
@@ -165,6 +166,12 @@ export const parseFacets = (facets: Array<RawFacet>): { [string]: Facet } => {
   return result;
 };
 
+export const formatFacetStat = (stat: ?number, mode: string): ?string => {
+  if (stat != null) {
+    return formatMeasure(stat, mode === 'effort' ? 'SHORT_WORK_DUR' : 'SHORT_INT');
+  }
+};
+
 export type ReferencedComponent = {
   key: string,
   name: string,
diff --git a/server/sonar-web/src/main/js/components/controls/SearchSelect.js b/server/sonar-web/src/main/js/components/controls/SearchSelect.js
new file mode 100644 (file)
index 0000000..3851527
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+import Select from 'react-select';
+import { debounce } from 'lodash';
+import { translate, translateWithParameters } from '../../helpers/l10n';
+
+type Option = { label: string, value: string };
+
+type Props = {|
+  autofocus: boolean,
+  minimumQueryLength: number,
+  onSearch: (query: string) => Promise<Array<Option>>,
+  onSelect: (value: string) => void,
+  renderOption?: (option: Object) => React.Element<*>,
+  resetOnBlur: boolean,
+  value?: string
+|};
+
+type State = {
+  loading: boolean,
+  options: Array<Option>,
+  query: string
+};
+
+export default class SearchSelect extends React.PureComponent {
+  mounted: boolean;
+  props: Props;
+  state: State;
+
+  static defaultProps = {
+    autofocus: true,
+    minimumQueryLength: 2,
+    resetOnBlur: true
+  };
+
+  constructor(props: Props) {
+    super(props);
+    this.state = { loading: false, options: [], query: '' };
+    this.search = debounce(this.search, 250);
+  }
+
+  componentDidMount() {
+    this.mounted = true;
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  search = (query: string) => {
+    this.props.onSearch(query).then(options => {
+      if (this.mounted) {
+        this.setState({ loading: false, options });
+      }
+    });
+  };
+
+  handleBlur = () => {
+    this.setState({ options: [], query: '' });
+  };
+
+  handleChange = (option: Option) => {
+    this.props.onSelect(option.value);
+  };
+
+  handleInputChange = (query: string = '') => {
+    if (query.length >= this.props.minimumQueryLength) {
+      this.setState({ loading: true, query });
+      this.search(query);
+    } else {
+      this.setState({ options: [], query });
+    }
+  };
+
+  // disable internal filtering
+  handleFilterOption = () => true;
+
+  render() {
+    return (
+      <Select
+        autofocus={this.props.autofocus}
+        cache={false}
+        className="input-super-large"
+        clearable={false}
+        filterOption={this.handleFilterOption}
+        isLoading={this.state.loading}
+        noResultsText={
+          this.state.query.length < this.props.minimumQueryLength
+            ? translateWithParameters('select2.tooShort', this.props.minimumQueryLength)
+            : translate('select2.noMatches')
+        }
+        onBlur={this.props.resetOnBlur ? this.handleBlur : undefined}
+        onChange={this.handleChange}
+        onInputChange={this.handleInputChange}
+        onOpen={this.props.minimumQueryLength === 0 ? this.handleInputChange : undefined}
+        optionRenderer={this.props.renderOption}
+        options={this.state.options}
+        placeholder={translate('search_verb')}
+        searchable={true}
+        value={this.props.value}
+        valueRenderer={this.props.renderOption}
+      />
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/SearchSelect-test.js b/server/sonar-web/src/main/js/components/controls/__tests__/SearchSelect-test.js
new file mode 100644 (file)
index 0000000..f4d46d4
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+import { shallow } from 'enzyme';
+import SearchSelect from '../SearchSelect';
+
+jest.mock('lodash', () => ({
+  debounce: fn => fn
+}));
+
+it('should render Select', () => {
+  expect(shallow(<SearchSelect onSearch={jest.fn()} onSelect={jest.fn()} />)).toMatchSnapshot();
+});
+
+it('should call onSelect', () => {
+  const onSelect = jest.fn();
+  const wrapper = shallow(<SearchSelect onSearch={jest.fn()} onSelect={onSelect} />);
+  wrapper.prop('onChange')({ value: 'foo' });
+  expect(onSelect).lastCalledWith('foo');
+});
+
+it('should call onSearch', () => {
+  const onSearch = jest.fn().mockReturnValue(Promise.resolve([]));
+  const wrapper = shallow(
+    <SearchSelect minimumQueryLength={2} onSearch={onSearch} onSelect={jest.fn()} />
+  );
+  wrapper.prop('onInputChange')('f');
+  expect(onSearch).not.toHaveBeenCalled();
+  wrapper.prop('onInputChange')('foo');
+  expect(onSearch).lastCalledWith('foo');
+});
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/SearchSelect-test.js.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/SearchSelect-test.js.snap
new file mode 100644 (file)
index 0000000..d3ea2ed
--- /dev/null
@@ -0,0 +1,52 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render Select 1`] = `
+<Select
+  addLabelText="Add \\"{label}\\"?"
+  arrowRenderer={[Function]}
+  autofocus={true}
+  autosize={true}
+  backspaceRemoves={true}
+  backspaceToRemoveMessage="Press backspace to remove {label}"
+  cache={false}
+  className="input-super-large"
+  clearAllText="Clear all"
+  clearRenderer={[Function]}
+  clearValueText="Clear value"
+  clearable={false}
+  deleteRemoves={true}
+  delimiter=","
+  disabled={false}
+  escapeClearsValue={true}
+  filterOption={[Function]}
+  filterOptions={[Function]}
+  ignoreAccents={true}
+  ignoreCase={true}
+  inputProps={Object {}}
+  isLoading={false}
+  joinValues={false}
+  labelKey="label"
+  matchPos="any"
+  matchProp="any"
+  menuBuffer={0}
+  menuRenderer={[Function]}
+  multi={false}
+  noResultsText="select2.tooShort.2"
+  onBlur={[Function]}
+  onBlurResetsInput={true}
+  onChange={[Function]}
+  onCloseResetsInput={true}
+  onInputChange={[Function]}
+  optionComponent={[Function]}
+  options={Array []}
+  pageSize={5}
+  placeholder="search_verb"
+  required={false}
+  scrollMenuIntoView={true}
+  searchable={true}
+  simpleValue={false}
+  tabSelectsValue={true}
+  valueComponent={[Function]}
+  valueKey="value"
+/>
+`;
diff --git a/server/sonar-web/src/main/js/components/facet/FacetBox.js b/server/sonar-web/src/main/js/components/facet/FacetBox.js
new file mode 100644 (file)
index 0000000..92b7afb
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+
+type Props = {|
+  children?: React.Element<*>
+|};
+
+export default function FacetBox(props: Props) {
+  return (
+    <div className="search-navigator-facet-box">
+      {props.children}
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/facet/FacetFooter.js b/server/sonar-web/src/main/js/components/facet/FacetFooter.js
new file mode 100644 (file)
index 0000000..d11af83
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+import SearchSelect from '../controls/SearchSelect';
+
+type Option = { label: string, value: string };
+
+type Props = {|
+  minimumQueryLength?: number,
+  onSearch: (query: string) => Promise<Array<Option>>,
+  onSelect: (value: string) => void,
+  renderOption?: (option: Object) => React.Element<*>
+|};
+
+export default class FacetFooter extends React.PureComponent {
+  props: Props;
+
+  render() {
+    return (
+      <div className="search-navigator-facet-footer">
+        <SearchSelect autofocus={false} {...this.props} />
+      </div>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/facet/FacetHeader.js b/server/sonar-web/src/main/js/components/facet/FacetHeader.js
new file mode 100644 (file)
index 0000000..42c7b27
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+// @flow
+/* eslint-disable max-len */
+import React from 'react';
+import { translate } from '../../helpers/l10n';
+
+type Props = {|
+  name: string,
+  onClear?: () => void,
+  onClick?: () => void,
+  open: boolean,
+  values?: number
+|};
+
+export default class FacetHeader extends React.PureComponent {
+  props: Props;
+
+  static defaultProps = {
+    open: true
+  };
+
+  handleClearClick = (event: Event & { currentTarget: HTMLElement }) => {
+    event.preventDefault();
+    event.currentTarget.blur();
+    if (this.props.onClear) {
+      this.props.onClear();
+    }
+  };
+
+  handleClick = (event: Event & { currentTarget: HTMLElement }) => {
+    event.preventDefault();
+    event.currentTarget.blur();
+    if (this.props.onClick) {
+      this.props.onClick();
+    }
+  };
+
+  renderCheckbox() {
+    return (
+      <svg viewBox="0 0 1792 1792" width="10" height="10" style={{ paddingTop: 3 }}>
+        {this.props.open
+          ? <path
+              style={{ fill: 'currentColor ' }}
+              d="M1683 808l-742 741q-19 19-45 19t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"
+            />
+          : <path
+              style={{ fill: 'currentColor ' }}
+              d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"
+            />}
+      </svg>
+    );
+  }
+
+  renderValueIndicator() {
+    if (this.props.open || !this.props.values) {
+      return null;
+    }
+    return (
+      <span className="spacer-left badge is-rounded">
+        {this.props.values}
+      </span>
+    );
+  }
+
+  render() {
+    const showClearButton: boolean = !!this.props.values && this.props.onClear != null;
+
+    return (
+      <div>
+        {showClearButton &&
+          <button
+            className="search-navigator-facet-header-button button-small button-red"
+            onClick={this.handleClearClick}>
+            {translate('clear')}
+          </button>}
+
+        {this.props.onClick
+          ? <a className="search-navigator-facet-header" href="#" onClick={this.handleClick}>
+              {this.renderCheckbox()} {this.props.name} {this.renderValueIndicator()}
+            </a>
+          : <span className="search-navigator-facet-header">
+              {this.props.name}
+            </span>}
+      </div>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/facet/FacetItem.js b/server/sonar-web/src/main/js/components/facet/FacetItem.js
new file mode 100644 (file)
index 0000000..4ecaf76
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+import classNames from 'classnames';
+
+type Props = {|
+  active: boolean,
+  disabled: boolean,
+  halfWidth: boolean,
+  name: string | React.Element<*>,
+  onClick: string => void,
+  stat?: ?(string | React.Element<*>),
+  value: string
+|};
+
+export default class FacetItem extends React.PureComponent {
+  props: Props;
+
+  static defaultProps = {
+    disabled: false,
+    halfWidth: false
+  };
+
+  handleClick = (event: Event & { currentTarget: HTMLElement }) => {
+    event.preventDefault();
+    this.props.onClick(this.props.value);
+  };
+
+  render() {
+    const className = classNames('facet', 'search-navigator-facet', {
+      active: this.props.active,
+      'search-navigator-facet-half': this.props.halfWidth
+    });
+
+    return this.props.disabled
+      ? <span className={className}>
+          <span className="facet-name">
+            {this.props.name}
+          </span>
+          {this.props.stat != null &&
+            <span className="facet-stat">
+              {this.props.stat}
+            </span>}
+        </span>
+      : <a className={className} href="#" onClick={this.handleClick}>
+          <span className="facet-name">
+            {this.props.name}
+          </span>
+          {this.props.stat != null &&
+            <span className="facet-stat">
+              {this.props.stat}
+            </span>}
+        </a>;
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/facet/FacetItemsList.js b/server/sonar-web/src/main/js/components/facet/FacetItemsList.js
new file mode 100644 (file)
index 0000000..5d36d99
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+
+type Props = {|
+  children?: Array<React.Element<*>>
+|};
+
+export default function FacetItemsList(props: Props) {
+  return (
+    <div className="search-navigator-facet-list">
+      {props.children}
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/FacetBox-test.js b/server/sonar-web/src/main/js/components/facet/__tests__/FacetBox-test.js
new file mode 100644 (file)
index 0000000..2fb313d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+import { shallow } from 'enzyme';
+import FacetBox from '../FacetBox';
+
+it('should render', () => {
+  expect(
+    shallow(
+      <FacetBox>
+        <div />
+      </FacetBox>
+    )
+  ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/FacetFooter-test.js b/server/sonar-web/src/main/js/components/facet/__tests__/FacetFooter-test.js
new file mode 100644 (file)
index 0000000..4dbf1cc
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+import { shallow } from 'enzyme';
+import FacetFooter from '../FacetFooter';
+
+it('should render', () => {
+  expect(shallow(<FacetFooter onSearch={jest.fn()} onSelect={jest.fn()} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/FacetHeader-test.js b/server/sonar-web/src/main/js/components/facet/__tests__/FacetHeader-test.js
new file mode 100644 (file)
index 0000000..aaa674c
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+import { shallow } from 'enzyme';
+import { click } from '../../../helpers/testUtils';
+import FacetHeader from '../FacetHeader';
+
+it('should render open facet with value', () => {
+  expect(
+    shallow(<FacetHeader name="foo" onClick={jest.fn()} open={true} values={1} />)
+  ).toMatchSnapshot();
+});
+
+it('should render open facet without value', () => {
+  expect(shallow(<FacetHeader name="foo" onClick={jest.fn()} open={true} />)).toMatchSnapshot();
+});
+
+it('should render closed facet with value', () => {
+  expect(
+    shallow(<FacetHeader name="foo" onClick={jest.fn()} open={false} values={1} />)
+  ).toMatchSnapshot();
+});
+
+it('should render closed facet without value', () => {
+  expect(shallow(<FacetHeader name="foo" onClick={jest.fn()} open={false} />)).toMatchSnapshot();
+});
+
+it('should render without link', () => {
+  expect(shallow(<FacetHeader name="foo" open={false} />)).toMatchSnapshot();
+});
+
+it('should call onClick', () => {
+  const onClick = jest.fn();
+  const wrapper = shallow(<FacetHeader name="foo" onClick={onClick} open={false} />);
+  click(wrapper.find('a'));
+  expect(onClick).toHaveBeenCalled();
+});
+
+it('should clear', () => {
+  const onClear = jest.fn();
+  const wrapper = shallow(
+    <FacetHeader name="foo" onClear={onClear} onClick={jest.fn()} open={false} values={3} />
+  );
+  expect(wrapper).toMatchSnapshot();
+  click(wrapper.find('.button-red'));
+  expect(onClear).toHaveBeenCalled();
+});
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/FacetItem-test.js b/server/sonar-web/src/main/js/components/facet/__tests__/FacetItem-test.js
new file mode 100644 (file)
index 0000000..2b602b2
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+import { shallow } from 'enzyme';
+import { click } from '../../../helpers/testUtils';
+import FacetItem from '../FacetItem';
+
+const renderFacetItem = (props: {}) =>
+  shallow(
+    <FacetItem active={false} name="foo" onClick={jest.fn()} stat={null} value="bar" {...props} />
+  );
+
+it('should render active', () => {
+  expect(renderFacetItem({ active: true })).toMatchSnapshot();
+});
+
+it('should render inactive', () => {
+  expect(renderFacetItem({ active: false })).toMatchSnapshot();
+});
+
+it('should render stat', () => {
+  expect(renderFacetItem({ stat: '13' })).toMatchSnapshot();
+});
+
+it('should render disabled', () => {
+  expect(renderFacetItem({ disabled: true })).toMatchSnapshot();
+});
+
+it('should render half width', () => {
+  expect(renderFacetItem({ halfWidth: true })).toMatchSnapshot();
+});
+
+it('should render effort stat', () => {
+  expect(renderFacetItem({ facetMode: 'effort', stat: '1234' })).toMatchSnapshot();
+});
+
+it('should call onClick', () => {
+  const onClick = jest.fn();
+  const wrapper = renderFacetItem({ onClick });
+  click(wrapper, { currentTarget: { dataset: { value: 'bar' } } });
+  expect(onClick).toHaveBeenCalled();
+});
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/FacetItemsList-test.js b/server/sonar-web/src/main/js/components/facet/__tests__/FacetItemsList-test.js
new file mode 100644 (file)
index 0000000..39fc1fb
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+// @flow
+import React from 'react';
+import { shallow } from 'enzyme';
+import FacetItemsList from '../FacetItemsList';
+
+it('should render', () => {
+  expect(
+    shallow(
+      <FacetItemsList>
+        <div />
+      </FacetItemsList>
+    )
+  ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetBox-test.js.snap b/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetBox-test.js.snap
new file mode 100644 (file)
index 0000000..e28d453
--- /dev/null
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render 1`] = `
+<div
+  className="search-navigator-facet-box"
+>
+  <div />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetFooter-test.js.snap b/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetFooter-test.js.snap
new file mode 100644 (file)
index 0000000..e2475bd
--- /dev/null
@@ -0,0 +1,15 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render 1`] = `
+<div
+  className="search-navigator-facet-footer"
+>
+  <SearchSelect
+    autofocus={false}
+    minimumQueryLength={2}
+    onSearch={[Function]}
+    onSelect={[Function]}
+    resetOnBlur={true}
+  />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetHeader-test.js.snap b/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetHeader-test.js.snap
new file mode 100644 (file)
index 0000000..8c92ac2
--- /dev/null
@@ -0,0 +1,192 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should clear 1`] = `
+<div>
+  <button
+    className="search-navigator-facet-header-button button-small button-red"
+    onClick={[Function]}
+  >
+    clear
+  </button>
+  <a
+    className="search-navigator-facet-header"
+    href="#"
+    onClick={[Function]}
+  >
+    <svg
+      height="10"
+      style={
+        Object {
+          "paddingTop": 3,
+        }
+      }
+      viewBox="0 0 1792 1792"
+      width="10"
+    >
+      <path
+        d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"
+        style={
+          Object {
+            "fill": "currentColor ",
+          }
+        }
+      />
+    </svg>
+     
+    foo
+     
+    <span
+      className="spacer-left badge is-rounded"
+    >
+      3
+    </span>
+  </a>
+</div>
+`;
+
+exports[`should render closed facet with value 1`] = `
+<div>
+  <a
+    className="search-navigator-facet-header"
+    href="#"
+    onClick={[Function]}
+  >
+    <svg
+      height="10"
+      style={
+        Object {
+          "paddingTop": 3,
+        }
+      }
+      viewBox="0 0 1792 1792"
+      width="10"
+    >
+      <path
+        d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"
+        style={
+          Object {
+            "fill": "currentColor ",
+          }
+        }
+      />
+    </svg>
+     
+    foo
+     
+    <span
+      className="spacer-left badge is-rounded"
+    >
+      1
+    </span>
+  </a>
+</div>
+`;
+
+exports[`should render closed facet without value 1`] = `
+<div>
+  <a
+    className="search-navigator-facet-header"
+    href="#"
+    onClick={[Function]}
+  >
+    <svg
+      height="10"
+      style={
+        Object {
+          "paddingTop": 3,
+        }
+      }
+      viewBox="0 0 1792 1792"
+      width="10"
+    >
+      <path
+        d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45l166-166q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"
+        style={
+          Object {
+            "fill": "currentColor ",
+          }
+        }
+      />
+    </svg>
+     
+    foo
+     
+  </a>
+</div>
+`;
+
+exports[`should render open facet with value 1`] = `
+<div>
+  <a
+    className="search-navigator-facet-header"
+    href="#"
+    onClick={[Function]}
+  >
+    <svg
+      height="10"
+      style={
+        Object {
+          "paddingTop": 3,
+        }
+      }
+      viewBox="0 0 1792 1792"
+      width="10"
+    >
+      <path
+        d="M1683 808l-742 741q-19 19-45 19t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"
+        style={
+          Object {
+            "fill": "currentColor ",
+          }
+        }
+      />
+    </svg>
+     
+    foo
+     
+  </a>
+</div>
+`;
+
+exports[`should render open facet without value 1`] = `
+<div>
+  <a
+    className="search-navigator-facet-header"
+    href="#"
+    onClick={[Function]}
+  >
+    <svg
+      height="10"
+      style={
+        Object {
+          "paddingTop": 3,
+        }
+      }
+      viewBox="0 0 1792 1792"
+      width="10"
+    >
+      <path
+        d="M1683 808l-742 741q-19 19-45 19t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"
+        style={
+          Object {
+            "fill": "currentColor ",
+          }
+        }
+      />
+    </svg>
+     
+    foo
+     
+  </a>
+</div>
+`;
+
+exports[`should render without link 1`] = `
+<div>
+  <span
+    className="search-navigator-facet-header"
+  >
+    foo
+  </span>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetItem-test.js.snap b/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetItem-test.js.snap
new file mode 100644 (file)
index 0000000..6aff532
--- /dev/null
@@ -0,0 +1,93 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render active 1`] = `
+<a
+  className="facet search-navigator-facet active"
+  href="#"
+  onClick={[Function]}
+>
+  <span
+    className="facet-name"
+  >
+    foo
+  </span>
+</a>
+`;
+
+exports[`should render disabled 1`] = `
+<span
+  className="facet search-navigator-facet"
+>
+  <span
+    className="facet-name"
+  >
+    foo
+  </span>
+</span>
+`;
+
+exports[`should render effort stat 1`] = `
+<a
+  className="facet search-navigator-facet"
+  href="#"
+  onClick={[Function]}
+>
+  <span
+    className="facet-name"
+  >
+    foo
+  </span>
+  <span
+    className="facet-stat"
+  >
+    1234
+  </span>
+</a>
+`;
+
+exports[`should render half width 1`] = `
+<a
+  className="facet search-navigator-facet search-navigator-facet-half"
+  href="#"
+  onClick={[Function]}
+>
+  <span
+    className="facet-name"
+  >
+    foo
+  </span>
+</a>
+`;
+
+exports[`should render inactive 1`] = `
+<a
+  className="facet search-navigator-facet"
+  href="#"
+  onClick={[Function]}
+>
+  <span
+    className="facet-name"
+  >
+    foo
+  </span>
+</a>
+`;
+
+exports[`should render stat 1`] = `
+<a
+  className="facet search-navigator-facet"
+  href="#"
+  onClick={[Function]}
+>
+  <span
+    className="facet-name"
+  >
+    foo
+  </span>
+  <span
+    className="facet-stat"
+  >
+    13
+  </span>
+</a>
+`;
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetItemsList-test.js.snap b/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetItemsList-test.js.snap
new file mode 100644 (file)
index 0000000..9962cfc
--- /dev/null
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render 1`] = `
+<div
+  className="search-navigator-facet-list"
+>
+  <div />
+</div>
+`;