SearchForm.jsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import { withTranslation } from 'react-i18next';
  4. import { withUnstatedContainers } from './UnstatedUtils';
  5. import AppContainer from '~/client/services/AppContainer';
  6. import SearchTypeahead from './SearchTypeahead';
  7. // SearchTypeahead wrapper
  8. class SearchForm extends React.Component {
  9. constructor(props) {
  10. super(props);
  11. this.state = {
  12. searchError: null,
  13. isShownHelp: false,
  14. };
  15. this.onSearchError = this.onSearchError.bind(this);
  16. this.onChange = this.onChange.bind(this);
  17. this.onBlur = this.onBlur.bind(this);
  18. this.onFocus = this.onFocus.bind(this);
  19. }
  20. componentDidMount() {
  21. }
  22. componentWillUnmount() {
  23. }
  24. onSearchError(err) {
  25. this.setState({
  26. searchError: err,
  27. });
  28. }
  29. onChange(selected) {
  30. const page = selected[0]; // should be single page selected
  31. // navigate to page
  32. if (page != null) {
  33. window.location = page.path;
  34. }
  35. }
  36. onBlur() {
  37. this.setState({
  38. isShownHelp: false,
  39. });
  40. this.getHelpElement();
  41. }
  42. onFocus() {
  43. this.setState({
  44. isShownHelp: true,
  45. });
  46. }
  47. getHelpElement() {
  48. const { t, appContainer } = this.props;
  49. const { isShownHelp } = this.state;
  50. const config = appContainer.getConfig();
  51. const isReachable = config.isSearchServiceReachable;
  52. if (!isReachable) {
  53. return (
  54. <>
  55. <h5 className="text-danger">Error occured on Search Service</h5>
  56. Try to reconnect from management page.
  57. </>
  58. );
  59. }
  60. if (!isShownHelp) {
  61. return <></>;
  62. }
  63. return (
  64. <table className="table grw-search-table search-help m-0">
  65. <caption className="text-left text-primary p-2">
  66. <h5 className="h6"><i className="icon-magnifier pr-2 mb-2" />{ t('search_help.title') }</h5>
  67. </caption>
  68. <tbody>
  69. <tr>
  70. <th className="py-2">
  71. <code>word1</code> <code>word2</code><br></br>
  72. <small>({ t('search_help.and.syntax help') })</small>
  73. </th>
  74. <td><h6 className="m-0">{ t('search_help.and.desc', { word1: 'word1', word2: 'word2' }) }</h6></td>
  75. </tr>
  76. <tr>
  77. <th className="py-2">
  78. <code>&quot;This is GROWI&quot;</code><br></br>
  79. <small>({ t('search_help.phrase.syntax help') })</small>
  80. </th>
  81. <td><h6 className="m-0">{ t('search_help.phrase.desc', { phrase: 'This is GROWI' }) }</h6></td>
  82. </tr>
  83. <tr>
  84. <th className="py-2"><code>-keyword</code></th>
  85. <td><h6 className="m-0">{ t('search_help.exclude.desc', { word: 'keyword' }) }</h6></td>
  86. </tr>
  87. <tr>
  88. <th className="py-2"><code>prefix:/user/</code></th>
  89. <td><h6 className="m-0">{ t('search_help.prefix.desc', { path: '/user/' }) }</h6></td>
  90. </tr>
  91. <tr>
  92. <th className="py-2"><code>-prefix:/user/</code></th>
  93. <td><h6 className="m-0">{ t('search_help.exclude_prefix.desc', { path: '/user/' }) }</h6></td>
  94. </tr>
  95. <tr>
  96. <th className="py-2"><code>tag:wiki</code></th>
  97. <td><h6 className="m-0">{ t('search_help.tag.desc', { tag: 'wiki' }) }</h6></td>
  98. </tr>
  99. <tr>
  100. <th className="py-2"><code>-tag:wiki</code></th>
  101. <td><h6 className="m-0">{ t('search_help.exclude_tag.desc', { tag: 'wiki' }) }</h6></td>
  102. </tr>
  103. </tbody>
  104. </table>
  105. );
  106. }
  107. render() {
  108. const { t, appContainer, dropup } = this.props;
  109. const config = appContainer.getConfig();
  110. const isReachable = config.isSearchServiceReachable;
  111. const placeholder = isReachable
  112. ? 'Search ...'
  113. : 'Error on Search Service';
  114. const emptyLabel = (this.state.searchError !== null)
  115. ? 'Error on searching.'
  116. : t('search.search page bodies');
  117. return (
  118. <SearchTypeahead
  119. dropup={dropup}
  120. onChange={this.onChange}
  121. onSubmit={this.props.onSubmit}
  122. onInputChange={this.props.onInputChange}
  123. onSearchError={this.onSearchError}
  124. emptyLabel={emptyLabel}
  125. placeholder={placeholder}
  126. helpElement={this.getHelpElement()}
  127. keywordOnInit={this.props.keyword}
  128. onBlur={this.onBlur}
  129. onFocus={this.onFocus}
  130. />
  131. );
  132. }
  133. }
  134. /**
  135. * Wrapper component for using unstated
  136. */
  137. const SearchFormWrapper = withUnstatedContainers(SearchForm, [AppContainer]);
  138. SearchForm.propTypes = {
  139. t: PropTypes.func.isRequired, // i18next
  140. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  141. dropup: PropTypes.bool,
  142. keyword: PropTypes.string,
  143. onSubmit: PropTypes.func.isRequired,
  144. onInputChange: PropTypes.func,
  145. };
  146. SearchForm.defaultProps = {
  147. onInputChange: () => {},
  148. };
  149. export default withTranslation()(SearchFormWrapper);