From 30d1d63813402ff8e056885f61b367784a496d86 Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Tue, 1 Feb 2022 11:10:24 -0500 Subject: [PATCH] Add wf node list item info popover (#11587) --- .../CheckboxListItem/CheckboxListItem.js | 12 ++++ .../CheckboxListItem/CheckboxListItem.test.js | 29 ++++++++ .../src/components/DetailList/DetailList.js | 4 +- .../NodeTypeStep/JobTemplatesList.js | 66 ++++++++++++++++++- .../NodeTypeStep/JobTemplatesList.test.js | 39 +++++++++++ 5 files changed, 147 insertions(+), 3 deletions(-) diff --git a/awx/ui/src/components/CheckboxListItem/CheckboxListItem.js b/awx/ui/src/components/CheckboxListItem/CheckboxListItem.js index fee1892cbc..bce6a322c2 100644 --- a/awx/ui/src/components/CheckboxListItem/CheckboxListItem.js +++ b/awx/ui/src/components/CheckboxListItem/CheckboxListItem.js @@ -3,6 +3,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { t } from '@lingui/macro'; import { Td, Tr } from '@patternfly/react-table'; +import { ActionsTd } from 'components/PaginatedTable'; const CheckboxListItem = ({ isRadio = false, @@ -15,6 +16,7 @@ const CheckboxListItem = ({ onSelect, columns, item, + rowActions, }) => { const handleRowClick = () => { if (isSelected && !isRadio) { @@ -62,6 +64,16 @@ const CheckboxListItem = ({ {label} )} + {rowActions && ( + + {rowActions.map((rowAction) => { + const { + props: { id }, + } = rowAction; + return {rowAction}; + })} + + )} ); }; diff --git a/awx/ui/src/components/CheckboxListItem/CheckboxListItem.test.js b/awx/ui/src/components/CheckboxListItem/CheckboxListItem.test.js index b45cb6c3a8..fe54582de2 100644 --- a/awx/ui/src/components/CheckboxListItem/CheckboxListItem.test.js +++ b/awx/ui/src/components/CheckboxListItem/CheckboxListItem.test.js @@ -21,4 +21,33 @@ describe('CheckboxListItem', () => { ); expect(wrapper).toHaveLength(1); }); + + test('should render row actions', () => { + const wrapper = mount( + + + {}} + onDeselect={() => {}} + rowActions={[ +
action_1
, +
action_2
, + ]} + /> +
+
+ ); + expect( + wrapper + .find('ActionsTd') + .containsAllMatchingElements([ +
action_1
, +
action_2
, + ]) + ).toEqual(true); + }); }); diff --git a/awx/ui/src/components/DetailList/DetailList.js b/awx/ui/src/components/DetailList/DetailList.js index e6999d8246..b03a9146fe 100644 --- a/awx/ui/src/components/DetailList/DetailList.js +++ b/awx/ui/src/components/DetailList/DetailList.js @@ -2,7 +2,7 @@ import React from 'react'; import { TextList, TextListVariants } from '@patternfly/react-core'; import styled from 'styled-components'; -const DetailList = ({ children, stacked, ...props }) => ( +const DetailList = ({ children, stacked, compact, ...props }) => ( {children} @@ -10,8 +10,8 @@ const DetailList = ({ children, stacked, ...props }) => ( export default styled(DetailList)` display: grid; - grid-gap: 20px; align-items: start; + ${(props) => (props.compact ? `column-gap: 20px;` : `grid-gap: 20px;`)} ${(props) => props.stacked ? ` diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.js b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.js index 4f94bdb398..eccd397cd9 100644 --- a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.js +++ b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.js @@ -2,13 +2,19 @@ import React, { useEffect, useCallback } from 'react'; import { useLocation } from 'react-router-dom'; import { t } from '@lingui/macro'; +import { Popover } from '@patternfly/react-core'; +import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons'; import { func, shape } from 'prop-types'; import { JobTemplatesAPI } from 'api'; import { getQSConfig, parseQueryString } from 'util/qs'; import useRequest from 'hooks/useRequest'; -import DataListToolbar from 'components/DataListToolbar'; import CheckboxListItem from 'components/CheckboxListItem'; +import ChipGroup from 'components/ChipGroup'; +import CredentialChip from 'components/CredentialChip'; +import DataListToolbar from 'components/DataListToolbar'; +import { Detail, DetailList } from 'components/DetailList'; import PaginatedTable, { + ActionItem, HeaderCell, HeaderRow, getSearchableKeys, @@ -20,6 +26,52 @@ const QS_CONFIG = getQSConfig('job-templates', { order_by: 'name', }); +function TemplatePopoverContent({ template }) { + return ( + + + + + {template.summary_fields?.credentials && + template.summary_fields.credentials.length ? ( + + {template.summary_fields.credentials.map((c) => ( + + ))} + + } + /> + ) : null} + + ); +} + function JobTemplatesList({ nodeResource, onUpdateNodeResource }) { const location = useLocation(); @@ -81,6 +133,18 @@ function JobTemplatesList({ nodeResource, onUpdateNodeResource }) { onSelect={() => onUpdateNodeResource(item)} onDeselect={() => onUpdateNodeResource(null)} isRadio + rowActions={[ + + } + headerContent={
{t`Details`}
} + id={`item-${item.id}-info-popover`} + position="right" + > + +
+
, + ]} /> )} renderToolbar={(props) => } diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.test.js b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.test.js index 0456680b7b..069dd8cc3f 100644 --- a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.test.js +++ b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.test.js @@ -82,6 +82,45 @@ describe('JobTemplatesList', () => { }); }); + test('Row should display popover', async () => { + JobTemplatesAPI.read.mockResolvedValueOnce({ + data: { + count: 1, + results: [ + { + id: 1, + name: 'Test Job Template', + type: 'job_template', + url: '/api/v2/job_templates/1', + inventory: 1, + project: 2, + }, + ], + }, + }); + JobTemplatesAPI.readOptions.mockResolvedValue({ + data: { + actions: { + GET: {}, + POST: {}, + }, + related_search_fields: [], + }, + }); + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + wrapper.update(); + expect( + wrapper.find('CheckboxListItem[name="Test Job Template"] Popover').length + ).toBe(1); + }); + test('Error shown when read() request errors', async () => { JobTemplatesAPI.read.mockRejectedValue(new Error()); JobTemplatesAPI.readOptions.mockResolvedValue({