mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[Code] Apply default search scope into fetching suggestions (#36494)
* [Code] adjust setup guide * [Code] Apply default search scope into fetching suggestions * [Code] Refactor the search bar in the source view page to use the same searchbar in other pages
This commit is contained in:
parent
5f56c30ffd
commit
ef8d7ea905
14 changed files with 81 additions and 197 deletions
|
@ -153,3 +153,9 @@ export enum SearchScope {
|
||||||
REPOSITORY = 'repository', // Only search repositories
|
REPOSITORY = 'repository', // Only search repositories
|
||||||
FILE = 'file', // Only search files
|
FILE = 'file', // Only search files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SearchOptions {
|
||||||
|
repoScope: Repository[];
|
||||||
|
defaultRepoScopeOn: boolean;
|
||||||
|
defaultRepoScope?: Repository;
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createAction } from 'redux-actions';
|
import { createAction } from 'redux-actions';
|
||||||
import { DocumentSearchResult, Repository, SearchScope } from '../../model';
|
import { DocumentSearchResult, Repository, SearchOptions, SearchScope } from '../../model';
|
||||||
|
|
||||||
export interface DocumentSearchPayload {
|
export interface DocumentSearchPayload {
|
||||||
query: string;
|
query: string;
|
||||||
|
@ -19,11 +19,6 @@ export interface RepositorySearchPayload {
|
||||||
query: string;
|
query: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchOptions {
|
|
||||||
repoScope: Repository[];
|
|
||||||
defaultRepoScopeOn: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For document search page
|
// For document search page
|
||||||
export const documentSearch = createAction<DocumentSearchPayload>('DOCUMENT SEARCH');
|
export const documentSearch = createAction<DocumentSearchPayload>('DOCUMENT SEARCH');
|
||||||
export const documentSearchSuccess = createAction<DocumentSearchResult>('DOCUMENT SEARCH SUCCESS');
|
export const documentSearchSuccess = createAction<DocumentSearchResult>('DOCUMENT SEARCH SUCCESS');
|
||||||
|
@ -45,7 +40,7 @@ export const repositoryTypeaheadSearchFailed = createAction<string>('REPOSITORY
|
||||||
|
|
||||||
export const saveSearchOptions = createAction<SearchOptions>('SAVE SEARCH OPTIONS');
|
export const saveSearchOptions = createAction<SearchOptions>('SAVE SEARCH OPTIONS');
|
||||||
|
|
||||||
export const turnOnDefaultRepoScope = createAction('TURN ON DEFAULT REPO SCOPE');
|
export const turnOnDefaultRepoScope = createAction<Repository>('TURN ON DEFAULT REPO SCOPE');
|
||||||
|
|
||||||
export const searchReposForScope = createAction<RepositorySearchPayload>('SEARCH REPOS FOR SCOPE');
|
export const searchReposForScope = createAction<RepositorySearchPayload>('SEARCH REPOS FOR SCOPE');
|
||||||
export const searchReposForScopeSuccess = createAction<any>('SEARCH REPOS FOR SCOPE SUCCESS');
|
export const searchReposForScopeSuccess = createAction<any>('SEARCH REPOS FOR SCOPE SUCCESS');
|
||||||
|
|
|
@ -10,8 +10,8 @@ import { connect } from 'react-redux';
|
||||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import { EuiTab, EuiTabs } from '@elastic/eui';
|
import { EuiTab, EuiTabs } from '@elastic/eui';
|
||||||
import { Repository, SearchScope } from '../../../model';
|
import { Repository, SearchOptions, SearchScope } from '../../../model';
|
||||||
import { changeSearchScope, SearchOptions } from '../../actions';
|
import { changeSearchScope } from '../../actions';
|
||||||
import { RootState } from '../../reducers';
|
import { RootState } from '../../reducers';
|
||||||
import { SearchBar } from '../search_bar';
|
import { SearchBar } from '../search_bar';
|
||||||
import { EmptyProject } from './empty_project';
|
import { EmptyProject } from './empty_project';
|
||||||
|
@ -126,9 +126,10 @@ class AdminPage extends React.PureComponent<Props, State> {
|
||||||
<div className="codeContainer__rootInner">
|
<div className="codeContainer__rootInner">
|
||||||
<div className="codeContainer__adminWrapper">
|
<div className="codeContainer__adminWrapper">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
repoScope={this.props.searchOptions.repoScope.map(r => r.uri)}
|
searchOptions={this.props.searchOptions}
|
||||||
query={this.props.query}
|
query={this.props.query}
|
||||||
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
||||||
|
enableSubmitWhenOptionsChanged={false}
|
||||||
ref={element => (this.searchBar = element)}
|
ref={element => (this.searchBar = element)}
|
||||||
/>
|
/>
|
||||||
<div className="codeContainer__adminMain">
|
<div className="codeContainer__adminMain">
|
||||||
|
|
|
@ -141,10 +141,11 @@ class SetupGuidePage extends React.PureComponent<{ setupOk?: boolean }, { hideTo
|
||||||
)}
|
)}
|
||||||
{this.props.setupOk === true && (
|
{this.props.setupOk === true && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<EuiSpacer size="xs" />
|
<EuiSpacer size="s" />
|
||||||
<EuiButton iconType="sortLeft">
|
<EuiButton iconType="sortLeft">
|
||||||
<Link to="/admin">Back To Project Dashboard</Link>
|
<Link to="/admin">Back To Project Dashboard</Link>
|
||||||
</EuiButton>
|
</EuiButton>
|
||||||
|
<EuiSpacer size="s" />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
<EuiPanel>
|
<EuiPanel>
|
||||||
|
@ -158,7 +159,7 @@ class SetupGuidePage extends React.PureComponent<{ setupOk?: boolean }, { hideTo
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <div className="condeContainer__setup">{setup}</div>;
|
return <div className="codeContainer__setup">{setup}</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { CommitDiff, FileDiff } from '../../../common/git_diff';
|
||||||
import { SearchScope } from '../../../model';
|
import { SearchScope } from '../../../model';
|
||||||
import { changeSearchScope } from '../../actions';
|
import { changeSearchScope } from '../../actions';
|
||||||
import { RootState } from '../../reducers';
|
import { RootState } from '../../reducers';
|
||||||
import { SearchBar } from '../search_bar';
|
// import { SearchBar } from '../search_bar';
|
||||||
import { DiffEditor } from './diff_editor';
|
import { DiffEditor } from './diff_editor';
|
||||||
|
|
||||||
const COMMIT_ID_LENGTH = 16;
|
const COMMIT_ID_LENGTH = 16;
|
||||||
|
@ -191,11 +191,11 @@ export class DiffPage extends React.Component<Props> {
|
||||||
));
|
));
|
||||||
return (
|
return (
|
||||||
<div className="diff">
|
<div className="diff">
|
||||||
<SearchBar
|
{/* <SearchBar
|
||||||
repoScope={this.props.repoScope}
|
repoScope={this.props.repoScope}
|
||||||
query={this.props.query}
|
query={this.props.query}
|
||||||
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
||||||
/>
|
/> */}
|
||||||
{topBar}
|
{topBar}
|
||||||
<Container>
|
<Container>
|
||||||
<EuiText>{commit.commit.message}</EuiText>
|
<EuiText>{commit.commit.message}</EuiText>
|
||||||
|
|
|
@ -17,12 +17,12 @@ import { RepositoryUtils } from '../../../common/repository_utils';
|
||||||
import {
|
import {
|
||||||
FileTree,
|
FileTree,
|
||||||
FileTreeItemType,
|
FileTreeItemType,
|
||||||
|
SearchOptions,
|
||||||
SearchScope,
|
SearchScope,
|
||||||
WorkerReservedProgress,
|
WorkerReservedProgress,
|
||||||
Repository,
|
|
||||||
} from '../../../model';
|
} from '../../../model';
|
||||||
import { CommitInfo, ReferenceInfo } from '../../../model/commit';
|
import { CommitInfo, ReferenceInfo } from '../../../model/commit';
|
||||||
import { changeSearchScope, FetchFileResponse, SearchOptions } from '../../actions';
|
import { changeSearchScope, FetchFileResponse } from '../../actions';
|
||||||
import { MainRouteParams, PathTypes } from '../../common/types';
|
import { MainRouteParams, PathTypes } from '../../common/types';
|
||||||
import { RepoState, RepoStatus, RootState } from '../../reducers';
|
import { RepoState, RepoStatus, RootState } from '../../reducers';
|
||||||
import {
|
import {
|
||||||
|
@ -53,8 +53,8 @@ interface Props extends RouteComponentProps<MainRouteParams> {
|
||||||
onSearchScopeChanged: (s: SearchScope) => void;
|
onSearchScopeChanged: (s: SearchScope) => void;
|
||||||
repoScope: string[];
|
repoScope: string[];
|
||||||
searchOptions: SearchOptions;
|
searchOptions: SearchOptions;
|
||||||
currentRepository?: Repository;
|
|
||||||
fileTreeLoading: boolean;
|
fileTreeLoading: boolean;
|
||||||
|
query: string;
|
||||||
}
|
}
|
||||||
const LANG_MD = 'markdown';
|
const LANG_MD = 'markdown';
|
||||||
|
|
||||||
|
@ -224,12 +224,12 @@ class CodeContent extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<div className="codeContainer__main">
|
<div className="codeContainer__main">
|
||||||
<TopBar
|
<TopBar
|
||||||
defaultSearchScope={this.props.currentRepository}
|
|
||||||
routeParams={this.props.match.params}
|
routeParams={this.props.match.params}
|
||||||
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
||||||
buttons={this.renderButtons()}
|
buttons={this.renderButtons()}
|
||||||
searchOptions={this.props.searchOptions}
|
searchOptions={this.props.searchOptions}
|
||||||
branches={this.props.branches}
|
branches={this.props.branches}
|
||||||
|
query={this.props.query}
|
||||||
/>
|
/>
|
||||||
{this.renderContent()}
|
{this.renderContent()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -395,7 +395,7 @@ const mapStateToProps = (state: RootState) => ({
|
||||||
loadingCommits: state.file.loadingCommits,
|
loadingCommits: state.file.loadingCommits,
|
||||||
repoStatus: statusSelector(state, repoUriSelector(state)),
|
repoStatus: statusSelector(state, repoUriSelector(state)),
|
||||||
searchOptions: state.search.searchOptions,
|
searchOptions: state.search.searchOptions,
|
||||||
currentRepository: state.repository.currentRepository,
|
query: state.search.query,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ParsedUrlQuery } from 'querystring';
|
|
||||||
import React from 'react';
|
|
||||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
|
||||||
import url from 'url';
|
|
||||||
|
|
||||||
import { unique } from 'lodash';
|
|
||||||
import { SearchScope, Repository } from '../../../model';
|
|
||||||
import { MainRouteParams, SearchScopeText } from '../../common/types';
|
|
||||||
import {
|
|
||||||
AutocompleteSuggestion,
|
|
||||||
FileSuggestionsProvider,
|
|
||||||
QueryBar,
|
|
||||||
RepositorySuggestionsProvider,
|
|
||||||
SymbolSuggestionsProvider,
|
|
||||||
} from '../query_bar';
|
|
||||||
import { Shortcut } from '../shortcuts';
|
|
||||||
import { SearchOptions } from '../../actions';
|
|
||||||
|
|
||||||
interface Props extends RouteComponentProps<MainRouteParams> {
|
|
||||||
onSearchScopeChanged: (s: SearchScope) => void;
|
|
||||||
searchOptions: SearchOptions;
|
|
||||||
defaultSearchScope?: Repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(mengwei): refactor this with the SearchBar in ../search_bar/
|
|
||||||
export class CodeSearchBar extends React.Component<Props> {
|
|
||||||
public state = {
|
|
||||||
searchScope: SearchScope.DEFAULT,
|
|
||||||
};
|
|
||||||
|
|
||||||
public queryBar: any | null = null;
|
|
||||||
|
|
||||||
public suggestionProviders = [
|
|
||||||
new SymbolSuggestionsProvider(),
|
|
||||||
new FileSuggestionsProvider(),
|
|
||||||
new RepositorySuggestionsProvider(),
|
|
||||||
];
|
|
||||||
|
|
||||||
public onSubmit = (queryString: string) => {
|
|
||||||
const { history } = this.props;
|
|
||||||
if (queryString.trim().length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const query: ParsedUrlQuery = {
|
|
||||||
q: queryString,
|
|
||||||
};
|
|
||||||
if (this.props.searchOptions.repoScope) {
|
|
||||||
// search from a repo page may have a default scope of this repo
|
|
||||||
if (this.props.searchOptions.defaultRepoScopeOn && this.props.defaultSearchScope) {
|
|
||||||
query.repoScope = unique([
|
|
||||||
...this.props.searchOptions.repoScope.map(r => r.uri),
|
|
||||||
this.props.defaultSearchScope.uri,
|
|
||||||
]).join(',');
|
|
||||||
} else {
|
|
||||||
query.repoScope = this.props.searchOptions.repoScope.map(r => r.uri).join(',');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.state.searchScope === SearchScope.REPOSITORY) {
|
|
||||||
query.scope = SearchScope.REPOSITORY;
|
|
||||||
}
|
|
||||||
history.push(url.format({ pathname: '/search', query }));
|
|
||||||
};
|
|
||||||
|
|
||||||
public onSelect = (item: AutocompleteSuggestion) => {
|
|
||||||
this.props.history.push(item.selectUrl);
|
|
||||||
};
|
|
||||||
|
|
||||||
public render() {
|
|
||||||
return (
|
|
||||||
<div className="codeContainer__searchBar">
|
|
||||||
<Shortcut
|
|
||||||
keyCode="p"
|
|
||||||
help={SearchScopeText[SearchScope.REPOSITORY]}
|
|
||||||
onPress={() => {
|
|
||||||
this.props.onSearchScopeChanged(SearchScope.REPOSITORY);
|
|
||||||
if (this.queryBar) {
|
|
||||||
this.queryBar.focusInput();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Shortcut
|
|
||||||
keyCode="y"
|
|
||||||
help={SearchScopeText[SearchScope.SYMBOL]}
|
|
||||||
onPress={() => {
|
|
||||||
this.props.onSearchScopeChanged(SearchScope.SYMBOL);
|
|
||||||
if (this.queryBar) {
|
|
||||||
this.queryBar.focusInput();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Shortcut
|
|
||||||
keyCode="s"
|
|
||||||
help={SearchScopeText[SearchScope.DEFAULT]}
|
|
||||||
onPress={() => {
|
|
||||||
this.props.onSearchScopeChanged(SearchScope.DEFAULT);
|
|
||||||
if (this.queryBar) {
|
|
||||||
this.queryBar.focusInput();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<QueryBar
|
|
||||||
query=""
|
|
||||||
onSubmit={this.onSubmit}
|
|
||||||
onSelect={this.onSelect}
|
|
||||||
appName="code"
|
|
||||||
disableAutoFocus={true}
|
|
||||||
suggestionProviders={this.suggestionProviders}
|
|
||||||
enableSubmitWhenOptionsChanged={false}
|
|
||||||
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
|
||||||
ref={instance => {
|
|
||||||
if (instance) {
|
|
||||||
// @ts-ignore
|
|
||||||
this.queryBar = instance.getWrappedInstance();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SearchBar = withRouter(CodeSearchBar);
|
|
|
@ -6,14 +6,13 @@
|
||||||
|
|
||||||
import { EuiFlexGroup, EuiFlexItem, EuiSelect } from '@elastic/eui';
|
import { EuiFlexGroup, EuiFlexItem, EuiSelect } from '@elastic/eui';
|
||||||
import React, { ChangeEvent } from 'react';
|
import React, { ChangeEvent } from 'react';
|
||||||
import { SearchScope, Repository } from '../../../model';
|
import { SearchOptions, SearchScope } from '../../../model';
|
||||||
import { ReferenceInfo } from '../../../model/commit';
|
import { ReferenceInfo } from '../../../model/commit';
|
||||||
import { MainRouteParams } from '../../common/types';
|
import { MainRouteParams } from '../../common/types';
|
||||||
import { encodeRevisionString } from '../../utils/url';
|
import { encodeRevisionString } from '../../utils/url';
|
||||||
import { history } from '../../utils/url';
|
import { history } from '../../utils/url';
|
||||||
import { Breadcrumb } from './breadcrumb';
|
import { Breadcrumb } from './breadcrumb';
|
||||||
import { SearchBar } from './search_bar';
|
import { SearchBar } from '../search_bar';
|
||||||
import { SearchOptions } from '../../actions';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
routeParams: MainRouteParams;
|
routeParams: MainRouteParams;
|
||||||
|
@ -21,7 +20,7 @@ interface Props {
|
||||||
buttons: React.ReactNode;
|
buttons: React.ReactNode;
|
||||||
searchOptions: SearchOptions;
|
searchOptions: SearchOptions;
|
||||||
branches: ReferenceInfo[];
|
branches: ReferenceInfo[];
|
||||||
defaultSearchScope?: Repository;
|
query: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TopBar extends React.Component<Props, { value: string }> {
|
export class TopBar extends React.Component<Props, { value: string }> {
|
||||||
|
@ -44,8 +43,9 @@ export class TopBar extends React.Component<Props, { value: string }> {
|
||||||
return (
|
return (
|
||||||
<div className="code-top-bar__container">
|
<div className="code-top-bar__container">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
defaultSearchScope={this.props.defaultSearchScope}
|
query={this.props.query}
|
||||||
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
||||||
|
enableSubmitWhenOptionsChanged={false}
|
||||||
searchOptions={this.props.searchOptions}
|
searchOptions={this.props.searchOptions}
|
||||||
/>
|
/>
|
||||||
<EuiFlexGroup gutterSize="none" justifyContent="spaceBetween">
|
<EuiFlexGroup gutterSize="none" justifyContent="spaceBetween">
|
||||||
|
|
|
@ -22,8 +22,7 @@ import {
|
||||||
import { EuiIcon } from '@elastic/eui';
|
import { EuiIcon } from '@elastic/eui';
|
||||||
import { unique } from 'lodash';
|
import { unique } from 'lodash';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Repository } from '../../../../model';
|
import { SearchOptions as ISearchOptions, Repository } from '../../../../model';
|
||||||
import { SearchOptions as ISearchOptions } from '../../../actions';
|
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
isFlyoutOpen: boolean;
|
isFlyoutOpen: boolean;
|
||||||
|
|
|
@ -9,15 +9,11 @@ import React, { Component } from 'react';
|
||||||
|
|
||||||
import { EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiOutsideClickDetector } from '@elastic/eui';
|
import { EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiOutsideClickDetector } from '@elastic/eui';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import { saveSearchOptions, searchReposForScope } from '../../../actions';
|
||||||
saveSearchOptions,
|
|
||||||
SearchOptions as ISearchOptions,
|
|
||||||
searchReposForScope,
|
|
||||||
} from '../../../actions';
|
|
||||||
import { matchPairs } from '../lib/match_pairs';
|
import { matchPairs } from '../lib/match_pairs';
|
||||||
import { SuggestionsComponent } from './typeahead/suggestions_component';
|
import { SuggestionsComponent } from './typeahead/suggestions_component';
|
||||||
|
|
||||||
import { SearchScope, Repository } from '../../../../model';
|
import { SearchOptions as ISearchOptions, SearchScope, Repository } from '../../../../model';
|
||||||
import { SearchScopePlaceholderText } from '../../../common/types';
|
import { SearchScopePlaceholderText } from '../../../common/types';
|
||||||
import { RootState } from '../../../reducers';
|
import { RootState } from '../../../reducers';
|
||||||
import {
|
import {
|
||||||
|
@ -194,11 +190,15 @@ export class CodeQueryBar extends Component<Props, State> {
|
||||||
|
|
||||||
const res = await Promise.all(
|
const res = await Promise.all(
|
||||||
this.props.suggestionProviders.map((provider: SuggestionsProvider) => {
|
this.props.suggestionProviders.map((provider: SuggestionsProvider) => {
|
||||||
return provider.getSuggestions(
|
// Merge the default repository scope if necessary.
|
||||||
query,
|
const repoScopes = this.props.searchOptions.repoScope.map(repo => repo.uri);
|
||||||
this.props.searchScope,
|
if (
|
||||||
this.props.searchOptions.repoScope.map(repo => repo.uri)
|
this.props.searchOptions.defaultRepoScopeOn &&
|
||||||
);
|
this.props.searchOptions.defaultRepoScope
|
||||||
|
) {
|
||||||
|
repoScopes.push(this.props.searchOptions.defaultRepoScope.uri);
|
||||||
|
}
|
||||||
|
return provider.getSuggestions(query, this.props.searchScope, repoScopes);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import querystring from 'querystring';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
|
|
||||||
import { SearchScope } from '../../../model';
|
import { SearchOptions, SearchScope } from '../../../model';
|
||||||
import { SearchScopeText } from '../../common/types';
|
import { SearchScopeText } from '../../common/types';
|
||||||
import { history } from '../../utils/url';
|
import { history } from '../../utils/url';
|
||||||
import { ShortcutsProvider, Shortcut } from '../shortcuts';
|
import { ShortcutsProvider, Shortcut } from '../shortcuts';
|
||||||
|
@ -24,13 +24,20 @@ import {
|
||||||
interface Props {
|
interface Props {
|
||||||
query: string;
|
query: string;
|
||||||
onSearchScopeChanged: (s: SearchScope) => void;
|
onSearchScopeChanged: (s: SearchScope) => void;
|
||||||
repoScope: string[];
|
searchOptions: SearchOptions;
|
||||||
|
enableSubmitWhenOptionsChanged: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SearchBar extends React.PureComponent<Props> {
|
export class SearchBar extends React.PureComponent<Props> {
|
||||||
public queryBar: any = null;
|
public queryBar: any = null;
|
||||||
|
|
||||||
public onSearchChanged = (query: string) => {
|
public onSearchChanged = (query: string) => {
|
||||||
|
// Merge the default repository scope if necessary.
|
||||||
|
const repoScopes = this.props.searchOptions.repoScope.map(repo => repo.uri);
|
||||||
|
if (this.props.searchOptions.defaultRepoScopeOn && this.props.searchOptions.defaultRepoScope) {
|
||||||
|
repoScopes.push(this.props.searchOptions.defaultRepoScope.uri);
|
||||||
|
}
|
||||||
|
|
||||||
// Update the url and push to history as well.
|
// Update the url and push to history as well.
|
||||||
const queries = querystring.parse(history.location.search.replace('?', ''));
|
const queries = querystring.parse(history.location.search.replace('?', ''));
|
||||||
history.push(
|
history.push(
|
||||||
|
@ -39,7 +46,7 @@ export class SearchBar extends React.PureComponent<Props> {
|
||||||
query: {
|
query: {
|
||||||
...queries,
|
...queries,
|
||||||
q: query,
|
q: query,
|
||||||
repoScope: this.props.repoScope,
|
repoScope: repoScopes,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -51,21 +58,21 @@ export class SearchBar extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onSubmit = (q: string) => {
|
||||||
|
this.onSearchChanged(q);
|
||||||
|
};
|
||||||
|
|
||||||
|
public onSelect = (item: AutocompleteSuggestion) => {
|
||||||
|
history.push(item.selectUrl);
|
||||||
|
};
|
||||||
|
|
||||||
|
public suggestionProviders = [
|
||||||
|
new SymbolSuggestionsProvider(),
|
||||||
|
new FileSuggestionsProvider(),
|
||||||
|
new RepositorySuggestionsProvider(),
|
||||||
|
];
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const onSubmit = (q: string) => {
|
|
||||||
this.onSearchChanged(q);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSelect = (item: AutocompleteSuggestion) => {
|
|
||||||
history.push(item.selectUrl);
|
|
||||||
};
|
|
||||||
|
|
||||||
const suggestionProviders = [
|
|
||||||
new SymbolSuggestionsProvider(),
|
|
||||||
new FileSuggestionsProvider(),
|
|
||||||
new RepositorySuggestionsProvider(),
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="codeSearchbar__container">
|
<div className="codeSearchbar__container">
|
||||||
<ShortcutsProvider />
|
<ShortcutsProvider />
|
||||||
|
@ -101,12 +108,12 @@ export class SearchBar extends React.PureComponent<Props> {
|
||||||
/>
|
/>
|
||||||
<QueryBar
|
<QueryBar
|
||||||
query={this.props.query}
|
query={this.props.query}
|
||||||
onSubmit={onSubmit}
|
onSubmit={this.onSubmit}
|
||||||
onSelect={onSelect}
|
onSelect={this.onSelect}
|
||||||
appName="code"
|
appName="code"
|
||||||
suggestionProviders={suggestionProviders}
|
suggestionProviders={this.suggestionProviders}
|
||||||
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
||||||
enableSubmitWhenOptionsChanged={true}
|
enableSubmitWhenOptionsChanged={this.props.enableSubmitWhenOptionsChanged}
|
||||||
ref={instance => {
|
ref={instance => {
|
||||||
if (instance) {
|
if (instance) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
|
@ -11,8 +11,8 @@ import { connect } from 'react-redux';
|
||||||
import chrome from 'ui/chrome';
|
import chrome from 'ui/chrome';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
|
|
||||||
import { DocumentSearchResult, SearchScope } from '../../../model';
|
import { DocumentSearchResult, SearchOptions, SearchScope } from '../../../model';
|
||||||
import { changeSearchScope, SearchOptions } from '../../actions';
|
import { changeSearchScope } from '../../actions';
|
||||||
import { RootState } from '../../reducers';
|
import { RootState } from '../../reducers';
|
||||||
import { history } from '../../utils/url';
|
import { history } from '../../utils/url';
|
||||||
import { ProjectItem } from '../admin_page/project_item';
|
import { ProjectItem } from '../admin_page/project_item';
|
||||||
|
@ -209,9 +209,10 @@ class SearchPage extends React.PureComponent<Props, State> {
|
||||||
/>
|
/>
|
||||||
<div className="codeContainer__search--main">
|
<div className="codeContainer__search--main">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
repoScope={this.props.searchOptions.repoScope.map(r => r.uri)}
|
searchOptions={this.props.searchOptions}
|
||||||
query={this.props.query}
|
query={this.props.query}
|
||||||
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
onSearchScopeChanged={this.props.onSearchScopeChanged}
|
||||||
|
enableSubmitWhenOptionsChanged={true}
|
||||||
ref={(element: any) => (this.searchBar = element)}
|
ref={(element: any) => (this.searchBar = element)}
|
||||||
/>
|
/>
|
||||||
{mainComp}
|
{mainComp}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import produce from 'immer';
|
||||||
|
|
||||||
import { Action, handleActions } from 'redux-actions';
|
import { Action, handleActions } from 'redux-actions';
|
||||||
|
|
||||||
import { DocumentSearchResult, RepositoryUri, SearchScope } from '../../model';
|
import { DocumentSearchResult, RepositoryUri, SearchOptions, SearchScope } from '../../model';
|
||||||
import {
|
import {
|
||||||
changeSearchScope,
|
changeSearchScope,
|
||||||
documentSearch as documentSearchQuery,
|
documentSearch as documentSearchQuery,
|
||||||
|
@ -20,7 +20,6 @@ import {
|
||||||
RepositorySearchPayload,
|
RepositorySearchPayload,
|
||||||
repositorySearchSuccess,
|
repositorySearchSuccess,
|
||||||
saveSearchOptions,
|
saveSearchOptions,
|
||||||
SearchOptions,
|
|
||||||
searchReposForScope,
|
searchReposForScope,
|
||||||
searchReposForScopeSuccess,
|
searchReposForScopeSuccess,
|
||||||
turnOnDefaultRepoScope,
|
turnOnDefaultRepoScope,
|
||||||
|
@ -177,6 +176,7 @@ export const search = handleActions<SearchState, any>(
|
||||||
}),
|
}),
|
||||||
[String(turnOnDefaultRepoScope)]: (state: SearchState, action: Action<any>) =>
|
[String(turnOnDefaultRepoScope)]: (state: SearchState, action: Action<any>) =>
|
||||||
produce<SearchState>(state, draft => {
|
produce<SearchState>(state, draft => {
|
||||||
|
draft.searchOptions.defaultRepoScope = action.payload;
|
||||||
draft.searchOptions.defaultRepoScopeOn = true;
|
draft.searchOptions.defaultRepoScopeOn = true;
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
|
@ -136,6 +136,12 @@ function* loadRepoSaga(action: any) {
|
||||||
try {
|
try {
|
||||||
const repo = yield call(fetchRepo, action.payload);
|
const repo = yield call(fetchRepo, action.payload);
|
||||||
yield put(loadRepoSuccess(repo));
|
yield put(loadRepoSuccess(repo));
|
||||||
|
|
||||||
|
// turn on defaultRepoScope if there's no repo scope specified when enter a source view page
|
||||||
|
const repoScope = yield select(repoScopeSelector);
|
||||||
|
if (repoScope.length === 0) {
|
||||||
|
yield put(turnOnDefaultRepoScope(repo));
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
yield put(loadRepoFailed(e));
|
yield put(loadRepoFailed(e));
|
||||||
}
|
}
|
||||||
|
@ -148,11 +154,7 @@ export function* watchLoadRepo() {
|
||||||
function* handleMainRouteChange(action: Action<Match>) {
|
function* handleMainRouteChange(action: Action<Match>) {
|
||||||
// in source view page, we need repos as default repo scope options when no query input
|
// in source view page, we need repos as default repo scope options when no query input
|
||||||
yield put(fetchRepos());
|
yield put(fetchRepos());
|
||||||
// turn on defaultRepoScope if there's no repo scope specified when enter a source view page
|
|
||||||
const repoScope = yield select(repoScopeSelector);
|
|
||||||
if (repoScope.length === 0) {
|
|
||||||
yield put(turnOnDefaultRepoScope());
|
|
||||||
}
|
|
||||||
const { location } = action.payload!;
|
const { location } = action.payload!;
|
||||||
const search = location.search.startsWith('?') ? location.search.substring(1) : location.search;
|
const search = location.search.startsWith('?') ? location.search.substring(1) : location.search;
|
||||||
const queryParams = queryString.parse(search);
|
const queryParams = queryString.parse(search);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue