diff --git a/assets/js/components/ExecutionResults/ExecutionResults.jsx b/assets/js/components/ExecutionResults/ExecutionResults.jsx index 31628f14df..d199ba1d75 100644 --- a/assets/js/components/ExecutionResults/ExecutionResults.jsx +++ b/assets/js/components/ExecutionResults/ExecutionResults.jsx @@ -4,28 +4,59 @@ import Table from '@components/Table'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import LoadingBox from '@components/LoadingBox'; +import Modal from '@components/Modal'; -import { getCheckResults, getCheckDescription } from './checksUtils'; +import { + getCheckResults, + getCheckDescription, + getCheckRemediation, +} from './checksUtils'; import ResultsContainer from './ResultsContainer'; import { ExecutionIcon } from './ExecutionIcon'; import CheckResultOutline from './CheckResultOutline'; import ExecutionHeader from './ExecutionHeader'; +const addHostnameToTargets = (targets, hostnames) => + targets?.map((target) => { + const { agent_id } = target; + + const { hostname } = hostnames.find(({ id }) => agent_id === id); + return { + ...target, + hostname, + }; + }); + const resultsTableConfig = { usePadding: false, columns: [ { title: 'Id', key: 'checkID', - render: (checkID) => ( -
{checkID}
+ render: (checkID, { onClick }) => ( +
+ +
), }, { title: 'Description', key: 'description', - render: (description) => {description}, + render: (description) => ( + + {description} + + ), }, { title: 'Result', @@ -50,25 +81,6 @@ const resultsTableConfig = { ), }; -const addHostnameToTargets = (targets, hostnames) => - targets?.map((target) => { - const { agent_id } = target; - - const { hostname } = hostnames.find(({ id }) => agent_id === id); - return { - ...target, - hostname, - }; - }); - -function MarkdownContent({ children }) { - return ( - - {children} - - ); -} - function ExecutionResults({ clusterID, clusterName, @@ -89,6 +101,8 @@ function ExecutionResults({ onStartExecution = () => {}, }) { const [predicates, setPredicates] = useState([]); + const [selectedCheck, setSelectedCheck] = useState(null); + const [modalOpen, setModalOpen] = useState(false); const hosts = hostnames.map((item) => item.id); @@ -135,6 +149,10 @@ function ExecutionResults({ description: getCheckDescription(catalog, checkID), expectationResults, agentsCheckResults: addHostnameToTargets(agentsCheckResults, hostnames), + onClick: () => { + setModalOpen(true); + setSelectedCheck(checkID); + }, }) ); @@ -162,6 +180,19 @@ function ExecutionResults({ > + + {getCheckDescription(catalog, selectedCheck)} + + } + onClose={() => setModalOpen(false)} + > + + {getCheckRemediation(catalog, selectedCheck)} + + ); } diff --git a/assets/js/components/ExecutionResults/ExecutionResults.test.jsx b/assets/js/components/ExecutionResults/ExecutionResults.test.jsx index 512e34f683..02bdd4fb32 100644 --- a/assets/js/components/ExecutionResults/ExecutionResults.test.jsx +++ b/assets/js/components/ExecutionResults/ExecutionResults.test.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { screen } from '@testing-library/react'; - +import userEvent from '@testing-library/user-event'; import { faker } from '@faker-js/faker'; import { renderWithRouter } from '@lib/test-utils'; @@ -411,4 +411,71 @@ describe('ExecutionResults', () => { ) ).toBeTruthy(); }); + + it('should open remediation modal when clicking on checkID and close it when clicking outside', async () => { + const { + clusterID, + hostnames, + loading, + catalog, + executionError, + executionStarted, + executionResult, + checks, + } = prepareStateData('completed'); + + renderWithRouter( + + ); + + const { id: checkID, remediation } = catalog[0]; + expect(screen.getByText(checkID).textContent).toBe(checks[0]); + expect(screen.queryByText(remediation)).not.toBeInTheDocument(); + await userEvent.click(screen.getByText(checkID)); + expect(screen.queryByText(remediation)).toBeInTheDocument(); + await userEvent.click(document.body); + expect(screen.queryByText(remediation)).not.toBeInTheDocument(); + }); + + it('should not open remediation modal when clicking on description', async () => { + const { + clusterID, + hostnames, + loading, + catalog, + executionError, + executionStarted, + executionResult, + checks, + } = prepareStateData('completed'); + + renderWithRouter( + + ); + + const { remediation, description } = catalog[0]; + expect(screen.getByText(description).textContent).toBe(description); + userEvent.click(screen.getByText(description)); + expect(screen.queryByText(remediation)).not.toBeInTheDocument(); + }); });