Source

PresentationalComponents/RulesTable/RulesTable.js

import React, { useState, useMemo } from 'react';
import propTypes from 'prop-types';
import { Skeleton } from '@patternfly/react-core';
import { COMPLIANCE_TABLE_DEFAULTS } from '@/constants';
import { ComplianceRemediationButton } from 'PresentationalComponents';
import { ComplianceTable } from 'PresentationalComponents';
import RuleDetailsRow from './RuleDetailsRow';
import buildFilterConfig from './Filters';
import defaultColumns from './Columns';

/**
 * A component to show rules of a policy or test result.
 *
 *  @param   {object}             [props]                      React component props
 *  @param   {Array}              props.columns                A set of RulesTable columns
 *  @param   {Array}              [props.rules]                A set of rules (for the current page to show)
 *  @param   {number}             [props.total]                The overall total number of rules available to go through
 *  @param   {object}             [props.ruleTree]             A table tree to show the rules in. If provided it will enable the "table view" toggle
 *  @param   {string}             [props.policyId]             A policy ID used for remediations and as profile for rules
 *  @param   {string}             [props.policyName]           A policy name used in the profile of rule items
 *  @param   {object}             [props.reportTestResult]     A report test result used for the remediatons button
 *  @param   {boolean}            [props.remediationsEnabled]  Enables the "RemediationButton"
 *  @param   {boolean}            [props.ansibleSupportFilter] Enables the ansible filter
 *  @param   {boolean}            [props.selectedFilter]       Enables the "Selected Only" filter
 *  @param   {Array}              [props.selectedRules]        An array of rule IDs currently selected
 *  @param   {Array}              [props.preselected]          An array of rule IDs set as initial selection
 *  @param   {boolean}            [props.hidePassed]           Enables a default filter to only show failed rules.
 *  @param   {object}             [props.options ]             AsyncTableTools options
 *  @param   {boolean}            [props.activeFiltersPassed]  Enable Default filter
 *  @param   {string}             [props.activeFilters]        Default filter
 *  @param   {object}             [props.ruleValues]           An object of values to show for certain rule values
 *  @param   {Array}              [props.valueDefinitions]     An array of value definitons available for rules in the table
 *  @param   {boolean}            [props.skipValueDefinitions] TODO
 *  @param   {Function}           [props.onRuleValueReset]     A function called when a rule value is reset
 *  @param   {React.ReactElement} [props.DedicatedAction]      A dedicated action to show in the table
 *  @param   {object}             [props.valueOverrides]       An object of rule values // TODO we should make the name of this more consistent
 *  @param   {Function}           [props.onValueOverrideSave]  A funciton called when a value is saved
 *  @param   {Function}           [props.onSelect]             A function called when a selection action is performed
 *  @param   {string}             [props.defaultTableView]     A table view to show by default ("row" or "tree") // TODO we should use the table options directly
 *
 *  @returns {React.ReactElement}
 *
 *  @category Compliance
 *
 */
const RulesTable = ({
  rules,
  ruleTree,
  policyId,
  policyName,
  columns = defaultColumns,
  remediationsEnabled = true,
  ansibleSupportFilter = false,
  selectedFilter = false, // TODO this is potentially obsolete.
  selectedRules: selectedRulesProp = [],
  preselected,
  hidePassed = false,
  options,
  activeFiltersPassed = false,
  activeFilters,
  ruleValues,
  valueDefinitions,
  skipValueDefinitions,
  onRuleValueReset,
  DedicatedAction,
  onValueOverrideSave,
  total,
  onSelect,
  defaultTableView = 'tree',
  reportTestResult,
  valueOverrides,
  ...rulesTableProps
}) => {
  const internalSelectedState = useState([]);
  const [selectedRules, setSelectedRules] =
    typeof onSelect === 'function'
      ? [selectedRulesProp, onSelect]
      : internalSelectedState;
  const showRuleStateFilter =
    columns.filter((c) => c.title === 'Rule state').length > 0;

  // TODO implement policies filter
  const policies = [];

  const remediationAction = () => (
    <ComplianceRemediationButton
      reportTestResults={selectedRules?.length ? [reportTestResult] : []}
      selectedRuleResultIds={selectedRules}
      reportId={policyId}
    />
  );

  const DetailsRow = useMemo(() => {
    function Row(props) {
      const item = {
        ...(props?.item || {}), // eslint-disable-line react/prop-types
        profile: { id: policyId, name: policyName },
      };

      return props?.item?.loaded || skipValueDefinitions ? ( // eslint-disable-line react/prop-types
        <RuleDetailsRow
          onValueChange={onValueOverrideSave}
          onRuleValueReset={onRuleValueReset}
          item={item}
        />
      ) : (
        // TODO This doesn't appear correctly in the tree view
        <Skeleton />
      );
    }

    return Row;
  }, [policyId, policyName, onValueOverrideSave, onRuleValueReset]);

  return (
    <ComplianceTable
      aria-label="Rules Table"
      items={rules}
      columns={columns}
      isStickyHeader
      filters={{
        filterConfig: buildFilterConfig({
          showRuleStateFilter,
          policies,
          ansibleSupportFilter,
        }),
        ...(hidePassed && {
          activeFilters: (currentActiveFilters = {}) => ({
            ...currentActiveFilters,
            ['rule-state']: currentActiveFilters['rule-state']
              ? currentActiveFilters['rule-state']
              : ['failed'],
            ...activeFilters,
          }),
        }),
        ...(activeFiltersPassed && {
          activeFilters: { ...activeFilters },
        }),
      }}
      options={{
        ...COMPLIANCE_TABLE_DEFAULTS,
        ...options,
        defaultTableView,
        // TODO set this in views where we want a tree and make rows default
        enableTreeView: true,
        tableTree: ruleTree,
        onSelect: (onSelect || remediationsEnabled) && setSelectedRules,
        selected: selectedRules,
        preselected: preselected,
        detailsComponent: DetailsRow,
        selectedFilter,
        dedicatedAction: DedicatedAction,
        ...(remediationsEnabled ? { dedicatedAction: remediationAction } : {}),
        total,
      }}
      total={total}
      {...rulesTableProps}
    />
  );
};

RulesTable.propTypes = {
  rules: propTypes.array,
  ruleTree: propTypes.array,
  policyId: propTypes.string,
  policyName: propTypes.string,
  hidePassed: propTypes.bool,
  activeFiltersPassed: propTypes.bool,
  remediationsEnabled: propTypes.bool,
  ansibleSupportFilter: propTypes.bool,
  selectedRules: propTypes.array,
  preselected: propTypes.array,
  selectedFilter: propTypes.bool,
  columns: propTypes.array,
  options: propTypes.object,
  activeFilters: propTypes.object,
  ruleValues: propTypes.object,
  onRuleValueReset: propTypes.func,
  DedicatedAction: propTypes.node,
  valueDefinitions: propTypes.shape({
    data: propTypes.any,
    loading: propTypes.bool.isRequired,
  }),
  skipValueDefinitions: propTypes.bool,
  onValueOverrideSave: propTypes.func,
  onSelect: propTypes.oneOf([propTypes.func, propTypes.bool]),
  total: propTypes.number,
  defaultTableView: propTypes.string,
  reportTestResult: propTypes.object,
  valueOverrides: propTypes.object,
};

export default RulesTable;