MetaPhase-Consulting/State-TalentMAP

View on GitHub
src/Components/Accordion/AccordionItem/AccordionItem.jsx

Summary

Maintainability
A
25 mins
Test Coverage
A
90%
import { Component } from 'react';
import PropTypes from 'prop-types';
import { isEqual, isUndefined } from 'lodash';
import { EMPTY_FUNCTION } from '../../../Constants/PropTypes';
import { formatIdSpacing } from '../../../utilities';

class AccordionItem extends Component {
  constructor(props) {
    super(props);
    this.state = {
      expanded: props.expanded,
    };
  }

  // Update the value of expanded, only if the prop value changed and the new value is not undefined
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (!isUndefined(nextProps.expanded) && !(isEqual(nextProps.expanded, this.props.expanded))) {
      this.setState({ expanded: nextProps.expanded });
    }
  }

  // Update local state and emit to the parent
  setExpanded = () => {
    const { disabled } = this.props;
    if (!disabled) {
      this.setState({ expanded: !this.state.expanded }, () => {
        this.props.setAccordion(this.props.id, this.state.expanded);
      });
    }
  };

  // helper function for parents to use via ref, to set the expanded state to a desired value
  setExpandedFromRef(expanded) {
    this.setState({ expanded });
  }

  // helper function for parents to use via ref, to get current expanded state value
  isExpanded() {
    return this.state.expanded;
  }

  render() {
    const { expanded: expandedState } = this.state;
    const { id, title, children, className, controlled, expanded: expandedProp, useIdClass,
      buttonClass, childClass, preContent, disabled } = this.props;
    const formattedId = formatIdSpacing(id);
    const idClass = useIdClass ? `accordion-${(formattedId || 'accordion').toLowerCase()}` : '';
    let expanded$ = expandedProp;
    if (controlled) {
      expanded$ = !disabled && expandedState;
    }
    return (
      <li className={className}>
        {preContent}
        <button
          id={`${id}-button`}
          className={`usa-accordion-button ${buttonClass} ${preContent ? 'has-pre-content' : ''}`}
          aria-expanded={expanded$}
          aria-controls={formattedId}
          onClick={this.setExpanded}
          tabIndex={disabled ? '-1' : undefined}
        >
          <div className="accordion-item-title">{title}</div>
        </button>
        <div id={formattedId} className={`usa-accordion-content ${childClass} ${idClass}`} aria-hidden={!expanded$}>
          {children}
        </div>
      </li>
    );
  }
}

AccordionItem.propTypes = {
  id: PropTypes.string.isRequired,
  title: PropTypes.node,
  expanded: PropTypes.bool,
  setAccordion: PropTypes.func,
  children: PropTypes.node,
  className: PropTypes.string,
  useIdClass: PropTypes.bool,
  buttonClass: PropTypes.string,
  childClass: PropTypes.string,
  preContent: PropTypes.node,
  disabled: PropTypes.bool,
  controlled: PropTypes.bool,
};

AccordionItem.defaultProps = {
  title: '',
  setAccordion: EMPTY_FUNCTION,
  expanded: false,
  children: null,
  className: '',
  useIdClass: true,
  buttonClass: '',
  childClass: '',
  preContent: undefined,
  disabled: false,
  controlled: false,
};

export default AccordionItem;