Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Expand Up @@ -5,22 +5,22 @@ import {
DescriptionListTerm,
} from '@patternfly/react-core';
import { ExpandableRowContent, Tr } from '@patternfly/react-table';
import { SECURITY_ENTERPRISE_CONTRACT_POLICY_AVAILABLE_RULE_COLLECTIONS_URL } from '~/consts/documentation';
import { CONFORMA_POLICY_AVAILABLE_RULE_COLLECTIONS_URL } from '~/consts/documentation';
import { UIConformaData } from '~/types/conforma';
import { ExternalLink, Timestamp } from '../../../shared';
import { UIEnterpriseContractData } from '../types';
import './EnterpriceContractTable.scss';
import './ConformaTable.scss';

interface Props {
obj: UIEnterpriseContractData;
obj: UIConformaData;
}

export const EnterpriseContractExpandedRowContent: React.FC<Props> = ({ obj }) => {
export const ConformaExpandedRowContent: React.FC<Props> = ({ obj }) => {
if (!obj.description && !obj.collection?.length && !obj.solution && !obj.timestamp) return null;

return (
<Tr className="ex-expanded-row" data-test="ec-expand-content">
<Tr className="conforma-expanded-row" data-test="conforma-expand-content">
<ExpandableRowContent>
<DescriptionList className="ec-description-list">
<DescriptionList className="conforma-description-list">
<DescriptionListGroup>
<DescriptionListTerm>Rule Description</DescriptionListTerm>
<DescriptionListDescription>{obj.description ?? '-'}</DescriptionListDescription>
Expand All @@ -30,9 +30,7 @@ export const EnterpriseContractExpandedRowContent: React.FC<Props> = ({ obj }) =
<DescriptionListGroup>
<DescriptionListTerm>Collection</DescriptionListTerm>
<DescriptionListDescription>
<ExternalLink
href={SECURITY_ENTERPRISE_CONTRACT_POLICY_AVAILABLE_RULE_COLLECTIONS_URL}
>
<ExternalLink href={CONFORMA_POLICY_AVAILABLE_RULE_COLLECTIONS_URL}>
{obj.collection.join(', ')}
</ExternalLink>
</DescriptionListDescription>
Expand Down
23 changes: 23 additions & 0 deletions src/components/Conforma/ConformaTable/ConformaHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createTableHeaders } from '~/shared/components/table/utils';

export const ConformaTableColumnClasses = {
rules: 'pf-m-width-30 wrap-column',
status: 'pf-m-width-10',
message: 'pf-m-width-30 wrap-column',
component: 'pf-m-width-25',
};

export const enum SortableConformaHeaders {
title,
component,
status,
}

const conformaColumns = [
{ title: 'Rules', className: ConformaTableColumnClasses.rules, sortable: true },
{ title: 'Status', className: ConformaTableColumnClasses.status, sortable: true },
{ title: 'Message', className: ConformaTableColumnClasses.message },
{ title: 'Component', className: ConformaTableColumnClasses.component, sortable: true },
];

export default createTableHeaders(conformaColumns);
63 changes: 63 additions & 0 deletions src/components/Conforma/ConformaTable/ConformaRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as React from 'react';
import { useParams, Link } from 'react-router-dom';
import { Truncate } from '@patternfly/react-core';
import { COMPONENT_DETAILS_PATH } from '@routes/paths';
import { TableData } from '~/shared';
import { useNamespace } from '~/shared/providers/Namespace';
import { UIConformaData } from '~/types/conforma';
import { getRuleStatus } from '../utils';
import { ConformaTableColumnClasses } from './ConformaHeader';
import './ConformaTable.scss';

type ConformaRowType = {
data: UIConformaData;
};

const ConformaRow: React.FC<ConformaRowType> = ({ data }) => {
const namespace = useNamespace();
const { applicationName } = useParams();
return (
<>
<TableData className={`${ConformaTableColumnClasses.rules} vertical-center-cell`}>
{data.title ?? '-'}
</TableData>
<TableData
data-test="rule-status"
className={`${ConformaTableColumnClasses.status} vertical-center-cell`}
>
{getRuleStatus(data.status)}
</TableData>
<TableData className={`${ConformaTableColumnClasses.message} vertical-center-cell`}>
{data.msg ? <Truncate content={data.msg} /> : '-'}
</TableData>
<TableData className={`${ConformaTableColumnClasses.component} vertical-center-cell`}>
<Link
to={COMPONENT_DETAILS_PATH.createPath({
workspaceName: namespace,
applicationName: applicationName || '',
componentName: data.component,
})}
>
{data.component}
</Link>
</TableData>
</>
);
};

interface WrappedConformaRowProps {
obj: UIConformaData;
customData: { sortedConformaResult: UIConformaData[] };
}

export const WrappedConformaRow: React.FC<WrappedConformaRowProps> = ({ obj, customData }) => {
const customConformaResult = customData?.sortedConformaResult;

if (Array.isArray(customConformaResult) && customConformaResult.length > 0) {
const index = customConformaResult.findIndex((item) => item === obj);

return <ConformaRow data={obj} key={index} />;
}

return null;
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
.ex-expanded-row {
.conforma-expanded-row {
display: flex;
padding-top: var(--pf-v5-global--spacer--md);
padding-bottom: var(--pf-v5-global--spacer--md);
padding-right: var(--pf-v5-global--spacer--md);
padding-left: 4%; // to align with the "first content" column
}

.ec-description-list {
.conforma-description-list {
// means the grid will have 3 equal-width columns,
// each taking up one fraction (1fr) of the available space.
--pf-v5-c-description-list--GridTemplateColumns: repeat(3, 1fr);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import * as React from 'react';
import { SortByDirection } from '@patternfly/react-table';
import { Table } from '~/shared';
import { ENTERPRISE_CONTRACT_STATUS, UIEnterpriseContractData } from '../types';
import { EnterpriseContractExpandedRowContent } from './EnterpriseContractExpandedRowContent';
import getEnterpriseContractHeader from './EnterpriseContractHeader';
import { WrappedEnterpriseContractRow } from './EnterpriseContractRow';
import './EnterpriceContractTable.scss';
import { CONFORMA_RESULT_STATUS, UIConformaData } from '~/types/conforma';
import { ConformaExpandedRowContent } from './ConformaExpandedRowContent';
import getConformaHeader from './ConformaHeader';
import { WrappedConformaRow } from './ConformaRow';
import './ConformaTable.scss';

type EnterpriseContractTableProps = {
ecResult: UIEnterpriseContractData[];
type ConformaTableProps = {
crResult: UIConformaData[];
};

const STATUS_SORT_ORDER = [
ENTERPRISE_CONTRACT_STATUS.violations,
ENTERPRISE_CONTRACT_STATUS.warnings,
ENTERPRISE_CONTRACT_STATUS.successes,
CONFORMA_RESULT_STATUS.violations,
CONFORMA_RESULT_STATUS.warnings,
CONFORMA_RESULT_STATUS.successes,
];
const COLUMN_ORDER = [undefined, 'title', 'status', 'msg', 'component'];

export const getSortColumnFuntion = (key: string, activeSortDirection: string) => {
switch (key) {
case 'status':
return (a: UIEnterpriseContractData, b: UIEnterpriseContractData) => {
return (a: UIConformaData, b: UIConformaData) => {
const aValue = STATUS_SORT_ORDER.indexOf(a[key]);
const bValue = STATUS_SORT_ORDER.indexOf(b[key]);
if (aValue < bValue) {
Expand All @@ -33,7 +33,7 @@ export const getSortColumnFuntion = (key: string, activeSortDirection: string) =
};

default:
return (a: UIEnterpriseContractData, b: UIEnterpriseContractData) => {
return (a: UIConformaData, b: UIConformaData) => {
const aValue = a[key];
const bValue = b[key];
if (typeof aValue === 'string' && typeof bValue === 'string') {
Expand All @@ -47,49 +47,53 @@ export const getSortColumnFuntion = (key: string, activeSortDirection: string) =
}
};

export const EnterpriseContractTable: React.FC<
React.PropsWithChildren<EnterpriseContractTableProps>
> = ({ ecResult }) => {
export const ConformaTable: React.FC<React.PropsWithChildren<ConformaTableProps>> = ({
crResult,
}) => {
const [activeSortIndex, setActiveSortIndex] = React.useState<number | null>(2);
const [activeSortDirection, setActiveSortDirection] = React.useState<
SortByDirection.asc | SortByDirection.desc | null
>(SortByDirection.asc);

const EnterpriseContractHeader = React.useMemo(
const ConformaHeader = React.useMemo(
() =>
getEnterpriseContractHeader(activeSortIndex, activeSortDirection, (_, index, direction) => {
getConformaHeader(activeSortIndex, activeSortDirection, (_, index, direction) => {
setActiveSortIndex(index);
setActiveSortDirection(direction);
}),
[activeSortDirection, activeSortIndex],
);

const sortedECResult = React.useMemo(() => {
return ecResult
? ecResult.sort(getSortColumnFuntion(COLUMN_ORDER[activeSortIndex], activeSortDirection))
const sortedCRResult = React.useMemo(() => {
return crResult
? crResult.sort(getSortColumnFuntion(COLUMN_ORDER[activeSortIndex], activeSortDirection))
: undefined;
}, [activeSortDirection, activeSortIndex, ecResult]);
}, [activeSortDirection, activeSortIndex, crResult]);

return sortedECResult ? (
return sortedCRResult ? (
<div className="pf-v5-c-table pf-m-compact pf-m-grid-md">
<Table
virtualize
data={sortedECResult}
aria-label="ec table"
Header={EnterpriseContractHeader}
data={sortedCRResult}
aria-label="conforma-table"
Header={ConformaHeader}
ExpandedContent={(props) => {
const obj = props.obj as UIEnterpriseContractData;
return <EnterpriseContractExpandedRowContent {...props} obj={obj} />;
const obj = props.obj as UIConformaData;
return <ConformaExpandedRowContent {...props} obj={obj} />;
}}
Row={(props) => {
const obj = props.obj as UIEnterpriseContractData;
const obj = props.obj as UIConformaData;

return (
<WrappedEnterpriseContractRow {...props} obj={obj} customData={{ sortedECResult }} />
<WrappedConformaRow
{...props}
obj={obj}
customData={{ sortedConformaResult: sortedCRResult }}
/>
);
}}
loaded
customData={{ sortedECResult }}
customData={{ sortedCRResult }}
expand
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { render, screen } from '@testing-library/react';
import { CONFORMA_RESULT_STATUS } from '~/types/conforma';
import { mockUseNamespaceHook } from '~/unit-test-utils/mock-namespace';
import { ENTERPRISE_CONTRACT_STATUS } from '../../types';
import { EnterpriseContractExpandedRowContent } from '../EnterpriseContractExpandedRowContent';
import { ConformaExpandedRowContent } from '../ConformaExpandedRowContent';

const rowContent = {
title: 'dummyTitle',
status: ENTERPRISE_CONTRACT_STATUS.violations,
status: CONFORMA_RESULT_STATUS.violations,
component: 'component-1',
description: 'dummy description',
msg: 'Fail',
Expand All @@ -23,11 +23,11 @@ const invalidContent = {
collection: null,
};

describe('EnterpriseContractExpandedRowContent', () => {
describe('ConformaExpandedRowContent', () => {
mockUseNamespaceHook('test-ns');

it('should render the component', () => {
render(<EnterpriseContractExpandedRowContent obj={rowContent} />);
render(<ConformaExpandedRowContent obj={rowContent} />);
screen.getByText('Effective from');
screen.getByText('Collection');
screen.getByText('abcd, efg');
Expand All @@ -36,7 +36,7 @@ describe('EnterpriseContractExpandedRowContent', () => {
});

it('should not render the component', () => {
render(<EnterpriseContractExpandedRowContent obj={invalidContent} />);
render(<ConformaExpandedRowContent obj={invalidContent} />);
expect(screen.queryByText('Effective from')).not.toBeInTheDocument();
expect(screen.queryByText('Collection')).not.toBeInTheDocument();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { render, screen } from '@testing-library/react';
import { UIEnterpriseContractData } from '~/types';
import { UIConformaData, CONFORMA_RESULT_STATUS } from '~/types/conforma';
import { mockUseNamespaceHook } from '~/unit-test-utils/mock-namespace';
import { ENTERPRISE_CONTRACT_STATUS } from '../../types';
import { EnterpriseContractExpandedRowContent } from '../EnterpriseContractExpandedRowContent';
import { WrappedEnterpriseContractRow } from '../EnterpriseContractRow';
import { ConformaExpandedRowContent } from '../ConformaExpandedRowContent';
import { WrappedConformaRow } from '../ConformaRow';

jest.mock('react-router-dom', () => {
const actual = jest.requireActual('react-router-dom');
Expand All @@ -20,14 +19,14 @@ jest.mock('react-router-dom', () => {

const dummySuccessRowData = {
title: 'dummyTitle',
status: ENTERPRISE_CONTRACT_STATUS.successes,
status: CONFORMA_RESULT_STATUS.successes,
component: 'component-1',
description: 'dummy description',
} as UIEnterpriseContractData;
} as UIConformaData;

const dumpFailRowData = {
title: 'dummyTitle',
status: ENTERPRISE_CONTRACT_STATUS.violations,
status: CONFORMA_RESULT_STATUS.violations,
component: 'component-1',
description: 'dummy description',
msg: 'Fail',
Expand All @@ -36,13 +35,13 @@ const dumpFailRowData = {
};

const customDummyData = {
sortedECResult: [dummySuccessRowData, dumpFailRowData],
sortedConformaResult: [dummySuccessRowData, dumpFailRowData],
};
describe('EnterpriseContractRow', () => {
describe('ConformaRow', () => {
mockUseNamespaceHook('test-ns');

it('should render the component', () => {
render(<WrappedEnterpriseContractRow customData={customDummyData} obj={dummySuccessRowData} />);
render(<WrappedConformaRow customData={customDummyData} obj={dummySuccessRowData} />);
screen.getByText('dummyTitle');
screen.getByText('component-1');
screen.getByText('Success');
Expand All @@ -52,8 +51,8 @@ describe('EnterpriseContractRow', () => {
it('should render Failed rule and failure message in table', () => {
render(
<>
<WrappedEnterpriseContractRow customData={customDummyData} obj={dumpFailRowData} />
<EnterpriseContractExpandedRowContent obj={dumpFailRowData} />
<WrappedConformaRow customData={customDummyData} obj={dumpFailRowData} />
<ConformaExpandedRowContent obj={dumpFailRowData} />
</>,
);
screen.getByText('dummyTitle');
Expand Down
Loading
Loading