All files / client/components HeaderSearchBox.tsx

0% Statements 0/29
0% Branches 0/11
0% Functions 0/10
0% Lines 0/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128                                                                                                                                                                                                                                                               
// This is the root component for #search-top
 
import React from 'react'
import queryString from 'query-string'
 
import SearchForm from './HeaderSearchBox/SearchForm'
import SearchSuggest from './HeaderSearchBox/SearchSuggest'
import Crowi from 'client/util/Crowi'
 
interface Props {
  crowi: Crowi
}
 
interface State {
  isSearchPage: boolean
  searchingKeyword: string
  searchedPages: {}
  searchError: Error | null
  searching: boolean
  focused: boolean
}
 
export default class HeaderSearchBox extends React.Component<Props, State> {
  public node: HTMLDivElement | null = null
 
  constructor(props: Props) {
    super(props)
 
    const { pathname = '', search: locationSearch } = this.props.crowi.location
    const parsedKeyword = queryString.parse(locationSearch).q || ''
    this.state = {
      isSearchPage: pathname.startsWith('/_search'),
      searchingKeyword: String(parsedKeyword),
      searchedPages: {},
      searchError: null,
      searching: false,
      focused: false,
    }
 
    this.search = this.search.bind(this)
    this.isShown = this.isShown.bind(this)
    this.handleClick = this.handleClick.bind(this)
    this.handleClickOutside = this.handleClickOutside.bind(this)
  }
 
  componentDidMount() {
    document.addEventListener('mousedown', this.handleClick, false)
  }
 
  componentWillUnmount() {
    document.removeEventListener('mousedown', this.handleClick, false)
  }
 
  isShown(focused: boolean) {
    this.setState({ focused })
  }
 
  handleClick(e) {
    if (this.node && !this.node.contains(e.target)) {
      this.handleClickOutside()
    }
  }
 
  handleClickOutside() {
    this.isShown(false)
  }
 
  async search(data: { keyword: string }) {
    const keyword = data.keyword
    if (keyword === '') {
      this.setState({
        searchingKeyword: '',
        searchedPages: [],
      })
 
      return true
    }
 
    this.setState({
      searchingKeyword: keyword,
      searching: true,
    })
 
    try {
      const [{ data: portalPages }, { data: publicPages }, { data: userPages }] = await Promise.all(
        ['portal', 'public', 'user'].map(type => this.props.crowi.apiGet('/search', { q: keyword, type, limit: 10 })),
      )
      this.setState({
        searchingKeyword: keyword,
        searchedPages: { portalPages, publicPages, userPages },
        searching: false,
        searchError: null,
      })
    } catch (err) {
      this.setState({
        searchError: err,
        searching: false,
      })
    }
  }
 
  render() {
    const { isSearchPage, searchingKeyword, searchedPages, searchError, searching, focused } = this.state
    const { crowi } = this.props
    return (
      <div className="search-box" ref={node => (this.node = node)}>
        <SearchForm
          onSearchFormChanged={this.search}
          focused={focused}
          isShown={this.isShown}
          isSearchPage={isSearchPage}
          keyword={this.state.searchingKeyword}
        />
        {!isSearchPage && (
          <SearchSuggest
            searchingKeyword={searchingKeyword}
            searchedPages={searchedPages}
            searchError={searchError}
            searching={searching}
            focused={focused}
            crowi={crowi}
          />
        )}
      </div>
    )
  }
}