All files / SearchKeyControl SearchKeyControl.js

96.96% Statements 32/33
65.21% Branches 15/23
100% Functions 12/12
96.77% Lines 30/31

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                              26x       21x 21x     21x 42x   126x   126x 126x       126x       21x     21x 21x 21x 7x         21x   21x   7x     7x 2x 2x 2x 2x         5x 5x       7x 7x         21x                       63x           7x                   26x                      
import { useCallback, useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
 
import isEqual from 'lodash/isEqual';
 
import { Checkbox } from '@folio/stripes/components';
 
import { usePrevNextPagination, useQIndex } from '../hooks';
 
import css from '../../../styles/SearchKeyControl.css';
 
/*
  IMPORTANT -- This component is controlled by the qIndex, rather than the other way around.
  It should inject values in/remove values from the qIndex, but not replace the entire thing.
*/
const SearchKeyControl = ({
                            options = []
                          }) => {
  // This component expects a qIndex comprising of a comma separated list
  const [qIndex, setQIndex] = useQIndex();
  const qIndexArray = useMemo(() => qIndex?.split(',')?.map(index => index.trim()) ?? [], [qIndex]);
 
  // Memoise this process so keyState changes if and only if options/qIndex change
  const createKeyState = useCallback(() => (
    options.reduce((acc, curr) => {
      // Is in use if URL contains ALL of the indexes
      const subindexes = ((curr?.indexes?.length ?? 0) > 0) ? curr.indexes : [curr.key];
 
      acc[curr.key] = {
        inUse: subindexes.every(si => qIndexArray.includes(si)),
        label: curr.label ?? curr.key,
        subIndexes: curr.indexes
      };
      return acc;
    }, {})
  ), [options, qIndexArray]);
 
  const [keyState, setKeyState] = useState(createKeyState());
 
  // Keep keyState up to date as options/qIndex change
  useEffect(() => {
    const newKeyState = createKeyState();
    if (!isEqual(newKeyState, keyState)) {
      setKeyState(newKeyState);
    }
  }, [createKeyState, keyState]);
 
  // Check to see if page param exists, and if it does then changing qIndex should reset it
  const { currentPage, resetPage } = usePrevNextPagination({ defaultToPageOne: false });
 
  const handleSearchKeyChange = useCallback(({ e: { target: { checked: targetIsChecked } = {} } = {}, key, value }) => {
    // Set up which indexes need to change
    const indicesToChange = ((value?.subIndexes?.length ?? 0) > 0) ? value.subIndexes : [key];
 
    // If false, we must remove from the qIndex
    if (!targetIsChecked) {
      indicesToChange.forEach(ind => {
        const indexOfKey = qIndexArray.indexOf(ind);
        Eif (indexOfKey > -1) { // only splice array when item is found
          qIndexArray.splice(indexOfKey, 1); // 2nd parameter means remove one item only
        }
      });
    } else {
      // If true, we need to add to qIndex
      indicesToChange.forEach(ind => {
        qIndexArray.push(ind);
      });
    }
 
    setQIndex(qIndexArray?.join(','));
    Iif (currentPage) {
      resetPage();
    }
  }, [currentPage, qIndexArray, resetPage, setQIndex]);
 
  return (
    <>
      <div className={css.searchKeyControlContainer}>
        {
          Object.entries(keyState).map(([key, value]) => {
            /* At this point we have "key" corresponding to a searchKey option,
            * and "value" an object of the shape
              {
                inUse: a bool determining if it is in use or not,
                label: the label to display on the checkbox
              }
            */
            return (
              <Checkbox
                key={`search-key-control-${key}`}
                checked={value?.inUse}
                className={css.searchKeyControlElement}
                label={value?.label}
                onChange={e => handleSearchKeyChange({ e, key, value })}
              />
            );
          })
        }
      </div>
    </>
  );
};
 
SearchKeyControl.propTypes = {
  options: PropTypes.arrayOf(PropTypes.shape({
    label: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.node
    ]).isRequired,
    key: PropTypes.string.isRequired
  }))
};
 
export default SearchKeyControl;