theforeman/foreman

View on GitHub
webpack/assets/javascripts/react_app/components/HostDetails/CardExpansionContext.js

Summary

Maintainability
A
3 hrs
Test Coverage
import PropTypes from 'prop-types';
import React, { useEffect, useReducer, useCallback } from 'react';
import forceSingleton from '../../common/forceSingleton';

export const CardExpansionContext = forceSingleton('CardExpansionContext', () =>
  React.createContext({})
);

const cardExpansionReducer = (state, action) => {
  // A React reducer, not a Redux one!
  switch (action.type) {
    case 'expand':
      return {
        ...state,
        [action.key]: true,
      };
    case 'collapse':
      return {
        ...state,
        [action.key]: false,
      };
    case 'add':
      if (state[action.key] === undefined) {
        return {
          ...state,
          [action.key]: true,
        };
      }
      return state;
    case 'expandAll': {
      const expandedState = {};
      Object.keys(state).forEach(key => {
        expandedState[key] = true;
      });
      return expandedState;
    }
    case 'collapseAll': {
      const collapsedState = {};
      Object.keys(state).forEach(key => {
        collapsedState[key] = false;
      });
      return collapsedState;
    }
    default:
      throw new Error(`invalid card expansion type: ${action.type}`);
  }
};

export const CardExpansionContextWrapper = ({ children }) => {
  const [cardExpandStates, dispatch] = useReducer(cardExpansionReducer, {});
  // On mount, get values from localStorage and set them in state
  const initializeCardFromLocalStorage = useCallback(key => {
    const value = localStorage.getItem(`${key} card expanded`);
    if (value !== null && value !== undefined) {
      dispatch({ type: value === 'true' ? 'expand' : 'collapse', key });
    } else {
      dispatch({ type: 'add', key });
    }
  }, []);
  // On unmount, save the values to local storage
  // eslint-disable-next-line arrow-body-style
  useEffect(() => {
    return () =>
      Object.entries(cardExpandStates).forEach(([key, value]) =>
        localStorage.setItem(`${key} card expanded`, value)
      );
  });
  return (
    <CardExpansionContext.Provider
      value={{
        cardExpandStates,
        dispatch,
        registerCard: initializeCardFromLocalStorage,
      }}
    >
      {children}
    </CardExpansionContext.Provider>
  );
};

CardExpansionContextWrapper.propTypes = {
  children: PropTypes.node.isRequired,
};