From: Grégoire Aubert Date: Tue, 2 May 2017 14:32:17 +0000 (+0200) Subject: SONAR-8870 Search by WS description X-Git-Tag: 6.4-RC1~104 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=ff4be625bf14bfc769b8872bb4aa8a49efd6ec8a;p=sonarqube.git SONAR-8870 Search by WS description --- diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Domain.js b/server/sonar-web/src/main/js/apps/web-api/components/Domain.js index 6e2ac633fcf..45513d9f9e7 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/Domain.js +++ b/server/sonar-web/src/main/js/apps/web-api/components/Domain.js @@ -22,7 +22,7 @@ import React from 'react'; import Action from './Action'; import DeprecatedBadge from './DeprecatedBadge'; import InternalBadge from './InternalBadge'; -import { getActionKey } from '../utils'; +import { getActionKey, actionsFilter } from '../utils'; import type { Domain as DomainType } from '../../../api/web-api'; type Props = { @@ -37,10 +37,9 @@ export default class Domain extends React.PureComponent { render() { const { domain, showInternal, showDeprecated, searchQuery } = this.props; - const filteredActions = domain.actions - .filter(action => showInternal || !action.internal) - .filter(action => showDeprecated || !action.deprecatedSince) - .filter(action => getActionKey(domain.path, action.key).indexOf(searchQuery) !== -1); + const filteredActions = domain.actions.filter(action => + actionsFilter(showDeprecated, showInternal, searchQuery, domain, action) + ); return (
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Menu.js b/server/sonar-web/src/main/js/apps/web-api/components/Menu.js index f7cd574fd41..566533c79c7 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/Menu.js +++ b/server/sonar-web/src/main/js/apps/web-api/components/Menu.js @@ -24,7 +24,7 @@ import classNames from 'classnames'; import DeprecatedBadge from './DeprecatedBadge'; import InternalBadge from './InternalBadge'; import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin'; -import { getActionKey, isDomainPathActive } from '../utils'; +import { isDomainPathActive, actionsFilter } from '../utils'; import type { Domain as DomainType } from '../../../api/web-api'; type Props = { @@ -42,10 +42,9 @@ export default class Menu extends React.PureComponent { const { domains, showInternal, showDeprecated, searchQuery, splat } = this.props; const filteredDomains = (domains || []) .map(domain => { - const filteredActions = domain.actions - .filter(action => showInternal || !action.internal) - .filter(action => showDeprecated || !action.deprecatedSince) - .filter(action => getActionKey(domain.path, action.key).indexOf(searchQuery) !== -1); + const filteredActions = domain.actions.filter(action => + actionsFilter(showDeprecated, showInternal, searchQuery, domain, action) + ); return { ...domain, filteredActions }; }) .filter(domain => domain.filteredActions.length); diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Search.js b/server/sonar-web/src/main/js/apps/web-api/components/Search.js index c43163ed951..f5971512023 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/Search.js +++ b/server/sonar-web/src/main/js/apps/web-api/components/Search.js @@ -44,19 +44,19 @@ export default class Search extends React.PureComponent { constructor(props: Props) { super(props); this.state = { query: '' }; - this.actuallySearch = debounce(this.actuallySearch.bind(this), 250); + this.actuallySearch = debounce(this.actuallySearch, 250); } - handleSearch(e: SyntheticInputEvent) { + handleSearch = (e: SyntheticInputEvent) => { const { value } = e.target; this.setState({ query: value }); this.actuallySearch(); - } + }; - actuallySearch() { + actuallySearch = () => { const { onSearch } = this.props; onSearch(this.state.query); - } + }; render() { const { showInternal, showDeprecated, onToggleInternal, onToggleDeprecated } = this.props; @@ -70,14 +70,17 @@ export default class Search extends React.PureComponent { type="search" value={this.state.query} placeholder={translate('search_verb')} - onChange={this.handleSearch.bind(this)} + onChange={this.handleSearch} />
- - {' '} +
- - {' '} + { const splat = this.props.params.splat || ''; this.scrollToElement(splat); - } + }; scrollToElement(id: string) { const element = document.getElementById(id); @@ -113,11 +112,11 @@ export default class WebApiApp extends React.PureComponent { } } - handleSearch(searchQuery: string) { + handleSearch = (searchQuery: string) => { this.setState({ searchQuery }); - } + }; - handleToggleInternal() { + handleToggleInternal = () => { const splat = this.props.params.splat || ''; const { router } = this.context; const { domains } = this.state; @@ -129,11 +128,11 @@ export default class WebApiApp extends React.PureComponent { } this.setState({ showInternal }); - } + }; - handleToggleDeprecated() { + handleToggleDeprecated = () => { this.setState(state => ({ showDeprecated: !state.showDeprecated })); - } + }; render() { const splat = this.props.params.splat || ''; @@ -153,9 +152,9 @@ export default class WebApiApp extends React.PureComponent { { - const actions = [ - { - key: 'foo', - deprecatedSince: '5.0' - } - ]; + const actions = [{ key: 'foo', deprecatedSince: '5.0' }]; const domain = { actions, path: 'api' }; expect( shallow() @@ -35,14 +30,35 @@ it('should render deprecated actions', () => { }); it('should not render deprecated actions', () => { - const actions = [ - { - key: 'foo', - deprecatedSince: '5.0' - } - ]; + const actions = [{ key: 'foo', deprecatedSince: '5.0' }]; const domain = { actions, path: 'api' }; expect( shallow() ).toMatchSnapshot(); }); + +it('should render internal actions', () => { + const actions = [{ key: 'foo', internal: true }]; + const domain = { actions, path: 'api' }; + expect(shallow()).toMatchSnapshot(); +}); + +it('should not render internal actions', () => { + const actions = [{ key: 'foo', internal: true }]; + const domain = { actions, path: 'api' }; + expect(shallow()).toMatchSnapshot(); +}); + +it('should render only actions matching the query', () => { + const actions = [{ key: 'foo' }, { key: 'bar' }]; + const domain = { actions, path: 'api' }; + expect(shallow()).toMatchSnapshot(); +}); + +it('should also render actions with a description matching the query', () => { + const actions = [{ key: 'foo', description: 'foobar' }, { key: 'bar' }, { key: 'baz' }]; + const domain = { actions, path: 'api' }; + expect( + shallow() + ).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Domain-test.js.snap b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Domain-test.js.snap index 22a9c18b1cb..8a1c7fd8843 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Domain-test.js.snap +++ b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Domain-test.js.snap @@ -1,5 +1,76 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`should also render actions with a description matching the query 1`] = ` +
+
+

+ api +

+
+
+ + +
+
+`; + exports[`should not render deprecated actions 1`] = `
`; +exports[`should not render internal actions 1`] = ` +
+
+

+ api +

+
+
+
+`; + exports[`should render deprecated actions 1`] = `
`; + +exports[`should render internal actions 1`] = ` +
+
+

+ api +

+
+
+ +
+
+`; + +exports[`should render only actions matching the query 1`] = ` +
+
+

+ api +

+
+
+ +
+
+`; diff --git a/server/sonar-web/src/main/js/apps/web-api/utils.js b/server/sonar-web/src/main/js/apps/web-api/utils.js index d5af23b6740..bd6efd34467 100644 --- a/server/sonar-web/src/main/js/apps/web-api/utils.js +++ b/server/sonar-web/src/main/js/apps/web-api/utils.js @@ -17,11 +17,30 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -export function getActionKey(domain, action) { - return domain + '/' + action; +//@flow +import type { Domain, Action } from '../../api/web-api'; + +export function actionsFilter( + showDeprecated: boolean, + showInternal: boolean, + searchQuery: string, + domain: Domain, + action: Action +) { + const lowSearchQuery = searchQuery.toLowerCase(); + return ( + (showInternal || !action.internal) && + (showDeprecated || !action.deprecatedSince) && + (getActionKey(domain.path, action.key).includes(lowSearchQuery) || + (action.description || '').toLowerCase().includes(lowSearchQuery)) + ); +} + +export function getActionKey(domainPath: string, actionKey: string) { + return domainPath + '/' + actionKey; } -export const isDomainPathActive = (path, splat) => { +export const isDomainPathActive = (path: string, splat: string) => { const pathTokens = path.split('/'); const splatTokens = splat.split('/');