huridocs/uwazi

View on GitHub
app/react/Library/components/SearchBar.tsx

Summary

Maintainability
A
0 mins
Test Coverage
A
93%
import React, { useState, useEffect } from 'react';
import { bindActionCreators, Dispatch } from 'redux';
import { actions as formActions } from 'react-redux-form';
import { connect, ConnectedProps } from 'react-redux';
import { useLocation, useNavigate } from 'react-router-dom';

import { Icon } from 'UI';
import {
  searchDocuments as searchDocumentsAction,
  processFilters,
} from 'app/Library/actions/libraryActions';
import { t, Translate } from 'app/I18N';
import { wrapDispatch } from 'app/Multireducer';
import ModalTips from 'app/App/ModalTips';
import { SearchTipsContent } from 'app/App/SearchTipsContent';
import { submitNewSearch } from 'app/SemanticSearch/actions/actions';
import { FeatureToggleSemanticSearch } from 'app/SemanticSearch/components/FeatureToggleSemanticSearch';
import { IStore } from 'app/istore';
import { Form } from 'app/Forms/Form';

interface SearchBarOwnProps {}
const mapStateToProps = (state: IStore) => {
  const { search, filters } = state.library;
  return {
    initSearch: search,
    initFilters: filters,
  };
};

const mapDispatchToProps = (dispatch: Dispatch<{}>) =>
  bindActionCreators(
    {
      searchDocuments: searchDocumentsAction,
      change: formActions.change,
      semanticSearch: submitNewSearch,
    },
    wrapDispatch(dispatch, 'library')
  );

const connector = connect(mapStateToProps, mapDispatchToProps);

type mappedProps = ConnectedProps<typeof connector> & SearchBarOwnProps;

const SearchBarComponent = ({
  initSearch,
  initFilters,

  searchDocuments,
  change,
  semanticSearch,
}: mappedProps) => {
  const location = useLocation();
  const navigate = useNavigate();
  const search = processFilters(initSearch, initFilters.toJS());
  const [searchTerm, setSearchTerm] = useState(search.searchTerm);
  const resetSearch = () => {
    change('library.search.searchTerm', '');
    const newSearch = { ...search };
    newSearch.searchTerm = '';
    searchDocuments({ search: newSearch, location, navigate });
    setSearchTerm('');
  };

  let debouncedSearch: string | number | NodeJS.Timeout | undefined;
  useEffect(() => {
    if (debouncedSearch) clearTimeout(debouncedSearch);
    debouncedSearch = setTimeout(() => {
      change('library.search.searchTerm', searchTerm);
    }, 350);
    return () => clearTimeout(debouncedSearch);
  }, [searchTerm, change]);

  const submitSemanticSearch = () => {
    semanticSearch(search);
  };

  const doSearch = (newSearch: any) => {
    change('library.search.searchTerm', searchTerm);
    searchDocuments({ search: { ...newSearch, searchTerm }, location, navigate });
  };

  return (
    <div className="search-box">
      <Form model="library.search" onSubmit={doSearch}>
        <div className={`input-group${search.searchTerm ? ' is-active' : ''}`}>
          <input
            type="text"
            placeholder={t('System', 'Search', null, false)}
            aria-label={t('System', 'Search text description', null, false)}
            className="form-control"
            autoComplete="off"
            value={searchTerm}
            onChange={e => setSearchTerm(e.target.value)}
          />
          <Icon icon="times" onClick={resetSearch} aria-label="Reset Search input" />

          <button type="submit" className="search-icon-wrapper">
            <Icon icon="search" aria-label="Search button" />
          </button>
        </div>
        <FeatureToggleSemanticSearch>
          <button
            disabled={!search.searchTerm}
            type="button"
            onClick={submitSemanticSearch}
            className="btn btn-success semantic-search-button"
          >
            <Icon icon="flask" /> <Translate>Semantic Search</Translate>
          </button>
        </FeatureToggleSemanticSearch>
      </Form>
      <ModalTips
        label={t('System', 'Search Tips', null, false)}
        title={t('System', 'Narrow down your searches', null, false)}
      >
        <SearchTipsContent />
      </ModalTips>
    </div>
  );
};

const container = connector(SearchBarComponent);
export { container as SearchBar };