<commit_before>import React, { Component } from 'react';
import c from 'classnames';
import { connect } from 'react-redux';
import { FormattedMessage, FormattedNumber } from 'react-intl';
import { Button, Popover, Position, Spinner } from '@blueprintjs/core';

import { fetchSearchResults } from 'src/actions';

import './SearchFilterFacet.css';


const SearchFilterFacetTick = ({ isTicked }) => (
  <span className={c('pt-icon-standard', 'search-tick', {'is-ticked': isTicked})} />
);

const SearchFilterFacetList = ({ items, selectedItems, onItemClick }) => (
  <ul className="search-filter-list">
    { items.length === 0 && (
      <li className="search-filter-list-item">
        <FormattedMessage id="search.filter.no.items"
                          defaultMessage="No options"/>
      </li>
    )}
    {items
      // .sort((a, b) => a.label < b.label ? -1 : 1)
      .map(item => (
        <li className="search-filter-list-item" onClick={onItemClick.bind(null, item.id)} key={item.id}>
          <SearchFilterFacetTick isTicked={selectedItems.indexOf(item.id) > -1} />
          <span className="search-filter-list-label" title={item.label}>{item.label}</span>
          <span><FormattedNumber value={item.count} /></span>
        </li>
      ))}
  </ul>
);

class SearchFilterFacet extends Component {
  constructor(props)  {
    super(props);

    this.state = {values: null};

    this.onOpen = this.onOpen.bind(this);
    this.onSelect = this.onSelect.bind(this);
  }

  componentDidUpdate(prevProps, nextProps) {
    if (!this.props.query.sameAs(prevProps.query)) {
      this.fetchValues();
    }
  }

  async fetchValues() {
    const { field, query, fetchSearchResults } = this.props;
    const params = query
      .limit(0)
      .clearFacets()
      .addFacet(field)
      .set('facet_size', 500)
      .toParams();
    const { result } = await fetchSearchResults({filters: params});
    this.setState({
      values: result.facets[field].values,
    });
  }

  onOpen() {
    if (this.state.values === null) {
      this.fetchValues();
    }
  }

  onSelect(value) {
    const { field } = this.props;
    let query = this.props.query;
    query = query.toggleFilter(field, value)
    this.props.updateQuery(query)
  }

  render() {
    const { query, children, field } = this.props;
    const { values } = this.state;
    const current = query.getFilter(field);

    return (
      <Popover popoverClassName="search-filter-facet"
               position={Position.BOTTOM_RIGHT}
               popoverWillOpen={this.onOpen} inline>
        <Button rightIconName="caret-down">
          {children}
        </Button>
        {values !== null ?
          <SearchFilterFacetList items={values}
                                 selectedItems={current}
                                 onItemClick={this.onSelect} /> :
          <Spinner className="search-filter-loading pt-large" />
        }
      </Popover>
    );
  }
}

SearchFilterFacet = connect(null, { fetchSearchResults })(SearchFilterFacet);
export default SearchFilterFacet;<commit_msg>Make facets lazy again.<commit_after>import React, { Component } from 'react';
import c from 'classnames';
import { connect } from 'react-redux';
import { FormattedMessage, FormattedNumber } from 'react-intl';
import { Button, Popover, Position, Spinner } from '@blueprintjs/core';

import { fetchSearchResults } from 'src/actions';

import './SearchFilterFacet.css';


const SearchFilterFacetTick = ({ isTicked }) => (
  <span className={c('pt-icon-standard', 'search-tick', {'is-ticked': isTicked})} />
);

const SearchFilterFacetList = ({ items, selectedItems, onItemClick }) => (
  <ul className="search-filter-list">
    { items.length === 0 && (
      <li className="search-filter-list-item">
        <FormattedMessage id="search.filter.no.items"
                          defaultMessage="No options"/>
      </li>
    )}
    {items
      // .sort((a, b) => a.label < b.label ? -1 : 1)
      .map(item => (
        <li className="search-filter-list-item" onClick={onItemClick.bind(null, item.id)} key={item.id}>
          <SearchFilterFacetTick isTicked={selectedItems.indexOf(item.id) > -1} />
          <span className="search-filter-list-label" title={item.label}>{item.label}</span>
          <span><FormattedNumber value={item.count} /></span>
        </li>
      ))}
  </ul>
);

class SearchFilterFacet extends Component {
  constructor(props)  {
    super(props);

    this.state = {
      values: null,
      isOpen: false,
    };

    this.onInteraction = this.onInteraction.bind(this);
    this.onSelect = this.onSelect.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    const { isOpen } = this.state;
    if (!nextProps.query.sameAs(this.props.query)) {
      // Invalidate previously fetched values.
      this.setState({ values: null });
      if (isOpen) {
        this.fetchValues();
      }
    }
  }

  async fetchValues() {
    const { field, query, fetchSearchResults } = this.props;
    const params = query
      .limit(0)
      .clearFacets()
      .addFacet(field)
      .set('facet_size', 500)
      .toParams();
    const { result } = await fetchSearchResults({filters: params});
    this.setState({
      values: result.facets[field].values,
    });
  }

  onInteraction(nextOpenState) {
    this.setState({ isOpen: nextOpenState });

    if (nextOpenState === true && this.state.values === null) {
      this.fetchValues();
    }
  }

  onSelect(value) {
    const { field } = this.props;
    let query = this.props.query;
    query = query.toggleFilter(field, value)
    this.props.updateQuery(query)
  }

  render() {
    const { query, children, field } = this.props;
    const { values, isOpen } = this.state;
    const current = query.getFilter(field);

    return (
      <Popover popoverClassName="search-filter-facet"
               position={Position.BOTTOM_RIGHT}
               isOpen={isOpen}
               onInteraction={this.onInteraction}
               inline>
        <Button rightIconName="caret-down">
          {children}
        </Button>
        {values !== null ?
          <SearchFilterFacetList items={values}
                                 selectedItems={current}
                                 onItemClick={this.onSelect} /> :
          <Spinner className="search-filter-loading pt-large" />
        }
      </Popover>
    );
  }
}

SearchFilterFacet = connect(null, { fetchSearchResults })(SearchFilterFacet);
export default SearchFilterFacet;
{'fraction_non_alphanumeric': 0.14684559617461052, 'fraction_numerical': 0.0024679932130186643, 'mean_word_length': 2.6102449888641424, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2975960', 'n_tokens_mistral': 1989, 'n_tokens_neox': 1956, 'n_words': 374}