/* eslint-disable react/no-array-index-key */
import { Fragment } from 'react';
import PropTypes from 'prop-types';

/**
 * This component lets you format a list of strings or React elements in
 * a comma-separated list that can be formulated correctly as a sentence. You
 * can choose to use [an Oxford comma][oxford], but this component doesn't use
 * one by default because that's not POLITICO style. You can also choose to
 * customize the tokens used to render the commas and the word "and". By
 * default, each item in the provided array is rendered inside a React
 * Fragment, by you can choose to render a different element type with the `as`
 * prop.
 *
 * [oxford]: https://en.wikipedia.org/wiki/Serial_comma
 */
const CommaAnd = function CommaAnd(props) {
  const {
    as,
    items,
    oxford,
    comma: commaToken,
    and: andToken,
    excludeAnd
  } = props;
  const Tag = as || Fragment;
  if (!items) {
    return null;
  }
  const n = items.length;
  const comma = <>
      {commaToken}
      {' '}
    </>;
  const and = <>
      {oxford && n > 2 && comma}
      {' '}
      {andToken}
      {' '}
    </>;
  const getItemProps = ({
    item,
    index
  }) => {
    if (!as) return {};
    return {
      item,
      index
    };
  };
  return <>
      {items.map((item, i) => <Fragment key={i}>
          <Tag {...getItemProps({
        item,
        index: i
      })}>
            {item}
          </Tag>
          {(excludeAnd ? i < n - 1 : i < n - 2) && comma}
          {i === n - 2 && !excludeAnd && and}
        </Fragment>)}
    </>;
};
CommaAnd.defaultProps = {
  items: undefined,
  as: undefined,
  oxford: false,
  comma: ',',
  and: 'and',
  excludeAnd: false
};
CommaAnd.propTypes = {
  /**
   * The items to separate by commas. These can be strings or more complex
   * nodes. Each element does not need its own `key` prop because each item
   * will end up being wrapped, either by a custom element type if you specify
   * the `as` prop, or by a `Fragment` by default.
   */
  items: PropTypes.arrayOf(PropTypes.node),
  /**
   * The kind of tag to use to wrap each element in the provided array. This is
   * a React Fragment by default. If you provide a custom element type, the
   * element will be rendered once for each item in the `items` array with the
   * item as the children of this component type. The component is also passed
   * an `item` prop containing the original item and an `index` prop containing
   * the index of the item in the `items` array.
   */
  as: PropTypes.elementType,
  /**
   * Whether to use [an Oxford comma][oxford], meaning that a "comma" token
   * will be inserted immediately before the "and" token when the provided
   * array contains at least three items.
   *
   * [oxford]: https://en.wikipedia.org/wiki/Serial_comma
   */
  oxford: PropTypes.bool,
  /**
   * The token to use as a comma in the comma-separated list. This could be
   * a simple string or a more complex node. Note that whether there is
   * a `comma` token before the `and` token for lists longer than three depends
   * on the value of the `oxford` prop. Also note that because the intent of
   * this component is to format sentences, a trailing space is added after the
   * `comma` token, but there is no space added _before_ the `comma` token.
   */
  comma: PropTypes.node,
  /**
   * The token to use as the word "and" between the last two items in the list.
   * This could be a simple string or a more complex node. Note that because
   * the intent of this component is to format sentences, spaces are added
   * before _and_ after the `and` token.
   */
  and: PropTypes.node,
  /**
   * Whether to render the `and` token or not.
   */
  excludeAnd: PropTypes.bool
};
export default CommaAnd;