Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Fragment } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import {
Stack,
Expand All @@ -19,8 +19,6 @@ import {
} from '@patternfly/react-core';
import { InsightsLabel } from '@redhat-cloud-services/frontend-components/InsightsLabel';
import {
CheckCircleIcon,
OutlinedQuestionCircleIcon,
ExternalLinkAltIcon,
PowerOffIcon
} from '@patternfly/react-icons';
Expand All @@ -38,6 +36,7 @@ import CsawLabel from '../Snippets/CsawLabel';
import CsawRuleSummary from './CsawRuleSummary';
import './CsawRuleBox.scss';
import { HashLink } from 'react-router-hash-link';
import RemediationPlanSplitItem from './components/RemediationPlanSplitItem';

const CsawRuleBox = ({ rules = [], synopsis, intl, setHeaderFilters, headerFilters }) => {
const sortedRules = [].concat(rules).sort((a, b) => (b.systems_affected - a.systems_affected));
Expand Down Expand Up @@ -134,54 +133,26 @@ const CsawRuleBox = ({ rules = [], synopsis, intl, setHeaderFilters, headerFilte
{RISK_OF_CHANGE_LABEL[rule.change_risk]}
</Tooltip>
</SplitItem>

<RemediationPlanSplitItem rule={rule} />

<SplitItem>
<Label className="label pf-v5-u-mb-xs">
{intl.formatMessage(messages.remediationLabel)}
Remediation requires reboot
</Label>
<Split>
<SplitItem>
{!rule.playbook_count
? intl.formatMessage(messages.no)
: (
<Fragment>
<Icon>
<CheckCircleIcon
className="checkCircleIcon pf-v5-u-mr-xs"
data-testid="check-circle-icon"
/>
</Icon>
{intl.formatMessage(messages.yes)}
<Tooltip
content={intl.formatMessage(
messages.ansibleRemediationTooltip
)}
>
<Icon>
<OutlinedQuestionCircleIcon
className="l-sm-spacer outlinedQuestionCircleIcon"
/>
</Icon>
</Tooltip>
</Fragment>
)
}
</SplitItem>
<SplitItem className="pf-v5-u-ml-md">
{rule.reboot_required &&
<Text>
<Icon>
<PowerOffIcon
className="pf-v5-u-mr-xs powerOffIcon"
data-testid="power-off-icon"
/>
</Icon>
{intl.formatMessage(messages.rebootRequired)}
</Text>
}
<Text data-testid="reboot-required">
<Icon>
<PowerOffIcon
className="pf-v5-u-mr-xs"
/>
</Icon>
{rule.reboot_required ? intl.formatMessage(messages.yes) : intl.formatMessage(messages.no) }
</Text>
</SplitItem>
</Split>
</SplitItem>

</Split>
</TextContent>
</StackItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import CsawRuleBox from './CsawRuleBox'
import configureStore from 'redux-mock-store';
import { initialState } from '../../../Store/Reducers/CVEDetailsPageStore';
import { fireEvent, render, screen } from '@testing-library/react';
import { fireEvent, render, screen, within } from '@testing-library/react';
import TestWrapper from '../../../Utilities/TestWrapper';
import '@testing-library/jest-dom';

Expand Down Expand Up @@ -73,7 +73,7 @@ describe('CSAW Rule Box', () => {
</TestWrapper>
);

expect(screen.queryByTestId('power-off-icon')).toBeNull();
expect(within(screen.queryByTestId('reboot-required')).getByText('No')).toBeInTheDocument();
});

it('Should render with reboot warning', () => {
Expand All @@ -99,7 +99,7 @@ describe('CSAW Rule Box', () => {
</TestWrapper>
);

expect(screen.getByTestId('power-off-icon')).not.toBeNull();
expect(within(screen.queryByTestId('reboot-required')).getByText('Yes')).toBeInTheDocument();
});

it('Should render assosiated cve links', () => {
Expand Down Expand Up @@ -155,7 +155,7 @@ describe('CSAW Rule Box', () => {
</TestWrapper>
);

expect(screen.getAllByTestId('check-circle-icon')).toHaveLength(1);
expect(within(screen.getByTestId("remediation-plan")).getByText("Available")).toBeInTheDocument()
});

it('Should render without playbook', () => {
Expand Down Expand Up @@ -183,7 +183,7 @@ describe('CSAW Rule Box', () => {
</TestWrapper>
);

expect(screen.queryByTestId('check-circle-icon')).toBeNull();
expect(within(screen.getByTestId("remediation-plan")).getByText("Not available")).toBeInTheDocument()
});

it('Should render with knowledge base link', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Tooltip,
SplitItem,
Split,
Icon
} from '@patternfly/react-core';
import {
CheckCircleIcon,
TimesIcon
} from '@patternfly/react-icons';
import Label from '../../Snippets/Label';

const RemediationPlanSplitItem = ({ rule }) => (
<SplitItem>
<Label className="label pf-v5-u-mb-xs">
Remediation plan
</Label>
<Split>
<SplitItem data-testid="remediation-plan">
{rule?.playbook_count === 0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the repeated status rendering logic into a reusable StatusIndicator component with a lookup map.

You can collapse all of that nested JSX and duplicated fragments into a small, self-contained StatusIndicator component and a simple lookup map. For example:

// StatusIndicator.js
import React from 'react';
import PropTypes from 'prop-types';
import { Tooltip, Icon } from '@patternfly/react-core';
import { CheckCircleIcon, TimesIcon } from '@patternfly/react-icons';

const STATUS_MAP = {
  available: {
    IconComponent: CheckCircleIcon,
    tooltip: 'You can create an Ansible playbook to remediate your systems.',
    label: 'Available',
    iconClass: 'pf-v5-u-mr-xs',
  },
  unavailable: {
    IconComponent: TimesIcon,
    tooltip: 'No Ansible playbooks are available, manual remediation is required for patching.',
    label: 'Not available',
    iconClass: 'pf-v5-u-mr-xs',
  },
};

const StatusIndicator = ({ status }) => {
  const { IconComponent, tooltip, label, iconClass } = STATUS_MAP[status];
  return (
    <>
      <Tooltip content={tooltip}>
        <Icon>
          <IconComponent className={iconClass} />
        </Icon>
      </Tooltip>
      {label}
    </>
  );
};

StatusIndicator.propTypes = {
  status: PropTypes.oneOf(['available', 'unavailable']).isRequired,
};

export default StatusIndicator;

Then simplify your main component to:

import React from 'react';
import PropTypes from 'prop-types';
import { Split, SplitItem } from '@patternfly/react-core';
import Label from '../../Snippets/Label';
import StatusIndicator from './StatusIndicator';

const RemediationPlanSplitItem = ({ rule }) => {
  const status = rule?.playbook_count > 0 ? 'available' : 'unavailable';

  return (
    <SplitItem>
      <Label className="label pf-v5-u-mb-xs">Remediation plan</Label>
      <Split>
        <SplitItem data-testid="remediation-plan">
          <StatusIndicator status={status} />
        </SplitItem>
      </Split>
    </SplitItem>
  );
};

RemediationPlanSplitItem.propTypes = {
  rule: PropTypes.shape({
    playbook_count: PropTypes.number,
  }),
};

export default RemediationPlanSplitItem;

This removes the inline fragments, duplicate tooltips/icons, and makes it trivial to extend or tweak in the future.

? (
<>
<Tooltip
content="No Ansible playbooks are available, manual remediation is required for patching."
>
<Icon>
<TimesIcon
className="pf-v5-u-mr-xs"
data-testid="times-icon"
/>
</Icon>
</Tooltip>
Not available
</>
) : (
<>
<Tooltip
content="You can create an Ansible playbook to remediate your systems."
>
<Icon>
<CheckCircleIcon
className="checkCircleIcon pf-v5-u-mr-xs"
data-testid="check-circle-icon"
/>
</Icon>
</Tooltip>
Available
</>
)
}
</SplitItem>
</Split>
</SplitItem>
);

RemediationPlanSplitItem.propTypes = {
rule: PropTypes.object
};

export default RemediationPlanSplitItem;
Loading