diff options
Diffstat (limited to 'server/sonar-web/src/main/js/components')
16 files changed, 177 insertions, 209 deletions
diff --git a/server/sonar-web/src/main/js/components/common/BubblePopup.tsx b/server/sonar-web/src/main/js/components/common/BubblePopup.tsx index deb5f3d7882..18b7dbd6f95 100644 --- a/server/sonar-web/src/main/js/components/common/BubblePopup.tsx +++ b/server/sonar-web/src/main/js/components/common/BubblePopup.tsx @@ -20,10 +20,15 @@ import * as React from 'react'; import * as classNames from 'classnames'; +export interface BubblePopupPosition { + top: number; + right: number; +} + interface Props { customClass?: string; children: React.ReactNode; - position: { top: number; right: number }; + position: BubblePopupPosition; } export default function BubblePopup(props: Props) { diff --git a/server/sonar-web/src/main/js/components/common/MultiSelect.js b/server/sonar-web/src/main/js/components/common/MultiSelect.tsx index 2921ab1ad76..b88ccd430cf 100644 --- a/server/sonar-web/src/main/js/components/common/MultiSelect.js +++ b/server/sonar-web/src/main/js/components/common/MultiSelect.tsx @@ -17,47 +17,46 @@ * 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 * as React from 'react'; import { difference } from 'lodash'; import MultiSelectOption from './MultiSelectOption'; import SearchBox from '../controls/SearchBox'; -import { translate } from '../../helpers/l10n'; -/*:: -type Props = { - selectedElements: Array<string>, - elements: Array<string>, - listSize: number, - onSearch: string => void, - onSelect: string => void, - onUnselect: string => void, - validateSearchInput: string => string, - placeholder: string -}; -*/ +interface Props { + selectedElements: Array<string>; + elements: Array<string>; + listSize?: number; + onSearch: (query: string) => void; + onSelect: (item: string) => void; + onUnselect: (item: string) => void; + validateSearchInput?: (value: string) => string; + placeholder: string; +} + +interface State { + query: string; + selectedElements: Array<string>; + unselectedElements: Array<string>; + activeIdx: number; +} -/*:: -type State = { - query: string, - selectedElements: Array<string>, - unselectedElements: Array<string>, - activeIdx: number -}; -*/ +interface DefaultProps { + listSize: number; + validateSearchInput: (value: string) => string; +} + +type PropsWithDefault = Props & DefaultProps; -export default class MultiSelect extends React.PureComponent { - /*:: container: HTMLElement; */ - /*:: searchInput: HTMLInputElement; */ - /*:: props: Props; */ - /*:: state: State; */ +export default class MultiSelect extends React.PureComponent<Props, State> { + container: HTMLDivElement | null; + searchInput: HTMLInputElement | null; - static defaultProps = { + static defaultProps: DefaultProps = { listSize: 10, - validateSearchInput: (value /*: string */) => value + validateSearchInput: (value: string) => value }; - constructor(props /*: Props */) { + constructor(props: Props) { super(props); this.state = { query: '', @@ -69,13 +68,13 @@ export default class MultiSelect extends React.PureComponent { componentDidMount() { this.updateSelectedElements(this.props); - this.updateUnselectedElements(this.props); + this.updateUnselectedElements(this.props as PropsWithDefault); if (this.container) { this.container.addEventListener('keydown', this.handleKeyboard, true); } } - componentWillReceiveProps(nextProps /*: Props */) { + componentWillReceiveProps(nextProps: PropsWithDefault) { if ( this.props.elements !== nextProps.elements || this.props.selectedElements !== nextProps.selectedElements @@ -91,14 +90,18 @@ export default class MultiSelect extends React.PureComponent { } componentDidUpdate() { - this.searchInput && this.searchInput.focus(); + if (this.searchInput) { + this.searchInput.focus(); + } } componentWillUnmount() { - this.container.removeEventListener('keydown', this.handleKeyboard); + if (this.container) { + this.container.removeEventListener('keydown', this.handleKeyboard); + } } - handleSelectChange = (item /*: string */, selected /*: boolean */) => { + handleSelectChange = (item: string, selected: boolean) => { if (selected) { this.onSelectItem(item); } else { @@ -106,17 +109,17 @@ export default class MultiSelect extends React.PureComponent { } }; - handleSearchChange = (value /*: string */) => { - this.onSearchQuery(this.props.validateSearchInput(value)); + handleSearchChange = (value: string) => { + this.onSearchQuery((this.props as PropsWithDefault).validateSearchInput(value)); }; - handleElementHover = (element /*: string */) => { + handleElementHover = (element: string) => { this.setState((prevState, props) => { return { activeIdx: this.getAllElements(props, prevState).indexOf(element) }; }); }; - handleKeyboard = (evt /*: KeyboardEvent */) => { + handleKeyboard = (evt: KeyboardEvent) => { switch (evt.keyCode) { case 40: // down this.setState(this.selectNextElement); @@ -140,28 +143,25 @@ export default class MultiSelect extends React.PureComponent { } }; - onSearchQuery(query /*: string */) { + onSearchQuery = (query: string) => { this.setState({ query, activeIdx: 0 }); this.props.onSearch(query); - } + }; - onSelectItem(item /*: string */) { + onSelectItem = (item: string) => { if (this.isNewElement(item, this.props)) { this.onSearchQuery(''); } this.props.onSelect(item); - } + }; - onUnselectItem(item /*: string */) { - this.props.onUnselect(item); - } + onUnselectItem = (item: string) => this.props.onUnselect(item); - isNewElement(elem /*: string */, { selectedElements, elements } /*: Props */) { - return elem && selectedElements.indexOf(elem) === -1 && elements.indexOf(elem) === -1; - } + isNewElement = (elem: string, { selectedElements, elements }: Props) => + elem && selectedElements.indexOf(elem) === -1 && elements.indexOf(elem) === -1; - updateSelectedElements(props /*: Props */) { - this.setState((state /*: State */) => { + updateSelectedElements = (props: Props) => { + this.setState((state: State) => { if (state.query) { return { selectedElements: [...props.selectedElements.filter(elem => elem.includes(state.query))] @@ -170,10 +170,10 @@ export default class MultiSelect extends React.PureComponent { return { selectedElements: [...props.selectedElements] }; } }); - } + }; - updateUnselectedElements(props /*: Props */) { - this.setState((state /*: State */) => { + updateUnselectedElements = (props: PropsWithDefault) => { + this.setState((state: State) => { if (props.listSize < state.selectedElements.length) { return { unselectedElements: [] }; } else { @@ -185,21 +185,19 @@ export default class MultiSelect extends React.PureComponent { }; } }); - } + }; - getAllElements(props /*: Props */, state /*: State */) { + getAllElements = (props: Props, state: State) => { if (this.isNewElement(state.query, props)) { return [...state.selectedElements, ...state.unselectedElements, state.query]; } else { return [...state.selectedElements, ...state.unselectedElements]; } - } + }; - setElementActive(idx /*: number */) { - this.setState({ activeIdx: idx }); - } + setElementActive = (idx: number) => this.setState({ activeIdx: idx }); - selectNextElement = (state /*: State */, props /*: Props */) => { + selectNextElement = (state: State, props: Props) => { const { activeIdx } = state; const allElements = this.getAllElements(props, state); if (activeIdx < 0 || activeIdx >= allElements.length - 1) { @@ -209,7 +207,7 @@ export default class MultiSelect extends React.PureComponent { } }; - selectPreviousElement = (state /*: State */, props /*: Props */) => { + selectPreviousElement = (state: State, props: Props) => { const { activeIdx } = state; const allElements = this.getAllElements(props, state); if (activeIdx <= 0) { @@ -220,13 +218,13 @@ export default class MultiSelect extends React.PureComponent { } }; - toggleSelect(item /*: string */) { + toggleSelect = (item: string) => { if (this.props.selectedElements.indexOf(item) === -1) { this.onSelectItem(item); } else { this.onUnselectItem(item); } - } + }; render() { const { query, activeIdx, selectedElements, unselectedElements } = this.state; diff --git a/server/sonar-web/src/main/js/components/common/MultiSelectOption.js b/server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx index ad144f5cd9d..89429426d7e 100644 --- a/server/sonar-web/src/main/js/components/common/MultiSelectOption.js +++ b/server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx @@ -17,40 +17,27 @@ * 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 * as React from 'react'; +import * as classNames from 'classnames'; -/*:: -type Props = { - element: string, - selected: boolean, - custom: boolean, - active: boolean, - onSelectChange: (string, boolean) => void, - onHover: string => void -}; -*/ - -export default class MultiSelectOption extends React.PureComponent { - /*:: props: Props; */ - - static defaultProps = { - selected: false, - custom: false, - active: false - }; +interface Props { + element: string; + selected?: boolean; + custom?: boolean; + active?: boolean; + onSelectChange: (elem: string, selected: boolean) => void; + onHover: (elem: string) => void; +} - handleSelect = (evt /*: SyntheticInputEvent */) => { +export default class MultiSelectOption extends React.PureComponent<Props> { + handleSelect = (evt: React.SyntheticEvent<HTMLAnchorElement>) => { evt.stopPropagation(); evt.preventDefault(); - evt.target.blur(); + evt.currentTarget.blur(); this.props.onSelectChange(this.props.element, !this.props.selected); }; - handleHover = () => { - this.props.onHover(this.props.element); - }; + handleHover = () => this.props.onHover(this.props.element); render() { const className = classNames('icon-checkbox', { diff --git a/server/sonar-web/src/main/js/components/common/__tests__/MultiSelect-test.js b/server/sonar-web/src/main/js/components/common/__tests__/MultiSelect-test.tsx index c843a7952bb..13bb1651866 100644 --- a/server/sonar-web/src/main/js/components/common/__tests__/MultiSelect-test.js +++ b/server/sonar-web/src/main/js/components/common/__tests__/MultiSelect-test.tsx @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import * as React from 'react'; import { shallow, mount } from 'enzyme'; -import React from 'react'; import MultiSelect from '../MultiSelect'; const props = { diff --git a/server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.js b/server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.tsx index a15475bc149..5b72d18b21e 100644 --- a/server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.js +++ b/server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.tsx @@ -17,15 +17,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import * as React from 'react'; import { shallow } from 'enzyme'; -import React from 'react'; import MultiSelectOption from '../MultiSelectOption'; const props = { element: 'mytag', - selected: false, - custom: false, - active: false, onSelectChange: () => {}, onHover: () => {} }; diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelect-test.js.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelect-test.tsx.snap index 5af710f8bb1..32dac980bbe 100644 --- a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelect-test.js.snap +++ b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelect-test.tsx.snap @@ -19,7 +19,6 @@ exports[`should render multiselect with selected elements 1`] = ` > <MultiSelectOption active={true} - custom={false} element="bar" key="bar" onHover={[Function]} @@ -49,7 +48,6 @@ exports[`should render multiselect with selected elements 2`] = ` > <MultiSelectOption active={true} - custom={false} element="bar" key="bar" onHover={[Function]} @@ -58,21 +56,17 @@ exports[`should render multiselect with selected elements 2`] = ` /> <MultiSelectOption active={false} - custom={false} element="foo" key="foo" onHover={[Function]} onSelectChange={[Function]} - selected={false} /> <MultiSelectOption active={false} - custom={false} element="baz" key="baz" onHover={[Function]} onSelectChange={[Function]} - selected={false} /> </ul> </div> @@ -97,7 +91,6 @@ exports[`should render multiselect with selected elements 3`] = ` > <MultiSelectOption active={false} - custom={false} element="bar" key="bar" onHover={[Function]} @@ -106,21 +99,17 @@ exports[`should render multiselect with selected elements 3`] = ` /> <MultiSelectOption active={false} - custom={false} element="foo" key="foo" onHover={[Function]} onSelectChange={[Function]} - selected={false} /> <MultiSelectOption active={true} - custom={false} element="baz" key="baz" onHover={[Function]} onSelectChange={[Function]} - selected={false} /> </ul> </div> @@ -145,7 +134,6 @@ exports[`should render multiselect with selected elements 4`] = ` > <MultiSelectOption active={false} - custom={false} element="bar" key="bar" onHover={[Function]} @@ -154,21 +142,17 @@ exports[`should render multiselect with selected elements 4`] = ` /> <MultiSelectOption active={false} - custom={false} element="foo" key="foo" onHover={[Function]} onSelectChange={[Function]} - selected={false} /> <MultiSelectOption active={true} - custom={false} element="baz" key="baz" onHover={[Function]} onSelectChange={[Function]} - selected={false} /> <MultiSelectOption active={false} @@ -177,7 +161,6 @@ exports[`should render multiselect with selected elements 4`] = ` key="test" onHover={[Function]} onSelectChange={[Function]} - selected={false} /> </ul> </div> diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.js.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.tsx.snap index b7b9f2a61c0..b7b9f2a61c0 100644 --- a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.js.snap +++ b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.tsx.snap diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js index 30c93818fff..c334412d5c0 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js +++ b/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js @@ -77,7 +77,6 @@ export default class SetIssueTagsPopup extends React.PureComponent { render() { return ( - // $FlowFixMe `this.props.popupPosition` is passed from `BabelPopupHelper` <TagsSelector position={this.props.popupPosition} tags={this.state.searchResult} diff --git a/server/sonar-web/src/main/js/components/measure/Measure.tsx b/server/sonar-web/src/main/js/components/measure/Measure.tsx index 200035f8016..ae12c4c480f 100644 --- a/server/sonar-web/src/main/js/components/measure/Measure.tsx +++ b/server/sonar-web/src/main/js/components/measure/Measure.tsx @@ -21,39 +21,32 @@ import * as React from 'react'; import Rating from '../ui/Rating'; import Level from '../ui/Level'; import Tooltips from '../controls/Tooltip'; -import { formatMeasure, isDiffMetric, MeasureEnhanced } from '../../helpers/measures'; -import { formatLeak, getRatingTooltip } from './utils'; +import { formatMeasure } from '../../helpers/measures'; +import { getRatingTooltip } from './utils'; interface Props { className?: string; decimals?: number | null; - measure?: MeasureEnhanced; + value?: string; + metricKey: string; + metricType: string; } -export default function Measure({ className, decimals, measure }: Props) { - if (measure === undefined) { - return <span>{'–'}</span>; - } - - const { metric } = measure; - const value = isDiffMetric(metric.key) ? measure.leak : measure.value; - +export default function Measure({ className, decimals, metricKey, metricType, value }: Props) { if (value === undefined) { return <span>{'–'}</span>; } - if (metric.type === 'LEVEL') { + if (metricType === 'LEVEL') { return <Level className={className} level={value} />; } - if (metric.type !== 'RATING') { - const formattedValue = isDiffMetric(metric.key) - ? formatLeak(measure.leak, metric.key, metric.type, { decimals }) - : formatMeasure(measure.value, metric.type, { decimals }); + if (metricType !== 'RATING') { + const formattedValue = formatMeasure(value, metricType, { decimals }); return <span className={className}>{formattedValue != null ? formattedValue : '–'}</span>; } - const tooltip = getRatingTooltip(metric.key, Number(value)); + const tooltip = getRatingTooltip(metricKey, Number(value)); const rating = <Rating value={value} />; if (tooltip) { return ( diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx b/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx index a62f16c513d..84aaf15fdb3 100644 --- a/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx +++ b/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx @@ -17,55 +17,46 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* eslint-disable import/first */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import Measure from '../Measure'; + jest.mock('../../../helpers/measures', () => { const measures = require.requireActual('../../../helpers/measures'); measures.getRatingTooltip = jest.fn(() => 'tooltip'); return measures; }); -import * as React from 'react'; -import { shallow } from 'enzyme'; -import Measure from '../Measure'; - it('renders trivial measure', () => { - const measure = { metric: { key: 'coverage', name: 'Coverage', type: 'PERCENT' }, value: '73.0' }; - expect(shallow(<Measure measure={measure} />)).toMatchSnapshot(); + expect( + shallow(<Measure metricKey="coverage" metricType="PERCENT" value="73.0" />) + ).toMatchSnapshot(); }); it('renders leak measure', () => { - const measure = { - metric: { key: 'new_coverage', name: 'Coverage on New Code', type: 'PERCENT' }, - leak: '36.0' - }; - expect(shallow(<Measure measure={measure} />)).toMatchSnapshot(); + expect( + shallow(<Measure metricKey="new_coverage" metricType="PERCENT" value="36.0" />) + ).toMatchSnapshot(); }); it('renders LEVEL', () => { - const measure = { - metric: { key: 'quality_gate_status', name: 'Quality Gate', type: 'LEVEL' }, - value: 'ERROR' - }; - expect(shallow(<Measure measure={measure} />)).toMatchSnapshot(); + expect( + shallow(<Measure metricKey="quality_gate_status" metricType="LEVEL" value="ERROR" />) + ).toMatchSnapshot(); }); it('renders known RATING', () => { - const measure = { - metric: { key: 'sqale_rating', name: 'Maintainability Rating', type: 'RATING' }, - value: '3' - }; - expect(shallow(<Measure measure={measure} />)).toMatchSnapshot(); + expect( + shallow(<Measure metricKey="sqale_rating" metricType="RATING" value="3" />) + ).toMatchSnapshot(); }); it('renders unknown RATING', () => { - const measure = { - metric: { key: 'foo_rating', name: 'Foo Rating', type: 'RATING' }, - value: '4' - }; - expect(shallow(<Measure measure={measure} />)).toMatchSnapshot(); + expect( + shallow(<Measure metricKey="foo_rating" metricType="RATING" value="4" />) + ).toMatchSnapshot(); }); it('renders undefined measure', () => { - const measure = { metric: { key: 'foo', name: 'Foo', type: 'PERCENT' } }; - expect(shallow(<Measure measure={measure} />)).toMatchSnapshot(); + expect(shallow(<Measure metricKey="foo" metricType="PERCENT" />)).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/components/measure/utils.ts b/server/sonar-web/src/main/js/components/measure/utils.ts index 8e932892152..626c65f552f 100644 --- a/server/sonar-web/src/main/js/components/measure/utils.ts +++ b/server/sonar-web/src/main/js/components/measure/utils.ts @@ -18,8 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { - formatMeasure, - formatMeasureVariation, getRatingTooltip as nextGetRatingTooltip, isDiffMetric, Measure, @@ -40,19 +38,6 @@ export function enhanceMeasure( }; } -export function formatLeak( - value: string | undefined, - metricKey: string, - metricType: string, - options: any -): string { - if (isDiffMetric(metricKey)) { - return formatMeasure(value, metricType, options); - } else { - return formatMeasureVariation(value, metricType, options); - } -} - export function getLeakValue(measure: Measure | undefined): string | undefined { if (!measure || !measure.periods) { return undefined; diff --git a/server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.d.ts b/server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.d.ts new file mode 100644 index 00000000000..744eed0493d --- /dev/null +++ b/server/sonar-web/src/main/js/components/preview-graph/PreviewGraph.d.ts @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { History } from '../../api/time-machine'; +import { Metric } from '../../app/types'; + +interface Props { + branch?: string; + history?: History; + metrics: Metric[]; + project: string; + renderWhenEmpty?: () => void; +} + +export default class PreviewGraph extends React.Component<Props> {} diff --git a/server/sonar-web/src/main/js/components/shared/drilldown-link.js b/server/sonar-web/src/main/js/components/shared/DrilldownLink.tsx index cdff767a2d9..1065e91e9ef 100644 --- a/server/sonar-web/src/main/js/components/shared/drilldown-link.js +++ b/server/sonar-web/src/main/js/components/shared/DrilldownLink.tsx @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import * as React from 'react'; import { Link } from 'react-router'; import { getComponentDrilldownUrl, getComponentIssuesUrl } from '../../helpers/urls'; @@ -47,21 +46,22 @@ const ISSUE_MEASURES = [ 'new_vulnerabilities' ]; -export class DrilldownLink extends React.PureComponent { - static propTypes = { - branch: PropTypes.string, - children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]), - className: PropTypes.string, - component: PropTypes.string.isRequired, - metric: PropTypes.string.isRequired, - sinceLeakPeriod: PropTypes.bool - }; +interface Props { + branch?: string; + children?: React.ReactNode; + className?: string; + component: string; + metric: string; + sinceLeakPeriod?: boolean; +} + +export default class DrilldownLink extends React.PureComponent<Props> { isIssueMeasure = () => { return ISSUE_MEASURES.indexOf(this.props.metric) !== -1; }; propsToIssueParams = () => { - const params = {}; + const params: { [key: string]: string | boolean } = {}; if (this.props.sinceLeakPeriod) { params.sinceLeakPeriod = true; diff --git a/server/sonar-web/src/main/js/components/tags/TagsSelector.js b/server/sonar-web/src/main/js/components/tags/TagsSelector.tsx index c257c31a5ed..f9ed202989c 100644 --- a/server/sonar-web/src/main/js/components/tags/TagsSelector.js +++ b/server/sonar-web/src/main/js/components/tags/TagsSelector.tsx @@ -17,26 +17,23 @@ * 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 BubblePopup from '../common/BubblePopup'; +import * as React from 'react'; +import BubblePopup, { BubblePopupPosition } from '../common/BubblePopup'; import MultiSelect from '../common/MultiSelect'; import { translate } from '../../helpers/l10n'; import './TagsList.css'; -/*:: -type Props = { - position: {}, - tags: Array<string>, - selectedTags: Array<string>, - listSize: number, - onSearch: string => void, - onSelect: string => void, - onUnselect: string => void -}; -*/ +interface Props { + position: BubblePopupPosition; + tags: string[]; + selectedTags: string[]; + listSize: number; + onSearch: (query: string) => void; + onSelect: (item: string) => void; + onUnselect: (item: string) => void; +} -export default function TagsSelector(props /*: Props */) { +export default function TagsSelector(props: Props) { return ( <BubblePopup position={props.position} @@ -55,7 +52,7 @@ export default function TagsSelector(props /*: Props */) { ); } -export function validateTag(value /*: string */) { +export function validateTag(value: string) { // Allow only a-z, 0-9, '+', '-', '#', '.' return value.toLowerCase().replace(/[^a-z0-9\+\-#.]/gi, ''); } diff --git a/server/sonar-web/src/main/js/components/tags/__tests__/TagsSelector-test.js b/server/sonar-web/src/main/js/components/tags/__tests__/TagsSelector-test.tsx index 98186daca4a..1b5169a2940 100644 --- a/server/sonar-web/src/main/js/components/tags/__tests__/TagsSelector-test.js +++ b/server/sonar-web/src/main/js/components/tags/__tests__/TagsSelector-test.tsx @@ -17,12 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import * as React from 'react'; import { shallow } from 'enzyme'; -import React from 'react'; import TagsSelector, { validateTag } from '../TagsSelector'; const props = { - position: { left: 0, top: 0 }, + position: { right: 0, top: 0 }, + listSize: 10, tags: ['foo', 'bar', 'baz'], selectedTags: ['bar'], onSearch: () => {}, diff --git a/server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsSelector-test.js.snap b/server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsSelector-test.tsx.snap index e2d57569b31..c2d4623b8c8 100644 --- a/server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsSelector-test.js.snap +++ b/server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsSelector-test.tsx.snap @@ -5,7 +5,7 @@ exports[`should render with selected tags 1`] = ` customClass="bubble-popup-bottom-right bubble-popup-menu abs-width-300" position={ Object { - "left": 0, + "right": 0, "top": 0, } } @@ -38,7 +38,7 @@ exports[`should render without tags at all 1`] = ` customClass="bubble-popup-bottom-right bubble-popup-menu abs-width-300" position={ Object { - "left": 0, + "right": 0, "top": 0, } } |