/* * 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 * as PropTypes from 'prop-types'; import * as classNames from 'classnames'; import Components from './Components'; import { getTree } from '../../../api/components'; import SearchBox from '../../../components/controls/SearchBox'; import { getBranchLikeQuery } from '../../../helpers/branches'; import { translate } from '../../../helpers/l10n'; import { getProjectUrl } from '../../../helpers/urls'; interface Props { branchLike?: T.BranchLike; component: T.ComponentMeasure; location: {}; } interface State { query: string; loading: boolean; results?: T.ComponentMeasure[]; selectedIndex?: number; } export default class Search extends React.PureComponent { mounted = false; static contextTypes = { router: PropTypes.object.isRequired }; state: State = { query: '', loading: false }; componentDidMount() { this.mounted = true; } componentWillReceiveProps(nextProps: Props) { // if the url has change, reset the current state if (nextProps.location !== this.props.location) { this.setState({ query: '', loading: false, results: undefined, selectedIndex: undefined }); } } componentWillUnmount() { this.mounted = false; } handleSelectNext() { const { selectedIndex, results } = this.state; if (results && selectedIndex !== undefined && selectedIndex < results.length - 1) { this.setState({ selectedIndex: selectedIndex + 1 }); } } handleSelectPrevious() { const { selectedIndex, results } = this.state; if (results && selectedIndex !== undefined && selectedIndex > 0) { this.setState({ selectedIndex: selectedIndex - 1 }); } } handleSelectCurrent() { const { branchLike, component } = this.props; const { results, selectedIndex } = this.state; if (results && selectedIndex !== undefined) { const selected = results[selectedIndex]; if (selected.refKey) { this.context.router.push(getProjectUrl(selected.refKey)); } else { this.context.router.push({ pathname: '/code', query: { id: component.key, selected: selected.key, ...getBranchLikeQuery(branchLike) } }); } } } handleKeyDown = (event: React.KeyboardEvent) => { switch (event.keyCode) { case 13: event.preventDefault(); this.handleSelectCurrent(); break; case 38: event.preventDefault(); this.handleSelectPrevious(); break; case 40: event.preventDefault(); this.handleSelectNext(); break; default: // do nothing } }; handleSearch = (query: string) => { if (this.mounted) { const { branchLike, component } = this.props; this.setState({ loading: true }); const isPortfolio = ['VW', 'SVW', 'APP'].includes(component.qualifier); const qualifiers = isPortfolio ? 'SVW,TRK' : 'BRC,UTS,FIL'; getTree({ component: component.key, q: query, s: 'qualifier,name', qualifiers, ...getBranchLikeQuery(branchLike) }) .then(r => { if (this.mounted) { this.setState({ results: r.components, selectedIndex: r.components.length > 0 ? 0 : undefined, loading: false }); } }) .catch(() => { if (this.mounted) { this.setState({ loading: false }); } }); } }; handleQueryChange = (query: string) => { this.setState({ query }); if (query.length === 0) { this.setState({ results: undefined }); } else { this.handleSearch(query); } }; render() { const { component } = this.props; const { loading, selectedIndex, results } = this.state; const selected = selectedIndex !== undefined && results ? results[selectedIndex] : undefined; const containerClassName = classNames('code-search', { 'code-search-with-results': Boolean(results) }); const isPortfolio = ['VW', 'SVW', 'APP'].includes(component.qualifier); return (