diff --git a/awx/ui_next/src/screens/User/UserList/UserList.jsx b/awx/ui_next/src/screens/User/UserList/UserList.jsx index 06c1d3ae1c..28530d45fe 100644 --- a/awx/ui_next/src/screens/User/UserList/UserList.jsx +++ b/awx/ui_next/src/screens/User/UserList/UserList.jsx @@ -7,7 +7,11 @@ import { UsersAPI } from '../../../api'; import AlertModal from '../../../components/AlertModal'; import DataListToolbar from '../../../components/DataListToolbar'; import ErrorDetail from '../../../components/ErrorDetail'; -import PaginatedDataList, { +import PaginatedTable, { + HeaderRow, + HeaderCell, +} from '../../../components/PaginatedTable'; +import { ToolbarAddButton, ToolbarDeleteButton, } from '../../../components/PaginatedDataList'; @@ -101,7 +105,7 @@ function UserList({ i18n }) { <> - ( @@ -167,13 +157,28 @@ function UserList({ i18n }) { ]} /> )} - renderItem={o => ( + headerRow={ + + + {i18n._(t`Username`)} + + + {i18n._(t`First Name`)} + + + {i18n._(t`Last Name`)} + + {i18n._(t`Role`)} + + } + renderRow={(user, index) => ( row.id === o.id)} - onSelect={() => handleSelect(o)} + key={user.id} + user={user} + detailUrl={`${match.url}/${user.id}/details`} + isSelected={selected.some(row => row.id === user.id)} + onSelect={() => handleSelect(user)} + rowIndex={index} /> )} emptyStateControls={ diff --git a/awx/ui_next/src/screens/User/UserList/UserList.test.jsx b/awx/ui_next/src/screens/User/UserList/UserList.test.jsx index 46cf02c128..21efc99d9c 100644 --- a/awx/ui_next/src/screens/User/UserList/UserList.test.jsx +++ b/awx/ui_next/src/screens/User/UserList/UserList.test.jsx @@ -129,51 +129,66 @@ describe('UsersList with full permissions', () => { test('should check and uncheck the row item', async () => { expect( - wrapper.find('DataListCheck[id="select-user-1"]').props().checked + wrapper + .find('.pf-c-table__check input') + .first() + .props().checked ).toBe(false); await act(async () => { - wrapper.find('DataListCheck[id="select-user-1"]').invoke('onChange')( - true - ); + wrapper + .find('.pf-c-table__check input') + .first() + .invoke('onChange')(true); }); wrapper.update(); expect( - wrapper.find('DataListCheck[id="select-user-1"]').props().checked + wrapper + .find('.pf-c-table__check input') + .first() + .props().checked ).toBe(true); await act(async () => { - wrapper.find('DataListCheck[id="select-user-1"]').invoke('onChange')( - false - ); + wrapper + .find('.pf-c-table__check input') + .first() + .invoke('onChange')(false); }); wrapper.update(); expect( - wrapper.find('DataListCheck[id="select-user-1"]').props().checked + wrapper + .find('.pf-c-table__check input') + .first() + .props().checked ).toBe(false); }); test('should check all row items when select all is checked', async () => { - wrapper.find('DataListCheck').forEach(el => { + expect(wrapper.find('.pf-c-table__check input')).toHaveLength(2); + wrapper.find('.pf-c-table__check input').forEach(el => { expect(el.props().checked).toBe(false); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(true); }); wrapper.update(); - wrapper.find('DataListCheck').forEach(el => { + wrapper.find('.pf-c-table__check input').forEach(el => { expect(el.props().checked).toBe(true); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(false); }); wrapper.update(); - wrapper.find('DataListCheck').forEach(el => { + wrapper.find('.pf-c-table__check input').forEach(el => { expect(el.props().checked).toBe(false); }); }); test('should call api delete users for each selected user', async () => { await act(async () => { - wrapper.find('DataListCheck[id="select-user-1"]').invoke('onChange')(); + wrapper + .find('.pf-c-table__check input') + .first() + .invoke('onChange')(); }); wrapper.update(); await act(async () => { @@ -185,10 +200,12 @@ describe('UsersList with full permissions', () => { test('should show error modal when user is not successfully deleted from api', async () => { UsersAPI.destroy.mockImplementationOnce(() => Promise.reject(new Error())); - // expect(wrapper.debug()).toBe(false); expect(wrapper.find('Modal').length).toBe(0); await act(async () => { - wrapper.find('DataListCheck[id="select-user-1"]').invoke('onChange')(); + wrapper + .find('.pf-c-table__check input') + .first() + .invoke('onChange')(); }); wrapper.update(); await act(async () => { diff --git a/awx/ui_next/src/screens/User/UserList/UserListItem.jsx b/awx/ui_next/src/screens/User/UserList/UserListItem.jsx index c479313e71..ba4cc016fe 100644 --- a/awx/ui_next/src/screens/User/UserList/UserListItem.jsx +++ b/awx/ui_next/src/screens/User/UserList/UserListItem.jsx @@ -3,24 +3,22 @@ import React, { Fragment } from 'react'; import { string, bool, func } from 'prop-types'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { - Button, - DataListAction, - DataListCheck, - DataListItem, - DataListItemCells, - DataListItemRow, - Label, - Tooltip, -} from '@patternfly/react-core'; - +import { Button, Label } from '@patternfly/react-core'; +import { Tr, Td } from '@patternfly/react-table'; import { Link } from 'react-router-dom'; import { PencilAltIcon } from '@patternfly/react-icons'; -import DataListCell from '../../../components/DataListCell'; +import { ActionsTd, ActionItem } from '../../../components/PaginatedTable'; import { User } from '../../../types'; -function UserListItem({ user, isSelected, onSelect, detailUrl, i18n }) { +function UserListItem({ + user, + isSelected, + onSelect, + detailUrl, + rowIndex, + i18n, +}) { const labelId = `check-action-${user.id}`; let user_type; @@ -36,84 +34,64 @@ function UserListItem({ user, isSelected, onSelect, detailUrl, i18n }) { const socialAuthUser = user.auth.length > 0; return ( - - - - - - - {user.username} - - - {ldapUser && ( - - - - )} - {socialAuthUser && ( - - - - )} - , - - {user.first_name && ( - - {i18n._(t`First Name`)} - {user.first_name} - - )} - , - - {user.last_name && ( - - {i18n._(t`Last Name`)} - {user.last_name} - - )} - , - - {user_type} - , - ]} - /> - + + + + {user.username} + + {ldapUser && ( + + + + )} + {socialAuthUser && ( + + + + )} + + + {user.first_name && ( + + {i18n._(t`First Name`)} + {user.first_name} + + )} + + + {user.last_name && ( + + {i18n._(t`Last Name`)} + {user.last_name} + + )} + + {user_type} + + - {user.summary_fields.user_capabilities.edit && ( - - - - )} - - - + + + + ); } diff --git a/awx/ui_next/src/screens/User/UserList/UserListItem.test.jsx b/awx/ui_next/src/screens/User/UserList/UserListItem.test.jsx index fd14bdc09b..062c8b6c0f 100644 --- a/awx/ui_next/src/screens/User/UserList/UserListItem.test.jsx +++ b/awx/ui_next/src/screens/User/UserList/UserListItem.test.jsx @@ -18,12 +18,16 @@ describe('UserListItem with full permissions', () => { wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
); @@ -36,9 +40,9 @@ describe('UserListItem with full permissions', () => { }); test('should display user data', () => { - expect( - wrapper.find('DataListCell[aria-label="user type"]').prop('children') - ).toEqual('System Administrator'); + expect(wrapper.find('td[data-label="Role"]').prop('children')).toEqual( + 'System Administrator' + ); expect( wrapper.find('Label[aria-label="social login"]').prop('children') ).toEqual('SOCIAL'); @@ -50,19 +54,23 @@ describe('UserListItem without full permissions', () => { wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
);