Source

components/InventoryDetail/AppInfo.js

import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { useLocation } from 'react-router-dom';
import { useSelector, useStore } from 'react-redux';
import {
  Skeleton,
  SkeletonSize,
} from '@redhat-cloud-services/frontend-components/Skeleton';

/**
 * Small component that just renders active detail with some specific class.
 * This component detail is accessed from redux if no component found `missing component` is displayed.
 *
 *  @param   {object}             props                 Component Props
 *  @param   {object}             props.componentMapper Enables passing different components list
 *  @param   {object}             props.activeApp       Identifies the active app
 *  @returns {React.ReactElement}                       Returns the app information for the active app
 */
const AppInfo = ({ componentMapper: Cmp, activeApp }) => {
  const store = useStore();
  const loaded = useSelector(({ entityDetails }) => entityDetails?.loaded);
  const entity = useSelector(({ entityDetails }) => entityDetails?.entity);

  if (loaded === true && !entity) {
    return null;
  }

  return (
    <Fragment>
      {loaded ? (
        activeApp && (
          <div className={`ins-active-app-${activeApp?.name}`}>
            {Cmp ? (
              <Cmp
                store={store}
                inventoryId={entity?.id}
                appName={activeApp?.name}
              />
            ) : (
              'missing component'
            )}
          </div>
        )
      ) : (
        <Skeleton size={SkeletonSize.md} />
      )}
    </Fragment>
  );
};

AppInfo.propTypes = {
  componentMapper: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
  activeApp: PropTypes.shape({
    title: PropTypes.node,
    name: PropTypes.string,
    pageId: PropTypes.string,
  }),
};

/**
 *  @deprecated This component can be removed once all apps migrate to componentMapper and activeApp
 *
 *  @param   {object}             props                 Component Props
 *  @param   {object}             props.componentMapper Enables passing different components list
 *  @param   {object}             props.activeApp       Identifies the active app
 *  @returns {React.ReactElement}                       Returns the app information for the active app
 */
const AppInfoWrapper = ({ componentMapper, activeApp, ...props }) => {
  const { search } = useLocation();
  const searchParams = new URLSearchParams(search);
  if (!componentMapper || !activeApp) {
    console.warn(
      'Please pass componentMapper and activeApp. We will be deprecating the old store controls',
    );
  }

  const currApp = useSelector(({ entityDetails }) => {
    const activeItem =
      searchParams.get('appName') || entityDetails?.activeApp?.appName;
    return (
      entityDetails?.activeApps?.find?.((item) => item?.name === activeItem) ||
      entityDetails?.activeApps?.[0]
    );
  });

  const currComponent = componentMapper || (activeApp || currApp)?.component;

  return (
    <AppInfo
      componentMapper={currComponent}
      activeApp={activeApp || currApp}
      {...props}
    />
  );
};

AppInfoWrapper.propTypes = AppInfo.propTypes;

/**
 *  @deprecated Remove once all apps send `componentMapper` and `activeApp` and use directly AppInfo
 *
 *  @param   {object}             props Component Props
 *  @returns {React.ReactElement}       Returns the app information for the active app
 */
const AppInfoCmp = (props) =>
  props.componentMapper && props.activeApp ? (
    <AppInfo {...props} />
  ) : (
    <AppInfoWrapper {...props} />
  );

AppInfoCmp.propTypes = AppInfo.propTypes;

export default AppInfoCmp;