<commit_before>import React, { PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import ReactPaginate from 'react-paginate'
import { push } from 'react-router-redux'
import shallowEqual from 'fbjs/lib/shallowEqual'
import { injectIntl, intlShape, defineMessages } from 'react-intl'

import * as SearchActions from '../actions/SearchActions'
import * as SearchFilterActions from '../actions/SearchFilterActions'
import * as ResourceActions from '../actions/ResourceActions'
import SearchResults from '../components/SearchResults'
import SearchFilters from '../components/SearchFilters'
import Constants from '../constants/Constants'
import SearchResultsText from '../components/SearchResultsText'
import SearchFilterBox from '../components/SearchFilterBox'

class Search extends React.Component {
  constructor (props) {
    super(props)
    this.handlePageClick = this.handlePageClick.bind(this)
  }

  componentWillMount () {
    this.props.searchActions.search()
  }

  componentDidUpdate (prevProps) {
    if (!shallowEqual(this.filterLocationQuery(this.props.locationQuery), this.filterLocationQuery(prevProps.locationQuery))) {
      this.props.searchActions.search()
    }
  }

  filterLocationQuery (locationQuery) {
    const filteredLocationQuery = {}
    Object.keys(locationQuery).filter(key => [ 'query', 'filter' ].includes(key)).forEach(key => {
      filteredLocationQuery[ key ] = locationQuery[ key ]
    })
    return filteredLocationQuery
  }

  handlePageClick (data) {
    let page = String(data.selected + 1)
    if (page !== this.props.location.query.page) {
      let newQuery = {
        ...this.props.location.query,
        page: page
      }
      this.props.dispatch(push({ pathname: '/search', query: newQuery }))
    }
  }

  renderPagination () {
    if ((this.props.totalHits > Constants.maxSearchResultsPerPage) && this.props.location.query.query) {
      return (
        <section className="pagination-area"
                 data-automation-id="search-results-pagination">
          <nav role="menu" aria-label={this.props.intl.formatMessage(messages.paginationLabel)}>
            <ReactPaginate previousLabel={<span aria-label={this.props.intl.formatMessage(messages.paginationPrevious)}>&lt;</span>}
                         nextLabel={<span aria-label={this.props.intl.formatMessage(messages.paginationNext)}>&gt;</span>}
                         breakLabel={<li className="break" aria-hidden="true"><span>...</span></li>}
                         forceSelected={this.props.location.query.page - 1 || 0}
                         marginPagesDisplayed={1}
                         pageRangeDisplayed={5}
                         pageNum={Math.ceil(Math.min(this.props.totalHits, Constants.maxSearchResults) / Constants.maxSearchResultsPerPage)}
                         clickCallback={this.handlePageClick}
                         containerClassName={'pagination'}
                         subContainerClassName={'pages pagination'}
                         activeClassName={'active'} />
          </nav>
        </section>
      )
    }
  }

  render () {
    return (
      <div className="container">
        <div className="row">
          {this.props.locationQuery.query
            ? (<div className="search-results-footer">
            <div className="search-results-number">
              <SearchResultsText totalHits={this.props.totalHits}
                                 totalHitsPublications={this.props.totalHitsPublications}
                                 locationQuery={this.props.locationQuery}
                                 isSearching={this.props.isSearching} />
              <SearchFilterBox filters = {this.props.filters} toggleFilter={this.props.searchFilterActions.toggleFilter}/>
            </div>
            {this.props.totalHits > 0
              ? (<div className="search-sorting patron-placeholder">
              <p>Sorter treff på</p>

              <div className="search-sorting-select-box">
                <select>
                  <option defaultValue>Årstall</option>
                  <option>Nyeste</option>
                  <option>Eldre</option>
                </select>
              </div>
            </div>) : null}
          </div>)
            : null}
          {this.props.totalHits > 0
            ? [ <SearchFilters key="searchFilters"
                               filters={this.props.filters}
                               locationQuery={this.props.location.query}
                               toggleFilter={this.props.searchFilterActions.toggleFilter}
                               toggleFilterVisibility={this.props.searchFilterActions.toggleFilterVisibility}
                               toggleAllFiltersVisibility={this.props.searchFilterActions.toggleAllFiltersVisibility}
                               toggleCollapseFilter={this.props.searchFilterActions.toggleCollapseFilter} />,
            <SearchResults key="searchResults"
                           locationQuery={this.props.location.query}
                           searchActions={this.props.searchActions}
                           searchResults={this.props.searchResults}
                           totalHits={this.props.totalHits}
                           searchError={this.props.searchError}
                           fetchWorkResource={this.props.resourceActions.fetchWorkResource}
                           resources={this.props.resources}
                           page={this.props.location.query.page}
            /> ]
            : null}
          {this.renderPagination()}
        </div>
      </div>
    )
  }
}

Search.propTypes = {
  searchActions: PropTypes.object.isRequired,
  searchFilterActions: PropTypes.object.isRequired,
  searchResults: PropTypes.array.isRequired,
  isSearching: PropTypes.bool.isRequired,
  dispatch: PropTypes.func.isRequired,
  searchError: PropTypes.any.isRequired,
  filters: PropTypes.array.isRequired,
  location: PropTypes.object.isRequired,
  searchFieldInput: PropTypes.string,
  totalHits: PropTypes.number.isRequired,
  totalHitsPublications: PropTypes.number.isRequired,
  locationQuery: PropTypes.object.isRequired,
  resources: PropTypes.object.isRequired,
  resourceActions: PropTypes.object.isRequired,
  intl: intlShape.isRequired
}

export { Search }

function mapStateToProps (state) {
  return {
    searchResults: state.search.searchResults,
    totalHits: state.search.totalHits,
    totalHitsPublications: state.search.totalHitsPublications,
    isSearching: state.search.isSearching,
    searchError: state.search.searchError,
    filters: state.search.filters,
    locationQuery: state.routing.locationBeforeTransitions.query,
    resources: state.resources.resources
  }
}

function mapDispatchToProps (dispatch) {
  return {
    searchActions: bindActionCreators(SearchActions, dispatch),
    searchFilterActions: bindActionCreators(SearchFilterActions, dispatch),
    resourceActions: bindActionCreators(ResourceActions, dispatch),
    dispatch: dispatch
  }
}

const messages = defineMessages({
  paginationLabel: {
    id: 'Search.paginationLabel',
    description: 'The ARIA label for the pagination bar',
    defaultMessage: 'Pagination'
  },
  paginationNext: {
    id: 'Search.paginationNext',
    description: 'The ARIA label for the "next" link',
    defaultMessage: 'Next'
  },
  paginationPrevious: {
    id: 'Search.paginationPrevious',
    description: 'The ARIA label frot the "previous" link',
    defaultMessage: 'Previous'
  }
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(injectIntl(Search))<commit_msg>patron-client: Fixed failing Search test<commit_after>import React, { PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import ReactPaginate from 'react-paginate'
import { push } from 'react-router-redux'
import shallowEqual from 'fbjs/lib/shallowEqual'
import { injectIntl, intlShape, defineMessages } from 'react-intl'

import * as SearchActions from '../actions/SearchActions'
import * as SearchFilterActions from '../actions/SearchFilterActions'
import * as ResourceActions from '../actions/ResourceActions'
import SearchResults from '../components/SearchResults'
import SearchFilters from '../components/SearchFilters'
import Constants from '../constants/Constants'
import SearchResultsText from '../components/SearchResultsText'
import SearchFilterBox from '../components/SearchFilterBox'

class Search extends React.Component {
  constructor (props) {
    super(props)
    this.handlePageClick = this.handlePageClick.bind(this)
  }

  componentWillMount () {
    this.props.searchActions.search()
  }

  componentDidUpdate (prevProps) {
    if (!shallowEqual(this.filterLocationQuery(this.props.locationQuery), this.filterLocationQuery(prevProps.locationQuery))) {
      this.props.searchActions.search()
    }
  }

  filterLocationQuery (locationQuery) {
    const filteredLocationQuery = {}
    Object.keys(locationQuery).filter(key => [ 'query', 'filter' ].includes(key)).forEach(key => {
      filteredLocationQuery[ key ] = locationQuery[ key ]
    })
    return filteredLocationQuery
  }

  handlePageClick (data) {
    let page = String(data.selected + 1)
    if (page !== this.props.location.query.page) {
      let newQuery = {
        ...this.props.location.query,
        page: page
      }
      this.props.dispatch(push({ pathname: '/search', query: newQuery }))
    }
  }

  renderPagination () {
    if ((this.props.totalHits > Constants.maxSearchResultsPerPage) && this.props.location.query.query) {
      return (
        <section className="pagination-area"
                 data-automation-id="search-results-pagination">
          <nav role="menu" aria-label={this.props.intl.formatMessage(messages.paginationLabel)}>
            <ReactPaginate
              previousLabel={<span aria-label={this.props.intl.formatMessage(messages.paginationPrevious)}>&lt;</span>}
              nextLabel={<span aria-label={this.props.intl.formatMessage(messages.paginationNext)}>&gt;</span>}
              breakLabel={<li className="break" aria-hidden="true"><span>...</span></li>}
              forceSelected={this.props.location.query.page - 1 || 0}
              marginPagesDisplayed={1}
              pageRangeDisplayed={5}
              pageNum={Math.ceil(Math.min(this.props.totalHits, Constants.maxSearchResults) / Constants.maxSearchResultsPerPage)}
              clickCallback={this.handlePageClick}
              containerClassName={'pagination'}
              subContainerClassName={'pages pagination'}
              activeClassName={'active'} />
          </nav>
        </section>
      )
    }
  }

  render () {
    return (
      <div className="container">
        <div className="row">
          {this.props.locationQuery.query
            ? (<div className="search-results-footer">
            <div className="search-results-number">
              <SearchResultsText totalHits={this.props.totalHits}
                                 totalHitsPublications={this.props.totalHitsPublications}
                                 locationQuery={this.props.locationQuery}
                                 isSearching={this.props.isSearching} />
              <SearchFilterBox query={this.props.locationQuery}
                               toggleFilter={this.props.searchFilterActions.toggleFilter} />
            </div>
            {this.props.totalHits > 0
              ? (<div className="search-sorting patron-placeholder">
              <p>Sorter treff på</p>

              <div className="search-sorting-select-box">
                <select>
                  <option defaultValue>Årstall</option>
                  <option>Nyeste</option>
                  <option>Eldre</option>
                </select>
              </div>
            </div>) : null}
          </div>)
            : null}
          {this.props.totalHits > 0
            ? [ <SearchFilters key="searchFilters"
                               filters={this.props.filters}
                               locationQuery={this.props.location.query}
                               toggleFilter={this.props.searchFilterActions.toggleFilter}
                               toggleFilterVisibility={this.props.searchFilterActions.toggleFilterVisibility}
                               toggleAllFiltersVisibility={this.props.searchFilterActions.toggleAllFiltersVisibility}
                               toggleCollapseFilter={this.props.searchFilterActions.toggleCollapseFilter} />,
            <SearchResults key="searchResults"
                           locationQuery={this.props.location.query}
                           searchActions={this.props.searchActions}
                           searchResults={this.props.searchResults}
                           totalHits={this.props.totalHits}
                           searchError={this.props.searchError}
                           fetchWorkResource={this.props.resourceActions.fetchWorkResource}
                           resources={this.props.resources}
                           page={this.props.location.query.page}
            /> ]
            : null}
          {this.renderPagination()}
        </div>
      </div>
    )
  }
}

Search.propTypes = {
  searchActions: PropTypes.object.isRequired,
  searchFilterActions: PropTypes.object.isRequired,
  searchResults: PropTypes.array.isRequired,
  isSearching: PropTypes.bool.isRequired,
  dispatch: PropTypes.func.isRequired,
  searchError: PropTypes.any.isRequired,
  filters: PropTypes.array.isRequired,
  location: PropTypes.object.isRequired,
  searchFieldInput: PropTypes.string,
  totalHits: PropTypes.number.isRequired,
  totalHitsPublications: PropTypes.number.isRequired,
  locationQuery: PropTypes.object.isRequired,
  resources: PropTypes.object.isRequired,
  resourceActions: PropTypes.object.isRequired,
  intl: intlShape.isRequired
}

const i18nSearch = injectIntl(Search)
export { i18nSearch as Search }

function mapStateToProps (state) {
  return {
    searchResults: state.search.searchResults,
    totalHits: state.search.totalHits,
    totalHitsPublications: state.search.totalHitsPublications,
    isSearching: state.search.isSearching,
    searchError: state.search.searchError,
    filters: state.search.filters,
    locationQuery: state.routing.locationBeforeTransitions.query,
    resources: state.resources.resources
  }
}

function mapDispatchToProps (dispatch) {
  return {
    searchActions: bindActionCreators(SearchActions, dispatch),
    searchFilterActions: bindActionCreators(SearchFilterActions, dispatch),
    resourceActions: bindActionCreators(ResourceActions, dispatch),
    dispatch: dispatch
  }
}

const messages = defineMessages({
  paginationLabel: {
    id: 'Search.paginationLabel',
    description: 'The ARIA label for the pagination bar',
    defaultMessage: 'Pagination'
  },
  paginationNext: {
    id: 'Search.paginationNext',
    description: 'The ARIA label for the "next" link',
    defaultMessage: 'Next'
  },
  paginationPrevious: {
    id: 'Search.paginationPrevious',
    description: 'The ARIA label frot the "previous" link',
    defaultMessage: 'Previous'
  }
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Search)
{'fraction_non_alphanumeric': 0.10830773253153801, 'fraction_numerical': 0.0011765474867638408, 'mean_word_length': 2.5797847449695834, 'pattern_counts': {'":': 0, '<': 81, '<?xml version=': 0, '>': 91, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5212649', 'n_tokens_mistral': 4003, 'n_tokens_neox': 4017, 'n_words': 673}