diff --git a/awx/ui/src/screens/Host/HostList/HostList.js b/awx/ui/src/screens/Host/HostList/HostList.js index f0c03a00a5..da270228ec 100644 --- a/awx/ui/src/screens/Host/HostList/HostList.js +++ b/awx/ui/src/screens/Host/HostList/HostList.js @@ -14,6 +14,7 @@ import PaginatedTable, { } from 'components/PaginatedTable'; import useRequest, { useDeleteItems } from 'hooks/useRequest'; import useSelected from 'hooks/useSelected'; +import useExpanded from 'hooks/useExpanded'; import { encodeQueryString, getQSConfig, parseQueryString } from 'util/qs'; import HostListItem from './HostListItem'; @@ -88,6 +89,9 @@ function HostList() { const { selected, isAllSelected, handleSelect, selectAll, clearSelected } = useSelected(hosts); + const { expanded, isAllExpanded, handleExpand, expandAll } = + useExpanded(hosts); + const { isLoading: isDeleteLoading, deleteItems: deleteHosts, @@ -165,6 +169,8 @@ function HostList() { {...props} isAllSelected={isAllSelected} onSelectAll={selectAll} + isAllExpanded={isAllExpanded} + onExpandAll={expandAll} qsConfig={QS_CONFIG} additionalControls={[ ...(canAdd @@ -195,6 +201,8 @@ function HostList() { row.id === host.id)} + onExpand={() => handleExpand(host)} detailUrl={`${match.url}/${host.id}/details`} isSelected={selected.some((row) => row.id === host.id)} onSelect={() => handleSelect(host)} diff --git a/awx/ui/src/screens/Host/HostList/HostListItem.js b/awx/ui/src/screens/Host/HostList/HostListItem.js index f861a426cc..87f0bff39d 100644 --- a/awx/ui/src/screens/Host/HostList/HostListItem.js +++ b/awx/ui/src/screens/Host/HostList/HostListItem.js @@ -1,5 +1,5 @@ import 'styled-components/macro'; -import React, { useState } from 'react'; +import React from 'react'; import { string, bool, func } from 'prop-types'; import { t } from '@lingui/macro'; @@ -13,9 +13,16 @@ import HostToggle from 'components/HostToggle'; import { DetailList, Detail } from 'components/DetailList'; import Sparkline from 'components/Sparkline'; -function HostListItem({ host, isSelected, onSelect, detailUrl, rowIndex }) { +function HostListItem({ + host, + isSelected, + onSelect, + detailUrl, + rowIndex, + isExpanded, + onExpand, +}) { const labelId = `check-action-${host.id}`; - const [isExpanded, setIsExpanded] = useState(false); const { summary_fields: { recent_jobs: recentJobs = [] }, @@ -28,7 +35,7 @@ function HostListItem({ host, isSelected, onSelect, detailUrl, rowIndex }) { expand={{ rowIndex, isExpanded, - onToggle: () => setIsExpanded(!isExpanded), + onToggle: onExpand, }} /> ; } else { content = ( - - {questions?.map((question, index) => ( - q.variable === question.variable)} - onSelect={() => handleSelect(question)} - onMoveUp={moveUp} - onMoveDown={moveDown} - canEdit={canEdit} - /> - ))} + <> + + + + + {t`Name`} + {t`Type`} + {t`Default`} + {t`Actions`} + + + + {questions?.map((question, index) => ( + q.variable === question.variable + )} + onSelect={() => handleSelect(question)} + onMoveUp={moveUp} + onMoveDown={moveDown} + canEdit={canEdit} + rowIndex={index} + /> + ))} + + + {isDeleteModalOpen && deleteModal} {isPreviewModalOpen && ( )} - - + ); } diff --git a/awx/ui/src/screens/Template/Survey/SurveyListItem.js b/awx/ui/src/screens/Template/Survey/SurveyListItem.js index 75d15d31ab..818be9eb4d 100644 --- a/awx/ui/src/screens/Template/Survey/SurveyListItem.js +++ b/awx/ui/src/screens/Template/Survey/SurveyListItem.js @@ -5,16 +5,11 @@ import { Link } from 'react-router-dom'; import { Button as _Button, Chip, - DataListAction as _DataListAction, - DataListCell, - DataListCheck, - DataListItemCells, - DataListItemRow, - DataListItem, Stack, StackItem, Tooltip, } from '@patternfly/react-core'; +import { Tr, Td } from '@patternfly/react-table'; import { CaretDownIcon, CaretUpIcon, @@ -22,20 +17,12 @@ import { } from '@patternfly/react-icons'; import styled from 'styled-components'; import ChipGroup from 'components/ChipGroup'; +import { ActionItem, ActionsTd } from 'components/PaginatedTable'; -const DataListAction = styled(_DataListAction)` - && { - margin-left: 0; - margin-right: 20px; - padding-top: 0; - padding-bottom: 0; - } -`; - -const Button = styled(_Button)` +const StackButton = styled(_Button)` padding-top: 0; padding-bottom: 0; - padding-left: 0; + padding-left: 20px; `; const Required = styled.span` @@ -43,12 +30,6 @@ const Required = styled.span` margin-left: var(--pf-global--spacer--xs); `; -const Label = styled.b` - margin-right: 20px; -`; - -const EditSection = styled(_DataListAction)``; - const EditButton = styled(_Button)``; function SurveyListItem({ @@ -60,121 +41,109 @@ function SurveyListItem({ onSelect, onMoveUp, onMoveDown, + rowIndex, }) { return ( - - - - - - - - - - - - - - - <> - - {question.question_name} - - {question.required && ( - - )} - - , - - - {question.type} - , - - - {[question.type].includes('password') && ( - {t`encrypted`.toUpperCase()} - )} - {[question.type].includes('multiselect') && - question.default.length > 0 && ( - - {question.default.split('\n').map((chip) => ( - - {chip} - - ))} - - )} - {![question.type].includes('password') && - ![question.type].includes('multiselect') && ( - {question.default} - )} - , - ]} - /> - - {canEdit && ( - - - - - - - + + + + <> + + {question.question_name} + + {question.required && ( + )} - - - + + + {question.type} + + {[question.type].includes('password') && ( + {t`encrypted`.toUpperCase()} + )} + {[question.type].includes('multiselect') && + question.default.length > 0 && ( + + {question.default.split('\n').map((chip) => ( + + {chip} + + ))} + + )} + {![question.type].includes('password') && + ![question.type].includes('multiselect') && ( + {question.default} + )} + + + + + + + + + + + + + <> + + + onMoveUp(question)} + > + + + + + onMoveDown(question)} + > + + + + + + + + ); } export default SurveyListItem; diff --git a/awx/ui/src/screens/Template/Survey/SurveyListItem.test.js b/awx/ui/src/screens/Template/Survey/SurveyListItem.test.js index 676f332261..0e115a1f6e 100644 --- a/awx/ui/src/screens/Template/Survey/SurveyListItem.test.js +++ b/awx/ui/src/screens/Template/Survey/SurveyListItem.test.js @@ -1,6 +1,9 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; -import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; +import { + mountWithContexts, + shallowWithContexts, +} from '../../../../testUtils/enzymeHelpers'; import SurveyListItem from './SurveyListItem'; describe('', () => { @@ -11,36 +14,57 @@ describe('', () => { type: 'text', id: 1, }; + test('renders successfully', () => { let wrapper; act(() => { - wrapper = mountWithContexts( + wrapper = shallowWithContexts( ); }); expect(wrapper.length).toBe(1); }); + test('fields are rendering properly', () => { let wrapper; act(() => { wrapper = mountWithContexts( - + + + + +
); }); const moveUp = wrapper.find('Button[aria-label="move up"]'); const moveDown = wrapper.find('Button[aria-label="move down"]'); expect(moveUp.length).toBe(1); expect(moveDown.length).toBe(1); - expect(wrapper.find('b').at(0).text()).toBe('Type'); - expect(wrapper.find('b').at(1).text()).toBe('Default'); - expect(wrapper.find('DataListCheck').length).toBe(1); - expect(wrapper.find('DataListCell').length).toBe(3); + expect(wrapper.find('SelectColumn').length).toBe(1); + expect(wrapper.find('Td').length).toBe(5); }); + test('move up and move down buttons are disabled', () => { let wrapper; act(() => { wrapper = mountWithContexts( - + + + + +
); }); const moveUp = wrapper @@ -49,9 +73,11 @@ describe('', () => { const moveDown = wrapper .find('Button[aria-label="move down"]') .prop('isDisabled'); + expect(moveUp).toBe(true); expect(moveDown).toBe(true); }); + test('required item has required asterisk', () => { const newItem = { question_name: 'Foo', @@ -64,7 +90,17 @@ describe('', () => { let wrapper; act(() => { wrapper = mountWithContexts( - + + + + +
); }); expect(wrapper.find('span[aria-label="Required"]').length).toBe(1); @@ -72,12 +108,19 @@ describe('', () => { test('items that are not required should not have an asterisk', () => { let wrapper; act(() => { - wrapper = mountWithContexts( - + wrapper = shallowWithContexts( + ); }); expect(wrapper.find('span[aria-label="Required"]').length).toBe(0); }); + test('required item has required asterisk', () => { const newItem = { question_name: 'Foo', @@ -89,7 +132,17 @@ describe('', () => { let wrapper; act(() => { wrapper = mountWithContexts( - + + + + +
); }); expect(wrapper.find('Chip').length).toBe(6); @@ -98,6 +151,7 @@ describe('', () => { .filter((chip) => chip.prop('isOverFlowChip') !== true) .map((chip) => expect(chip.prop('isReadOnly')).toBe(true)); }); + test('items that are no required should have no an asterisk', () => { const newItem = { question_name: 'Foo', @@ -109,24 +163,31 @@ describe('', () => { let wrapper; act(() => { wrapper = mountWithContexts( - + + + + +
); }); expect(wrapper.find('span').text()).toBe('ENCRYPTED'); }); + test('users without edit/delete permissions are unable to reorder the questions', () => { let wrapper; act(() => { - wrapper = mountWithContexts( + wrapper = shallowWithContexts( ); }); - expect(wrapper.find('button[aria-label="move up"]').prop('disabled')).toBe( - true - ); - expect( - wrapper.find('button[aria-label="move down"]').prop('disabled') - ).toBe(true); + expect(wrapper.find('button[aria-label="move up"]')).toHaveLength(0); + expect(wrapper.find('button[aria-label="move down"]')).toHaveLength(0); expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy(); }); @@ -134,9 +195,20 @@ describe('', () => { let wrapper; act(() => { wrapper = mountWithContexts( - + + + + +
); }); + expect(wrapper.find('PencilAltIcon').exists()).toBeTruthy(); expect(wrapper.find('Button[ouiaId="edit-survey-buzz"]').prop('to')).toBe( 'survey/edit?question_variable=buzz' diff --git a/awx/ui/src/screens/Template/Survey/SurveyToolbar.js b/awx/ui/src/screens/Template/Survey/SurveyToolbar.js index d1a829133f..a1a31fb5fe 100644 --- a/awx/ui/src/screens/Template/Survey/SurveyToolbar.js +++ b/awx/ui/src/screens/Template/Survey/SurveyToolbar.js @@ -16,14 +16,13 @@ import { import { ToolbarAddButton } from 'components/PaginatedTable'; const Toolbar = styled(_Toolbar)` - margin-left: 52px; + margin-left: 10px; `; function SurveyToolbar({ canEdit, isAllSelected, onSelectAll, - surveyEnabled, onToggleSurvey, isDeleteDisabled,