ManageIQ/manageiq-ui-classic

View on GitHub
app/javascript/components/dashboard-widgets/widget-wrapper/index.jsx

Summary

Maintainability
A
2 hrs
Test Coverage
/* eslint-disable camelcase */
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { OverflowMenu } from 'carbon-components-react';
import debouncePromise from '../../../helpers/promise-debounce';
import { getOverflowButtons, getWidget } from './helper';
import WidgetRemoveModal from '../widget-remove-modal';

const WidgetWrapper = ({
  widgetId, widgetType, widgetButtons, widgetLastRun, widgetNextRun, widgetTitle,
}) => {
  const [{
    widgetModel, href, isLoading, showConfirm, error,
  }, setState] = useState({ isLoading: true, showConfirm: false, error: false });
  const widgetUrl = () => {
    const widgetTypeUrl = {
      menu: '/dashboard/widget_menu_data/',
      report: '/dashboard/widget_report_data/',
      chart: '/dashboard/widget_chart_data/',
    };

    if (widgetTypeUrl[widgetType]) {
      return [widgetTypeUrl[widgetType], widgetId].join('/');
    }
    return null;
  };

  const getWidgetData = () => {
    http.get(widgetUrl())
      .then((response) => {
        setState({
          widgetModel: response.content,
          isLoading: false,
        });
      });
  };

  useEffect(() => {
    // Handles the closing of the overflow menu when the page is scrolled
    if (document.getElementById('main-content')) {
      document.getElementById('main-content').addEventListener('scroll', () => {
        if (document.getElementById(`${widgetId}-menu`).getAttribute('aria-expanded') === 'true') {
          const temp = document.getElementsByClassName('bx--overflow-menu-options');
          temp.forEach((element) => {
            element.classList.remove('bx--overflow-menu-options--open');
          });
        }
      });
    }

    if (isLoading) {
      const asyncValidatorDebounced = debouncePromise(getWidgetData);
      asyncValidatorDebounced()
        .catch(() => {
          setState({
            widgetModel: undefined,
            isLoading: false,
            error: true,
          });
        });
    }
  });

  /** Function to assign style to the OverflowMenu to display the list on the top of its menu button.
   * Note: The open={true} property for the OverflowMenu is not working as expected. Therefore,
   * we are unable to set the direction={"top"|"bottom"}
  */
  const overflowMenuDirection = (widgetId, widgetButtons) => {
    const widgetMenu = document.getElementById(`${widgetId}-menu`);
    if (widgetMenu) {
      const rowHeight = 30; // Height of a row item in the OverflowMenu list.
      const dimensions = widgetMenu.getBoundingClientRect();
      const overflowMenuHeight = rowHeight * JSON.parse(widgetButtons).length;
      const visibleMenuHeight = dimensions.bottom + overflowMenuHeight;
      if (visibleMenuHeight > window.innerHeight) {
        const overflowMenu = document.getElementsByClassName('bx--overflow-menu-options--open')[0];
        if (overflowMenu) {
          overflowMenu.style.top = `${dimensions.top - overflowMenuHeight}px`;
        }
      }
    }
  };

  return (
    <div className="card-pf card-pf-view">
      <div className="card-pf-body">
        <div className="card-pf-heading-kebab">
          <OverflowMenu
            className="widget-overflow-menu"
            id={`${widgetId}-menu`}
            aria-labelledby={`widget-title-${widgetId}`}
            flipped
            onOpen={() => overflowMenuDirection(widgetId, widgetButtons)}
          >
            {getOverflowButtons(widgetButtons, widgetId, widgetType, widgetTitle, setState, widgetModel, widgetLastRun, widgetNextRun)}
          </OverflowMenu>
          <div className="card-pf-title sortable-handle ui-sortable-handle">
            <span id={`widget-title-${widgetId}`}>{widgetTitle}</span>
          </div>
        </div>
      </div>
      {getWidget(widgetId, isLoading, widgetModel, widgetType, widgetLastRun, widgetNextRun, error)}
      <WidgetRemoveModal showConfirm={showConfirm} setState={setState} widgetTitle={widgetTitle} href={href} />
    </div>
  );
};

WidgetWrapper.propTypes = {
  widgetId: PropTypes.number.isRequired,
  widgetType: PropTypes.string.isRequired,
  widgetButtons: PropTypes.string.isRequired,
  widgetLastRun: PropTypes.string,
  widgetNextRun: PropTypes.string,
  widgetTitle: PropTypes.string,
};

WidgetWrapper.defaultProps = {
  widgetLastRun: 'Never',
  widgetNextRun: 'Never',
  widgetTitle: '',
};

export default WidgetWrapper;