ManageIQ/manageiq-ui-classic

View on GitHub
app/javascript/components/toolbar/Toolbar.jsx

Summary

Maintainability
A
1 hr
Test Coverage
/* eslint-disable react/destructuring-assignment */
import React, { forwardRef } from 'react';
import PropTypes from 'prop-types';
import { ToolbarView } from './ToolbarView';
import { ToolbarButton } from './ToolbarButton';
import { ToolbarList } from './ToolbarList';
import { ToolbarKebab } from './ToolbarKebab';
import CountContext from './ToolbarContext';

export const ButtonType = {
  BUTTON: 'button',
  BUTTON_TWO_STATE: 'buttonTwoState',
  BUTTON_SELECT: 'buttonSelect',
  SEPARATOR: 'separator',
  KEBAB: 'kebab',
};
const ref = React.createRef();
const isButton = (item) => (item.type === ButtonType.BUTTON);
const isButtonTwoState = (item) => (item.type === ButtonType.BUTTON_TWO_STATE);
const isButtonSelect = (item) => (item.type === ButtonType.BUTTON_SELECT);
const isKebabMenu = (item) => (item.type === ButtonType.KEBAB);

const isButtonSelectVisible = (props) => props.items.filter((i) => !i.hidden).length !== 0;

const isVisibleButtonOrSelect = (item) => (
  item.type && (
    (isButtonSelect(item) && isButtonSelectVisible(item))
    || (isButton(item) && !item.hidden)
    || (isButtonTwoState(item) && !item.hidden)
    || (isKebabMenu(item) && !item.hidden)
  )
);

const toolbarGroupHasContent = (group) =>
  group
    && group.filter((item) => item
      && isVisibleButtonOrSelect(item)).length !== 0;

export const ButtonCase = forwardRef(({ item, index, onClick }, ref) => {
  if (isButton(item) || isButtonTwoState(item)) {
    return <ToolbarButton key={index} {...item} onClick={onClick} />;
  } if (isButtonSelect(item) && (item.items.length > 0)) {
    return <ToolbarList key={index} {...item} onClick={onClick} />;
  }
  if (isKebabMenu(item) && (item.items.length > 0)) {
    return <ToolbarKebab key={index} {...item} onClick={onClick} ref={ref} />;
  }
  return null;
});

ButtonCase.propTypes = {
  item: PropTypes.objectOf(PropTypes.any).isRequired,
  index: PropTypes.number,
  onClick: PropTypes.func.isRequired,
};

ButtonCase.defaultProps = {
  index: 0,
};

/* custom buttons have ID's starting with this: */
const CUSTOM_ID = 'custom_';

const collapseOverlimit = (itemsGroup, kebabLimit) => {
  let numItems = 0;

  return itemsGroup.reduce((acc, i) => {
    if (i.id.includes(CUSTOM_ID)) {
      if (numItems >= kebabLimit) {
        acc[acc.length - 1].items.push(i);
      } else {
        numItems += 1;
        acc.splice(acc.length - 1, 0, i);
      }
    } else {
      acc.splice(acc.length - 1, 0, i);
    }
    return acc;
  }, [{ type: ButtonType.KEBAB, items: [] }]);
};

/*
 * kebabLimit === -1 means no compacting
 * kebabLimit === 0 means always compact into kebab
 * other values of kebabLimit give number of items to keep, before the rest is kebabized
*/
const collapseCustomGroups = (itemsGroup, kebabLimit) => (
  (kebabLimit === -1) || (itemsGroup.length < kebabLimit)
    ? itemsGroup
    : collapseOverlimit(itemsGroup, kebabLimit)
);

export const ToolbarGroup = forwardRef(({ group, onClick }, ref) => {
  const visibleItems = group.filter(isVisibleButtonOrSelect);

  if (visibleItems.length === 0) {
    return null;
  }

  return (
    <div className="miq-toolbar-group form-group">
      {visibleItems.map((i, index) => <ButtonCase item={i} key={index} index={index} onClick={onClick} ref={ref} />)}
    </div>
  );
});

ToolbarGroup.propTypes = {
  group: PropTypes.arrayOf(PropTypes.any).isRequired,
  onClick: PropTypes.func.isRequired,
};

export const Toolbar = (props) => (
  <CountContext.Provider value={props.count}>
    <div className="toolbar-pf-actions miq-toolbar-actions">
      { props.groups
        .filter(toolbarGroupHasContent)
        .map((group, index) =>
          // eslint-disable-next-line react/no-array-index-key
          <ToolbarGroup key={index} ref={ref} onClick={props.onClick} group={collapseCustomGroups(group, props.kebabLimit)} />)}
      <ToolbarView onClick={props.onViewClick} views={props.views} />
    </div>
  </CountContext.Provider>
);

Toolbar.propTypes = {
  count: PropTypes.number,
  kebabLimit: PropTypes.number,
  groups: PropTypes.arrayOf(PropTypes.any).isRequired, // array of arrays of buttons
  views: PropTypes.arrayOf(PropTypes.any).isRequired, // array of view buttons
  onClick: PropTypes.func.isRequired,
  onViewClick: PropTypes.func.isRequired,
};

Toolbar.defaultProps = {
  count: 0,
  kebabLimit: 3,
};