SearchForm.jsx 4.7 KB

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