diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-07-27 12:18:59 +0200 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-08-14 11:44:44 +0200 |
commit | a0ea568244db7b77b165c7ecf7dca83620f8edbd (patch) | |
tree | 02c361c952478ae4ab28f9d0a2a6c6f9ff9ef4c5 /server/sonar-web/src/main/js/apps | |
parent | f2c95a685a3b950a68122af1e5673978a07df773 (diff) | |
download | sonarqube-a0ea568244db7b77b165c7ecf7dca83620f8edbd.tar.gz sonarqube-a0ea568244db7b77b165c7ecf7dca83620f8edbd.zip |
Generalize facet components for both issues and measures
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
50 files changed, 147 insertions, 1844 deletions
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js index ac2083422d1..c7e8dd68b21 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js +++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainFacet.js @@ -19,11 +19,11 @@ */ // @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 index 92b7afb58db..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetBox.js +++ /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 index 2cc9d19f98b..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetHeader.js +++ /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 index e84480b1317..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItem.js +++ /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/FacetItemsList.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItemsList.js deleted file mode 100644 index 5d36d9934e3..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItemsList.js +++ /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/FacetItemMeasureValue.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetMeasureValue.js index ede45d98793..eae569c72e0 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetItemMeasureValue.js +++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/FacetMeasureValue.js @@ -23,7 +23,7 @@ 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 }) { +export default function FacetMeasureValue({ measure }: { measure: MeasureEnhanced }) { if (isDiffMetric(measure.metric.key)) { return ( <div diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.js b/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.js index f707eb462a5..58c0326c9dc 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.js +++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.js @@ -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 index 0fe3347edbd..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetHeader-test.js +++ /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 index e8c8bfe4d54..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/FacetItem-test.js +++ /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__/FacetMeasureValue-test.js index d2a6ec5769a..b92096c3e40 100644 --- 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__/FacetMeasureValue-test.js @@ -20,7 +20,7 @@ // @flow import React from 'react'; import { shallow } from 'enzyme'; -import FacetItemMeasureValue from '../FacetItemMeasureValue'; +import FacetMeasureValue from '../FacetMeasureValue'; const MEASURE = { metric: { @@ -45,9 +45,9 @@ const LEAK_MEASURE = { }; it('should display measure value', () => { - expect(shallow(<FacetItemMeasureValue measure={MEASURE} />)).toMatchSnapshot(); + expect(shallow(<FacetMeasureValue measure={MEASURE} />)).toMatchSnapshot(); }); it('should display leak measure value', () => { - expect(shallow(<FacetItemMeasureValue measure={LEAK_MEASURE} />)).toMatchSnapshot(); + expect(shallow(<FacetMeasureValue measure={LEAK_MEASURE} />)).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/DomainFacet-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/DomainFacet-test.js.snap index 6ca87d36a3f..a55b82c48d7 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/DomainFacet-test.js.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/DomainFacet-test.js.snap @@ -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 index bd6c0e5681c..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetHeader-test.js.snap +++ /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 index 82e72d3b0af..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/FacetItem-test.js.snap +++ /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__/FacetMeasureValue-test.js.snap index 9bd6a3e7e00..9bd6a3e7e00 100644 --- 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__/FacetMeasureValue-test.js.snap diff --git a/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.js b/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.js index 4340f1670bf..250c8ed2a6d 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.js +++ b/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.js @@ -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 index 7d95fdde139..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/components/SearchSelect.js +++ /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 index f4d46d4f8a8..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/SearchSelect-test.js +++ /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 index d3ea2edb7b0..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/SearchSelect-test.js.snap +++ /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" -/> -`; diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.js index 20b3387486f..94407c286b6 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.js @@ -21,12 +21,12 @@ 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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/AuthorFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/AuthorFacet.js index 96f81dce317..8cb5c1f05ab 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/AuthorFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/AuthorFacet.js @@ -20,10 +20,11 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.js index ab491dd167b..f7cc13735a9 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.js @@ -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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/DirectoryFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/DirectoryFacet.js index 13cf51d17d7..35d8b70b2ac 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/DirectoryFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/DirectoryFacet.js @@ -20,13 +20,14 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/FacetMode.js b/server/sonar-web/src/main/js/apps/issues/sidebar/FacetMode.js index 1c178bc8873..64419abdf81 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/FacetMode.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/FacetMode.js @@ -19,10 +19,10 @@ */ // @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} /> )} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.js index b6404f3fd3b..33af07a4212 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.js @@ -20,14 +20,15 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/LanguageFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/LanguageFacet.js index ec7ba416c86..37406c5f00e 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/LanguageFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/LanguageFacet.js @@ -20,13 +20,14 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/ModuleFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/ModuleFacet.js index c7eb7c8dc26..8f7df010f83 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/ModuleFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/ModuleFacet.js @@ -20,13 +20,14 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/ProjectFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/ProjectFacet.js index 62a1af46084..dbbf3f4784b 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/ProjectFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/ProjectFacet.js @@ -20,16 +20,17 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/ResolutionFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/ResolutionFacet.js index c905c98c2b8..c6a949b3fb5 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/ResolutionFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/ResolutionFacet.js @@ -20,11 +20,12 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/RuleFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/RuleFacet.js index d4d322eeb6d..9cb922314ee 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/RuleFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/RuleFacet.js @@ -20,13 +20,14 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.js index 8b4edb49672..69765762b8e 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.js @@ -20,12 +20,13 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.js index 8fc81051b1b..3ca01cf3c09 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.js @@ -20,11 +20,12 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/TagFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/TagFacet.js index e979418cab0..f4f210c2ea6 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/TagFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/TagFacet.js @@ -20,14 +20,15 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.js b/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.js index 362e690c997..92eb4914359 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.js +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.js @@ -20,12 +20,13 @@ // @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} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.js.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.js.snap index f09143436c7..08badebe355 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.js.snap +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.js.snap @@ -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 index 9f0c6cf98ee..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetBox.js +++ /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 index cc7d381b3e7..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetFooter.js +++ /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 index eb084d38f65..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetHeader.js +++ /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 index 88c5a0e7c77..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetItem.js +++ /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 index 5d36d9934e3..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/FacetItemsList.js +++ /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 index 2ebd4b3feaa..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetBox-test.js +++ /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 index 4dbf1cc3ece..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetFooter-test.js +++ /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 index ed3f143eedc..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetHeader-test.js +++ /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 index ddc84eca458..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetItem-test.js +++ /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 index 39fc1fb4eef..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/FacetItemsList-test.js +++ /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 index 317329065b1..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetBox-test.js.snap +++ /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 index e2475bdc5dd..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetFooter-test.js.snap +++ /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 index 8c92ac2d4ba..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetHeader-test.js.snap +++ /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 index b8db949fded..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetItem-test.js.snap +++ /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 index 9962cfc364e..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/components/__tests__/__snapshots__/FacetItemsList-test.js.snap +++ /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> -`; diff --git a/server/sonar-web/src/main/js/apps/issues/utils.js b/server/sonar-web/src/main/js/apps/issues/utils.js index c3be4a5207b..adf2d8aba6e 100644 --- a/server/sonar-web/src/main/js/apps/issues/utils.js +++ b/server/sonar-web/src/main/js/apps/issues/utils.js @@ -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, |