From 8081d668725b4e8b77ba056d83ceb2ee72ecf3cd Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Thu, 21 Jan 2021 15:33:08 -0800 Subject: [PATCH] convert credentials list to table --- .../components/PaginatedTable/HeaderRow.jsx | 3 + .../CredentialList/CredentialList.jsx | 18 ++- .../CredentialList/CredentialList.test.jsx | 34 ++++- .../CredentialList/CredentialListItem.jsx | 123 +++++++----------- .../CredentialListItem.test.jsx | 80 +++++++----- 5 files changed, 142 insertions(+), 116 deletions(-) diff --git a/awx/ui_next/src/components/PaginatedTable/HeaderRow.jsx b/awx/ui_next/src/components/PaginatedTable/HeaderRow.jsx index 5ea13ee6b3..cec9a984ef 100644 --- a/awx/ui_next/src/components/PaginatedTable/HeaderRow.jsx +++ b/awx/ui_next/src/components/PaginatedTable/HeaderRow.jsx @@ -1,3 +1,4 @@ +import 'styled-components/macro'; import React from 'react'; import { useLocation, useHistory } from 'react-router-dom'; import { Thead, Tr, Th as PFTh } from '@patternfly/react-table'; @@ -72,6 +73,7 @@ export function HeaderCell({ columnIndex, idPrefix, className, + alignRight, children, }) { const sort = sortKey @@ -86,6 +88,7 @@ export function HeaderCell({ id={sortKey ? `${idPrefix}-${sortKey}` : null} className={className} sort={sort} + css={alignRight ? 'text-align: right' : null} > {children} diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx index e4a10ed86c..ced36412f5 100644 --- a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx @@ -7,10 +7,14 @@ import { CredentialsAPI } from '../../../api'; import AlertModal from '../../../components/AlertModal'; import ErrorDetail from '../../../components/ErrorDetail'; import DataListToolbar from '../../../components/DataListToolbar'; -import PaginatedDataList, { +import { ToolbarAddButton, ToolbarDeleteButton, } from '../../../components/PaginatedDataList'; +import PaginatedTable, { + HeaderRow, + HeaderCell, +} from '../../../components/PaginatedTable'; import useRequest, { useDeleteItems } from '../../../util/useRequest'; import { getQSConfig, parseQueryString } from '../../../util/qs'; import CredentialListItem from './CredentialListItem'; @@ -114,7 +118,7 @@ function CredentialList({ i18n }) { return ( - ( + headerRow={ + + {i18n._(t`Name`)} + {i18n._(t`Type`)} + {i18n._(t`Actions`)} + + } + renderRow={(item, index) => ( row.id === item.id)} onSelect={() => handleSelect(item)} + rowIndex={index} /> )} renderToolbar={props => ( diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.test.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.test.jsx index 88c52ff228..35bcce1d64 100644 --- a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.test.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.test.jsx @@ -57,25 +57,41 @@ describe('', () => { test('should check and uncheck the row item', async () => { expect( - wrapper.find('DataListCheck[id="select-credential-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(false); await act(async () => { wrapper - .find('DataListCheck[id="select-credential-1"]') + .find('.pf-c-table__check') + .first() + .find('input') .invoke('onChange')(true); }); wrapper.update(); expect( - wrapper.find('DataListCheck[id="select-credential-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(true); await act(async () => { wrapper - .find('DataListCheck[id="select-credential-1"]') + .find('.pf-c-table__check') + .first() + .find('input') .invoke('onChange')(false); }); wrapper.update(); expect( - wrapper.find('DataListCheck[id="select-credential-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(false); }); @@ -105,7 +121,9 @@ describe('', () => { await act(async () => { wrapper - .find('DataListCheck[id="select-credential-3"]') + .find('.pf-c-table__check') + .at(2) + .find('input') .invoke('onChange')(); }); wrapper.update(); @@ -122,7 +140,9 @@ describe('', () => { ); await act(async () => { wrapper - .find('DataListCheck[id="select-credential-2"]') + .find('.pf-c-table__check') + .at(1) + .find('input') .invoke('onChange')(); }); wrapper.update(); diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx index a4e436c073..9886cf3572 100644 --- a/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx @@ -3,31 +3,16 @@ import { string, bool, func } from 'prop-types'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Link } from 'react-router-dom'; -import { - Button, - DataListAction as _DataListAction, - DataListCheck, - DataListItem, - DataListItemRow, - DataListItemCells, - Tooltip, -} from '@patternfly/react-core'; +import { Button } from '@patternfly/react-core'; +import { Tr, Td } from '@patternfly/react-table'; import { PencilAltIcon } from '@patternfly/react-icons'; -import styled from 'styled-components'; -import DataListCell from '../../../components/DataListCell'; +import { ActionsTd, ActionItem } from '../../../components/PaginatedTable'; import { timeOfDay } from '../../../util/dates'; import { Credential } from '../../../types'; import { CredentialsAPI } from '../../../api'; import CopyButton from '../../../components/CopyButton'; -const DataListAction = styled(_DataListAction)` - align-items: center; - display: grid; - grid-gap: 16px; - grid-template-columns: repeat(2, 40px); -`; - function CredentialListItem({ credential, detailUrl, @@ -35,6 +20,7 @@ function CredentialListItem({ onSelect, i18n, fetchCredentials, + rowIndex, }) { const [isDisabled, setIsDisabled] = useState(false); @@ -57,64 +43,49 @@ function CredentialListItem({ }, []); return ( - - - - - - {credential.name} - - , - - {credential.summary_fields.credential_type.name} - , - ]} - /> - - {canEdit && ( - - - - )} - {credential.summary_fields.user_capabilities.copy && ( - - )} - - - + + + + + {credential.name} + + + + {credential.summary_fields.credential_type.name} + + + + + + + + + + ); } diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.test.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.test.jsx index fcfc085d2d..74bb7e6915 100644 --- a/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.test.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.test.jsx @@ -16,24 +16,32 @@ describe('', () => { test('edit button shown to users with edit capabilities', () => { wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
); expect(wrapper.find('PencilAltIcon').exists()).toBeTruthy(); }); test('edit button hidden from users without edit capabilities', () => { wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
); expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy(); }); @@ -41,12 +49,16 @@ describe('', () => { CredentialsAPI.copy.mockResolvedValue(); wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
); await act(async () => @@ -60,12 +72,16 @@ describe('', () => { CredentialsAPI.copy.mockRejectedValue(new Error()); wrapper = mountWithContexts( - {}} - credential={mockCredentials.results[0]} - /> + + + {}} + credential={mockCredentials.results[0]} + /> + +
); await act(async () => wrapper.find('Button[aria-label="Copy"]').prop('onClick')() @@ -77,12 +93,16 @@ describe('', () => { test('should not render copy button', async () => { wrapper = mountWithContexts( - {}} - credential={mockCredentials.results[1]} - /> + + + {}} + credential={mockCredentials.results[1]} + /> + +
); expect(wrapper.find('CopyButton').length).toBe(0); });