ManageIQ/manageiq-ui-classic

View on GitHub
app/javascript/components/miq-custom-tab/index.jsx

Summary

Maintainability
A
2 hrs
Test Coverage
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Tabs, Tab } from 'carbon-components-react';
import { useDispatch } from 'react-redux';
import { miqCustomTabActions } from '../../miq-redux/actions/miq-custom-tab-actions';
import { labelConfig, tabText } from './helper';

const MiqCustomTab = ({ containerId, tabLabels, type }) => {
  const dispatch = useDispatch();
  const [data, setData] = useState({ loading: false });

  /** Labels used for a Tab found from the 'type'. */
  const selectedLabels = labelConfig(type);

  /** Configuration for tabs using this component.
   * type: Unique string used to identify the tab component.
   * js: Returns a function that gets executed on tab onClick event.
   * url: Redirects to the specified url on tab onClick event.
   */
  const tabConfigurations = (name) => [
    { type: 'AE-RESOLVE-OPTIONS' },
    { type: 'CATALOG_SUMMARY' },
    { type: 'CATALOG_EDIT', js: () => name === 'detail' && dispatch(miqCustomTabActions.incrementClickCount()) },
    { type: 'CATALOG_REQUEST_INFO', url: `/miq_request/prov_field_changed?tab_id=${name}&edit_mode=true` },
    { type: 'UTILIZATION' },
  ];

  const configuration = (name) => tabConfigurations(name).find((item) => item.type === type);

  const tabIdentifier = (name) => {
    const config = configuration(name);
    const cType = config && config.url ? 'dynamic' : 'static';
    return `${type.toLowerCase()}_${cType}`;
  };

  /** Function to find the tabs dom elements using the container Id. */
  const containerTabs = () => {
    const container = document.getElementById(containerId);
    if (!container) {
      return [];
    }
    return container.getElementsByClassName('tab_content');
  };

  /** Function to clear all tabs content so that, the react component will render a fresh instance */
  const clearTabContents = () => {
    const tabs = containerTabs();
    tabs.forEach((child) => {
      child.innerHTML = '';
    });
  };

  /** Function to load the tab contents which are already available within the page. */
  const staticContents = (name, config) => {
    const tabs = containerTabs();
    tabs.forEach((child) => {
      if (child.parentElement.id === containerId) {
        child.classList.remove('active');
        if (child.id === `${name}`) {
          child.classList.add('active');
          if (config.js) config.js();
        }
      }
    });
    miqSparkleOff();
  };

  /** Function to load tab contents after a url is executed.
   *  After the url is executed, the selected tab contents are displays using the staticContents function.
  */
  const dynamicContents = (name, config) => {
    clearTabContents();
    window.miqJqueryRequest(config.url).then(() => {
      staticContents(name, config);
      setData({ loading: false });
    });
  };

  /** Function to handle tab click events. */
  const onTabSelect = (name) => {
    if (!data.loading) {
      miqSparkleOn();
      const config = configuration(name);
      return config && config.url ? dynamicContents(name, config) : staticContents(name, config);
    }
    return data;
  };

  /** Function to render the tabs from the tabLabels props */
  const renderTabs = () => tabLabels.map((label) => (
    <Tab key={`tab${label}`} label={`${tabText(selectedLabels, label)}`} onClick={() => onTabSelect(label)} />
  ));

  return (
    <Tabs className="miq_custom_tabs" id={tabIdentifier('')}>
      {renderTabs()}
    </Tabs>
  );
};

export default MiqCustomTab;

MiqCustomTab.propTypes = {
  containerId: PropTypes.string.isRequired,
  tabLabels: PropTypes.arrayOf(PropTypes.string).isRequired,
  type: PropTypes.string.isRequired,
};