mirror of
https://github.com/ansible/awx.git
synced 2026-01-23 07:28:02 -03:30
Merge pull request #9196 from keithjgrant/6189-admin-list-tables
Convert admin pages lists to tables Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
commit
060578b30b
@ -6,7 +6,7 @@ import { t } from '@lingui/macro';
|
||||
import AlertModal from '../AlertModal';
|
||||
import ErrorDetail from '../ErrorDetail';
|
||||
import NotificationListItem from './NotificationListItem';
|
||||
import PaginatedDataList from '../PaginatedDataList';
|
||||
import PaginatedTable, { HeaderRow, HeaderCell } from '../PaginatedTable';
|
||||
import { getQSConfig, parseQueryString } from '../../util/qs';
|
||||
import useRequest from '../../util/useRequest';
|
||||
import { NotificationTemplatesAPI } from '../../api';
|
||||
@ -169,7 +169,7 @@ function NotificationList({
|
||||
|
||||
return (
|
||||
<>
|
||||
<PaginatedDataList
|
||||
<PaginatedTable
|
||||
contentError={contentError}
|
||||
hasContentLoading={isLoading}
|
||||
items={notifications}
|
||||
@ -211,19 +211,18 @@ function NotificationList({
|
||||
key: 'modified_by__username__icontains',
|
||||
},
|
||||
]}
|
||||
toolbarSortColumns={[
|
||||
{
|
||||
name: i18n._(t`Name`),
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Type`),
|
||||
key: 'notification_type',
|
||||
},
|
||||
]}
|
||||
toolbarSearchableKeys={searchableKeys}
|
||||
toolbarRelatedSearchableKeys={relatedSearchableKeys}
|
||||
renderItem={notification => (
|
||||
headerRow={
|
||||
<HeaderRow qsConfig={QS_CONFIG} isSelectable={false}>
|
||||
<HeaderCell sortKey="name">{i18n._(t`Name`)}</HeaderCell>
|
||||
<HeaderCell sortKey="notification_type">
|
||||
{i18n._(t`Type`)}
|
||||
</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Options`)}</HeaderCell>
|
||||
</HeaderRow>
|
||||
}
|
||||
renderRow={(notification, index) => (
|
||||
<NotificationListItem
|
||||
key={notification.id}
|
||||
notification={notification}
|
||||
@ -239,6 +238,7 @@ function NotificationList({
|
||||
successTurnedOn={successTemplateIds.includes(notification.id)}
|
||||
typeLabels={typeLabels}
|
||||
showApprovalsToggle={showApprovalsToggle}
|
||||
rowIndex={index}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
@ -87,10 +87,6 @@ describe('<NotificationList />', () => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('initially renders succesfully', () => {
|
||||
expect(wrapper.find('PaginatedDataList')).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('should render list fetched of items', () => {
|
||||
expect(NotificationTemplatesAPI.read).toHaveBeenCalled();
|
||||
expect(NotificationTemplatesAPI.readOptions).toHaveBeenCalled();
|
||||
|
||||
@ -3,25 +3,9 @@ import { shape, number, string, bool, func } from 'prop-types';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
DataListAction as _DataListAction,
|
||||
DataListItem,
|
||||
DataListItemCells,
|
||||
DataListItemRow,
|
||||
Switch,
|
||||
} from '@patternfly/react-core';
|
||||
import styled from 'styled-components';
|
||||
import DataListCell from '../DataListCell';
|
||||
|
||||
const DataListAction = styled(_DataListAction)`
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: ${props => `repeat(${props.columns}, max-content)`};
|
||||
`;
|
||||
const Label = styled.b`
|
||||
margin-right: 20px;
|
||||
`;
|
||||
import { Switch } from '@patternfly/react-core';
|
||||
import { Tr, Td } from '@patternfly/react-table';
|
||||
import { ActionsTd, ActionItem } from '../PaginatedTable';
|
||||
|
||||
function NotificationListItem({
|
||||
canToggleNotifications,
|
||||
@ -37,54 +21,37 @@ function NotificationListItem({
|
||||
showApprovalsToggle,
|
||||
}) {
|
||||
return (
|
||||
<DataListItem
|
||||
aria-labelledby={`items-list-item-${notification.id}`}
|
||||
key={notification.id}
|
||||
id={`${notification.id}`}
|
||||
>
|
||||
<DataListItemRow>
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell key="name">
|
||||
<Link
|
||||
to={{
|
||||
pathname: detailUrl,
|
||||
}}
|
||||
>
|
||||
<b id={`items-list-item-${notification.id}`}>
|
||||
{notification.name}
|
||||
</b>
|
||||
</Link>
|
||||
</DataListCell>,
|
||||
<DataListCell key="type">
|
||||
<Label>{i18n._(t`Type `)}</Label>
|
||||
{typeLabels[notification.notification_type]}
|
||||
</DataListCell>,
|
||||
]}
|
||||
/>
|
||||
<DataListAction
|
||||
aria-label={i18n._(t`actions`)}
|
||||
aria-labelledby={`items-list-item-${notification.id}`}
|
||||
id={`items-list-item-${notification.id}`}
|
||||
columns={showApprovalsToggle ? 4 : 3}
|
||||
>
|
||||
{showApprovalsToggle && (
|
||||
<Switch
|
||||
id={`notification-${notification.id}-approvals-toggle`}
|
||||
label={i18n._(t`Approval`)}
|
||||
labelOff={i18n._(t`Approval`)}
|
||||
isChecked={approvalsTurnedOn}
|
||||
isDisabled={!canToggleNotifications}
|
||||
onChange={() =>
|
||||
toggleNotification(
|
||||
notification.id,
|
||||
approvalsTurnedOn,
|
||||
'approvals'
|
||||
)
|
||||
}
|
||||
aria-label={i18n._(t`Toggle notification approvals`)}
|
||||
/>
|
||||
)}
|
||||
<Tr id={`notification-row-${notification.id}`}>
|
||||
<Td id={`notification-${notification.id}`} dataLabel={i18n._(t`Name`)}>
|
||||
<Link to={`${detailUrl}`}>
|
||||
<b>{notification.name}</b>
|
||||
</Link>
|
||||
</Td>
|
||||
<Td dataLabel={i18n._(t`Type`)}>
|
||||
{typeLabels[notification.notification_type]}
|
||||
</Td>
|
||||
<ActionsTd
|
||||
dataLabel={i18n._(t`Options`)}
|
||||
gridColumns="120px 120px 120px 120px"
|
||||
>
|
||||
<ActionItem visible={showApprovalsToggle}>
|
||||
<Switch
|
||||
id={`notification-${notification.id}-approvals-toggle`}
|
||||
label={i18n._(t`Approval`)}
|
||||
labelOff={i18n._(t`Approval`)}
|
||||
isChecked={approvalsTurnedOn}
|
||||
isDisabled={!canToggleNotifications}
|
||||
onChange={() =>
|
||||
toggleNotification(
|
||||
notification.id,
|
||||
approvalsTurnedOn,
|
||||
'approvals'
|
||||
)
|
||||
}
|
||||
aria-label={i18n._(t`Toggle notification approvals`)}
|
||||
/>
|
||||
</ActionItem>
|
||||
<ActionItem visible>
|
||||
<Switch
|
||||
id={`notification-${notification.id}-started-toggle`}
|
||||
label={i18n._(t`Start`)}
|
||||
@ -96,6 +63,8 @@ function NotificationListItem({
|
||||
}
|
||||
aria-label={i18n._(t`Toggle notification start`)}
|
||||
/>
|
||||
</ActionItem>
|
||||
<ActionItem visible>
|
||||
<Switch
|
||||
id={`notification-${notification.id}-success-toggle`}
|
||||
label={i18n._(t`Success`)}
|
||||
@ -107,6 +76,8 @@ function NotificationListItem({
|
||||
}
|
||||
aria-label={i18n._(t`Toggle notification success`)}
|
||||
/>
|
||||
</ActionItem>
|
||||
<ActionItem visible>
|
||||
<Switch
|
||||
id={`notification-${notification.id}-error-toggle`}
|
||||
label={i18n._(t`Failure`)}
|
||||
@ -118,9 +89,9 @@ function NotificationListItem({
|
||||
}
|
||||
aria-label={i18n._(t`Toggle notification failure`)}
|
||||
/>
|
||||
</DataListAction>
|
||||
</DataListItemRow>
|
||||
</DataListItem>
|
||||
</ActionItem>
|
||||
</ActionsTd>
|
||||
</Tr>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -30,13 +30,17 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
|
||||
test('initially renders succesfully and displays correct label', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
expect(wrapper.find('NotificationListItem')).toMatchSnapshot();
|
||||
expect(wrapper.find('Switch').length).toBe(3);
|
||||
@ -44,46 +48,55 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
|
||||
test('shows approvals toggle when configured', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
showApprovalsToggle
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
showApprovalsToggle
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
expect(wrapper.find('Switch').length).toBe(4);
|
||||
});
|
||||
|
||||
test('displays correct label in correct column', () => {
|
||||
test('displays correct type', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
const typeCell = wrapper
|
||||
.find('DataListCell')
|
||||
.at(1)
|
||||
.find('div');
|
||||
const typeCell = wrapper.find('Td').at(1);
|
||||
expect(typeCell.text()).toContain('Slack');
|
||||
});
|
||||
|
||||
test('handles approvals click when toggle is on', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
approvalsTurnedOn
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
showApprovalsToggle
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
approvalsTurnedOn
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
showApprovalsToggle
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
wrapper
|
||||
.find('Switch[aria-label="Toggle notification approvals"]')
|
||||
@ -95,15 +108,19 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
|
||||
test('handles approvals click when toggle is off', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
approvalsTurnedOn={false}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
showApprovalsToggle
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
approvalsTurnedOn={false}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
showApprovalsToggle
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
wrapper
|
||||
.find('Switch[aria-label="Toggle notification approvals"]')
|
||||
@ -114,14 +131,18 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
|
||||
test('handles started click when toggle is on', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
startedTurnedOn
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
startedTurnedOn
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
wrapper
|
||||
.find('Switch[aria-label="Toggle notification start"]')
|
||||
@ -132,14 +153,18 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
|
||||
test('handles started click when toggle is off', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
startedTurnedOn={false}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
startedTurnedOn={false}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
wrapper
|
||||
.find('Switch[aria-label="Toggle notification start"]')
|
||||
@ -150,14 +175,18 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
|
||||
test('handles success click when toggle is on', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
successTurnedOn
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
successTurnedOn
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
wrapper
|
||||
.find('Switch[aria-label="Toggle notification success"]')
|
||||
@ -168,14 +197,18 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
|
||||
test('handles success click when toggle is off', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
successTurnedOn={false}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
successTurnedOn={false}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
wrapper
|
||||
.find('Switch[aria-label="Toggle notification success"]')
|
||||
@ -186,14 +219,18 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
|
||||
test('handles error click when toggle is on', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
errorTurnedOn
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
errorTurnedOn
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
wrapper
|
||||
.find('Switch[aria-label="Toggle notification failure"]')
|
||||
@ -204,14 +241,18 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
|
||||
test('handles error click when toggle is off', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
errorTurnedOn={false}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
errorTurnedOn={false}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
wrapper
|
||||
.find('Switch[aria-label="Toggle notification failure"]')
|
||||
|
||||
@ -24,398 +24,442 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
}
|
||||
}
|
||||
>
|
||||
<DataListItem
|
||||
aria-labelledby="items-list-item-9000"
|
||||
className=""
|
||||
id="9000"
|
||||
isExpanded={false}
|
||||
key="9000"
|
||||
<Tr
|
||||
id="notification-row-9000"
|
||||
>
|
||||
<li
|
||||
aria-labelledby="items-list-item-9000"
|
||||
className="pf-c-data-list__item"
|
||||
id="9000"
|
||||
<TrBase
|
||||
id="notification-row-9000"
|
||||
innerRef={null}
|
||||
>
|
||||
<DataListItemRow
|
||||
key=".0"
|
||||
rowid="items-list-item-9000"
|
||||
<tr
|
||||
className=""
|
||||
data-ouia-component-id="OUIA-Generated-TableRow-1"
|
||||
data-ouia-component-type="PF4/TableRow"
|
||||
data-ouia-safe={true}
|
||||
hidden={false}
|
||||
id="notification-row-9000"
|
||||
>
|
||||
<div
|
||||
className="pf-c-data-list__item-row"
|
||||
<Td
|
||||
dataLabel="Name"
|
||||
id="notification-9000"
|
||||
>
|
||||
<DataListItemCells
|
||||
dataListCells={
|
||||
Array [
|
||||
<ForwardRef(Styled(PFDataListCell))>
|
||||
<ForwardRef
|
||||
to={
|
||||
Object {
|
||||
"pathname": "/foo",
|
||||
}
|
||||
}
|
||||
<TdBase
|
||||
dataLabel="Name"
|
||||
id="notification-9000"
|
||||
innerRef={null}
|
||||
>
|
||||
<td
|
||||
className=""
|
||||
data-label="Name"
|
||||
id="notification-9000"
|
||||
>
|
||||
<Link
|
||||
to="/foo"
|
||||
>
|
||||
<LinkAnchor
|
||||
href="/foo"
|
||||
navigate={[Function]}
|
||||
>
|
||||
<a
|
||||
href="/foo"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<b
|
||||
id="items-list-item-9000"
|
||||
>
|
||||
<b>
|
||||
Foo
|
||||
</b>
|
||||
</ForwardRef>
|
||||
</ForwardRef(Styled(PFDataListCell))>,
|
||||
<ForwardRef(Styled(PFDataListCell))>
|
||||
<ForwardRef(styled.b)>
|
||||
Type
|
||||
</ForwardRef(styled.b)>
|
||||
Slack
|
||||
</ForwardRef(Styled(PFDataListCell))>,
|
||||
]
|
||||
}
|
||||
key=".0"
|
||||
rowid="items-list-item-9000"
|
||||
</a>
|
||||
</LinkAnchor>
|
||||
</Link>
|
||||
</td>
|
||||
</TdBase>
|
||||
</Td>
|
||||
<Td
|
||||
dataLabel="Type"
|
||||
>
|
||||
<TdBase
|
||||
dataLabel="Type"
|
||||
innerRef={null}
|
||||
>
|
||||
<div
|
||||
className="pf-c-data-list__item-content"
|
||||
<td
|
||||
className=""
|
||||
data-label="Type"
|
||||
>
|
||||
<DataListCell
|
||||
key="name"
|
||||
>
|
||||
<StyledComponent
|
||||
forwardedComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "sc-bdVaJa",
|
||||
"isStatic": false,
|
||||
"lastClassName": "kruorc",
|
||||
"rules": Array [
|
||||
"
|
||||
word-break: break-word;
|
||||
",
|
||||
],
|
||||
},
|
||||
"displayName": "DataListCell",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "sc-bdVaJa",
|
||||
"target": [Function],
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
"withComponent": [Function],
|
||||
}
|
||||
}
|
||||
forwardedRef={null}
|
||||
>
|
||||
<PFDataListCell
|
||||
className="sc-bdVaJa kruorc"
|
||||
>
|
||||
<div
|
||||
className="pf-c-data-list__cell sc-bdVaJa kruorc"
|
||||
>
|
||||
<Link
|
||||
to={
|
||||
Object {
|
||||
"pathname": "/foo",
|
||||
}
|
||||
}
|
||||
>
|
||||
<LinkAnchor
|
||||
href="/foo"
|
||||
navigate={[Function]}
|
||||
>
|
||||
<a
|
||||
href="/foo"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<b
|
||||
id="items-list-item-9000"
|
||||
>
|
||||
Foo
|
||||
</b>
|
||||
</a>
|
||||
</LinkAnchor>
|
||||
</Link>
|
||||
</div>
|
||||
</PFDataListCell>
|
||||
</StyledComponent>
|
||||
</DataListCell>
|
||||
<DataListCell
|
||||
key="type"
|
||||
>
|
||||
<StyledComponent
|
||||
forwardedComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "sc-bdVaJa",
|
||||
"isStatic": false,
|
||||
"lastClassName": "kruorc",
|
||||
"rules": Array [
|
||||
"
|
||||
word-break: break-word;
|
||||
",
|
||||
],
|
||||
},
|
||||
"displayName": "DataListCell",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "sc-bdVaJa",
|
||||
"target": [Function],
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
"withComponent": [Function],
|
||||
}
|
||||
}
|
||||
forwardedRef={null}
|
||||
>
|
||||
<PFDataListCell
|
||||
className="sc-bdVaJa kruorc"
|
||||
>
|
||||
<div
|
||||
className="pf-c-data-list__cell sc-bdVaJa kruorc"
|
||||
>
|
||||
<styled.b>
|
||||
<StyledComponent
|
||||
forwardedComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "sc-htpNat",
|
||||
"isStatic": false,
|
||||
"lastClassName": "jyYvCB",
|
||||
"rules": Array [
|
||||
"
|
||||
margin-right: 20px;
|
||||
",
|
||||
],
|
||||
},
|
||||
"displayName": "styled.b",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "sc-htpNat",
|
||||
"target": "b",
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
"withComponent": [Function],
|
||||
}
|
||||
}
|
||||
forwardedRef={null}
|
||||
>
|
||||
<b
|
||||
className="sc-htpNat jyYvCB"
|
||||
>
|
||||
Type
|
||||
</b>
|
||||
</StyledComponent>
|
||||
</styled.b>
|
||||
Slack
|
||||
</div>
|
||||
</PFDataListCell>
|
||||
</StyledComponent>
|
||||
</DataListCell>
|
||||
</div>
|
||||
</DataListItemCells>
|
||||
<Styled(DataListAction)
|
||||
aria-label="actions"
|
||||
aria-labelledby="items-list-item-9000"
|
||||
columns={3}
|
||||
id="items-list-item-9000"
|
||||
key=".1"
|
||||
rowid="items-list-item-9000"
|
||||
Slack
|
||||
</td>
|
||||
</TdBase>
|
||||
</Td>
|
||||
<ActionsTd
|
||||
dataLabel="Options"
|
||||
gridColumns="120px 120px 120px 120px"
|
||||
>
|
||||
<ActionsTd___StyledTd
|
||||
_css={160}
|
||||
dataLabel="Options"
|
||||
>
|
||||
<StyledComponent
|
||||
aria-label="actions"
|
||||
aria-labelledby="items-list-item-9000"
|
||||
columns={3}
|
||||
_css={160}
|
||||
dataLabel="Options"
|
||||
forwardedComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "sc-bwzfXH",
|
||||
"componentId": "ActionsTd___StyledTd-sc-1ys3lw-1",
|
||||
"isStatic": false,
|
||||
"lastClassName": "llKtln",
|
||||
"lastClassName": "dIDjZI",
|
||||
"rules": Array [
|
||||
"
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: ",
|
||||
"text-align:right;--pf-c-table--cell--Width:",
|
||||
[Function],
|
||||
";
|
||||
",
|
||||
"px;",
|
||||
],
|
||||
},
|
||||
"displayName": "Styled(DataListAction)",
|
||||
"displayName": "ActionsTd___StyledTd",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "sc-bwzfXH",
|
||||
"target": [Function],
|
||||
"styledComponentId": "ActionsTd___StyledTd-sc-1ys3lw-1",
|
||||
"target": Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"displayName": "Td",
|
||||
"render": [Function],
|
||||
},
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
"withComponent": [Function],
|
||||
}
|
||||
}
|
||||
forwardedRef={null}
|
||||
id="items-list-item-9000"
|
||||
rowid="items-list-item-9000"
|
||||
>
|
||||
<DataListAction
|
||||
aria-label="actions"
|
||||
aria-labelledby="items-list-item-9000"
|
||||
className="sc-bwzfXH llKtln"
|
||||
columns={3}
|
||||
id="items-list-item-9000"
|
||||
rowid="items-list-item-9000"
|
||||
<Td
|
||||
_css={160}
|
||||
className="ActionsTd___StyledTd-sc-1ys3lw-1 dIDjZI"
|
||||
dataLabel="Options"
|
||||
>
|
||||
<div
|
||||
className="pf-c-data-list__item-action sc-bwzfXH llKtln"
|
||||
columns={3}
|
||||
rowid="items-list-item-9000"
|
||||
<TdBase
|
||||
_css={160}
|
||||
className="ActionsTd___StyledTd-sc-1ys3lw-1 dIDjZI"
|
||||
dataLabel="Options"
|
||||
innerRef={null}
|
||||
>
|
||||
<Switch
|
||||
aria-label="Toggle notification start"
|
||||
id="notification-9000-started-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
label="Start"
|
||||
labelOff="Start"
|
||||
onChange={[Function]}
|
||||
<td
|
||||
_css={160}
|
||||
className="ActionsTd___StyledTd-sc-1ys3lw-1 dIDjZI"
|
||||
data-label="Options"
|
||||
>
|
||||
<label
|
||||
className="pf-c-switch"
|
||||
data-ouia-component-id="OUIA-Generated-Switch-1"
|
||||
data-ouia-component-type="PF4/Switch"
|
||||
data-ouia-safe={true}
|
||||
htmlFor="notification-9000-started-toggle"
|
||||
<ActionsGrid
|
||||
gridColumns="120px 120px 120px 120px"
|
||||
numActions={4}
|
||||
>
|
||||
<input
|
||||
aria-label="Toggle notification start"
|
||||
aria-labelledby={null}
|
||||
checked={false}
|
||||
className="pf-c-switch__input"
|
||||
disabled={false}
|
||||
id="notification-9000-started-toggle"
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
className="pf-c-switch__toggle"
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-on"
|
||||
id={null}
|
||||
<StyledComponent
|
||||
forwardedComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "ActionsTd__ActionsGrid-sc-1ys3lw-0",
|
||||
"isStatic": false,
|
||||
"lastClassName": "dkSSIN",
|
||||
"rules": Array [
|
||||
"display:grid;grid-gap:16px;align-items:center;",
|
||||
[Function],
|
||||
],
|
||||
},
|
||||
"displayName": "ActionsGrid",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "ActionsTd__ActionsGrid-sc-1ys3lw-0",
|
||||
"target": "div",
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
"withComponent": [Function],
|
||||
}
|
||||
}
|
||||
forwardedRef={null}
|
||||
gridColumns="120px 120px 120px 120px"
|
||||
numActions={4}
|
||||
>
|
||||
Start
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-off"
|
||||
id={null}
|
||||
>
|
||||
Start
|
||||
</span>
|
||||
</label>
|
||||
</Switch>
|
||||
<Switch
|
||||
aria-label="Toggle notification success"
|
||||
id="notification-9000-success-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
label="Success"
|
||||
labelOff="Success"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<label
|
||||
className="pf-c-switch"
|
||||
data-ouia-component-id="OUIA-Generated-Switch-2"
|
||||
data-ouia-component-type="PF4/Switch"
|
||||
data-ouia-safe={true}
|
||||
htmlFor="notification-9000-success-toggle"
|
||||
>
|
||||
<input
|
||||
aria-label="Toggle notification success"
|
||||
aria-labelledby={null}
|
||||
checked={false}
|
||||
className="pf-c-switch__input"
|
||||
disabled={false}
|
||||
id="notification-9000-success-toggle"
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
className="pf-c-switch__toggle"
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-on"
|
||||
id={null}
|
||||
>
|
||||
Success
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-off"
|
||||
id={null}
|
||||
>
|
||||
Success
|
||||
</span>
|
||||
</label>
|
||||
</Switch>
|
||||
<Switch
|
||||
aria-label="Toggle notification failure"
|
||||
id="notification-9000-error-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
label="Failure"
|
||||
labelOff="Failure"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<label
|
||||
className="pf-c-switch"
|
||||
data-ouia-component-id="OUIA-Generated-Switch-3"
|
||||
data-ouia-component-type="PF4/Switch"
|
||||
data-ouia-safe={true}
|
||||
htmlFor="notification-9000-error-toggle"
|
||||
>
|
||||
<input
|
||||
aria-label="Toggle notification failure"
|
||||
aria-labelledby={null}
|
||||
checked={false}
|
||||
className="pf-c-switch__input"
|
||||
disabled={false}
|
||||
id="notification-9000-error-toggle"
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
className="pf-c-switch__toggle"
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-on"
|
||||
id={null}
|
||||
>
|
||||
Failure
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-off"
|
||||
id={null}
|
||||
>
|
||||
Failure
|
||||
</span>
|
||||
</label>
|
||||
</Switch>
|
||||
</div>
|
||||
</DataListAction>
|
||||
<div
|
||||
className="ActionsTd__ActionsGrid-sc-1ys3lw-0 dkSSIN"
|
||||
>
|
||||
<ActionItem
|
||||
column={1}
|
||||
key=".0"
|
||||
visible={false}
|
||||
/>
|
||||
<ActionItem
|
||||
column={2}
|
||||
key=".1"
|
||||
visible={true}
|
||||
>
|
||||
<ActionItem___StyledDiv
|
||||
_css={2}
|
||||
>
|
||||
<StyledComponent
|
||||
_css={2}
|
||||
forwardedComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "ActionItem___StyledDiv-sc-1x1z9nz-0",
|
||||
"isStatic": false,
|
||||
"lastClassName": "gydHvj",
|
||||
"rules": Array [
|
||||
"grid-column:",
|
||||
[Function],
|
||||
";",
|
||||
],
|
||||
},
|
||||
"displayName": "ActionItem___StyledDiv",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "ActionItem___StyledDiv-sc-1x1z9nz-0",
|
||||
"target": "div",
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
"withComponent": [Function],
|
||||
}
|
||||
}
|
||||
forwardedRef={null}
|
||||
>
|
||||
<div
|
||||
className="ActionItem___StyledDiv-sc-1x1z9nz-0 cSmxUZ"
|
||||
>
|
||||
<Switch
|
||||
aria-label="Toggle notification start"
|
||||
id="notification-9000-started-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
label="Start"
|
||||
labelOff="Start"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<label
|
||||
className="pf-c-switch"
|
||||
data-ouia-component-id="OUIA-Generated-Switch-1"
|
||||
data-ouia-component-type="PF4/Switch"
|
||||
data-ouia-safe={true}
|
||||
htmlFor="notification-9000-started-toggle"
|
||||
>
|
||||
<input
|
||||
aria-label="Toggle notification start"
|
||||
aria-labelledby={null}
|
||||
checked={false}
|
||||
className="pf-c-switch__input"
|
||||
disabled={false}
|
||||
id="notification-9000-started-toggle"
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
className="pf-c-switch__toggle"
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-on"
|
||||
id={null}
|
||||
>
|
||||
Start
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-off"
|
||||
id={null}
|
||||
>
|
||||
Start
|
||||
</span>
|
||||
</label>
|
||||
</Switch>
|
||||
</div>
|
||||
</StyledComponent>
|
||||
</ActionItem___StyledDiv>
|
||||
</ActionItem>
|
||||
<ActionItem
|
||||
column={3}
|
||||
key=".2"
|
||||
visible={true}
|
||||
>
|
||||
<ActionItem___StyledDiv
|
||||
_css={3}
|
||||
>
|
||||
<StyledComponent
|
||||
_css={3}
|
||||
forwardedComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "ActionItem___StyledDiv-sc-1x1z9nz-0",
|
||||
"isStatic": false,
|
||||
"lastClassName": "gydHvj",
|
||||
"rules": Array [
|
||||
"grid-column:",
|
||||
[Function],
|
||||
";",
|
||||
],
|
||||
},
|
||||
"displayName": "ActionItem___StyledDiv",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "ActionItem___StyledDiv-sc-1x1z9nz-0",
|
||||
"target": "div",
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
"withComponent": [Function],
|
||||
}
|
||||
}
|
||||
forwardedRef={null}
|
||||
>
|
||||
<div
|
||||
className="ActionItem___StyledDiv-sc-1x1z9nz-0 fZyHIW"
|
||||
>
|
||||
<Switch
|
||||
aria-label="Toggle notification success"
|
||||
id="notification-9000-success-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
label="Success"
|
||||
labelOff="Success"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<label
|
||||
className="pf-c-switch"
|
||||
data-ouia-component-id="OUIA-Generated-Switch-2"
|
||||
data-ouia-component-type="PF4/Switch"
|
||||
data-ouia-safe={true}
|
||||
htmlFor="notification-9000-success-toggle"
|
||||
>
|
||||
<input
|
||||
aria-label="Toggle notification success"
|
||||
aria-labelledby={null}
|
||||
checked={false}
|
||||
className="pf-c-switch__input"
|
||||
disabled={false}
|
||||
id="notification-9000-success-toggle"
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
className="pf-c-switch__toggle"
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-on"
|
||||
id={null}
|
||||
>
|
||||
Success
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-off"
|
||||
id={null}
|
||||
>
|
||||
Success
|
||||
</span>
|
||||
</label>
|
||||
</Switch>
|
||||
</div>
|
||||
</StyledComponent>
|
||||
</ActionItem___StyledDiv>
|
||||
</ActionItem>
|
||||
<ActionItem
|
||||
column={4}
|
||||
key=".3"
|
||||
visible={true}
|
||||
>
|
||||
<ActionItem___StyledDiv
|
||||
_css={4}
|
||||
>
|
||||
<StyledComponent
|
||||
_css={4}
|
||||
forwardedComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "ActionItem___StyledDiv-sc-1x1z9nz-0",
|
||||
"isStatic": false,
|
||||
"lastClassName": "gydHvj",
|
||||
"rules": Array [
|
||||
"grid-column:",
|
||||
[Function],
|
||||
";",
|
||||
],
|
||||
},
|
||||
"displayName": "ActionItem___StyledDiv",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "ActionItem___StyledDiv-sc-1x1z9nz-0",
|
||||
"target": "div",
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
"withComponent": [Function],
|
||||
}
|
||||
}
|
||||
forwardedRef={null}
|
||||
>
|
||||
<div
|
||||
className="ActionItem___StyledDiv-sc-1x1z9nz-0 gydHvj"
|
||||
>
|
||||
<Switch
|
||||
aria-label="Toggle notification failure"
|
||||
id="notification-9000-error-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
label="Failure"
|
||||
labelOff="Failure"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<label
|
||||
className="pf-c-switch"
|
||||
data-ouia-component-id="OUIA-Generated-Switch-3"
|
||||
data-ouia-component-type="PF4/Switch"
|
||||
data-ouia-safe={true}
|
||||
htmlFor="notification-9000-error-toggle"
|
||||
>
|
||||
<input
|
||||
aria-label="Toggle notification failure"
|
||||
aria-labelledby={null}
|
||||
checked={false}
|
||||
className="pf-c-switch__input"
|
||||
disabled={false}
|
||||
id="notification-9000-error-toggle"
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
className="pf-c-switch__toggle"
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-on"
|
||||
id={null}
|
||||
>
|
||||
Failure
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-off"
|
||||
id={null}
|
||||
>
|
||||
Failure
|
||||
</span>
|
||||
</label>
|
||||
</Switch>
|
||||
</div>
|
||||
</StyledComponent>
|
||||
</ActionItem___StyledDiv>
|
||||
</ActionItem>
|
||||
</div>
|
||||
</StyledComponent>
|
||||
</ActionsGrid>
|
||||
</td>
|
||||
</TdBase>
|
||||
</Td>
|
||||
</StyledComponent>
|
||||
</Styled(DataListAction)>
|
||||
</div>
|
||||
</DataListItemRow>
|
||||
</li>
|
||||
</DataListItem>
|
||||
</ActionsTd___StyledTd>
|
||||
</ActionsTd>
|
||||
</tr>
|
||||
</TrBase>
|
||||
</Tr>
|
||||
</NotificationListItem>
|
||||
`;
|
||||
|
||||
@ -13,9 +13,13 @@ export default function ActionItem({ column, tooltip, visible, children }) {
|
||||
grid-column: ${column};
|
||||
`}
|
||||
>
|
||||
<Tooltip content={tooltip} position="top">
|
||||
<div>{children}</div>
|
||||
</Tooltip>
|
||||
{tooltip ? (
|
||||
<Tooltip content={tooltip} position="top">
|
||||
<div>{children}</div>
|
||||
</Tooltip>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -13,7 +13,12 @@ const Th = styled(PFTh)`
|
||||
--pf-c-table--cell--Overflow: initial;
|
||||
`;
|
||||
|
||||
export default function HeaderRow({ qsConfig, isExpandable, children }) {
|
||||
export default function HeaderRow({
|
||||
qsConfig,
|
||||
isExpandable,
|
||||
isSelectable,
|
||||
children,
|
||||
}) {
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
|
||||
@ -49,7 +54,7 @@ export default function HeaderRow({ qsConfig, isExpandable, children }) {
|
||||
<Thead>
|
||||
<Tr>
|
||||
{isExpandable && <Th />}
|
||||
<Th />
|
||||
{isSelectable && <Th />}
|
||||
{React.Children.map(
|
||||
children,
|
||||
child =>
|
||||
@ -66,6 +71,10 @@ export default function HeaderRow({ qsConfig, isExpandable, children }) {
|
||||
);
|
||||
}
|
||||
|
||||
HeaderRow.defaultProps = {
|
||||
isSelectable: true,
|
||||
};
|
||||
|
||||
export function HeaderCell({
|
||||
sortKey,
|
||||
onSort,
|
||||
|
||||
@ -48,6 +48,7 @@ describe('<ApplicationsList/>', () => {
|
||||
});
|
||||
await waitForElement(wrapper, 'ApplicationsList', el => el.length > 0);
|
||||
});
|
||||
|
||||
test('should have data fetched and render 2 rows', async () => {
|
||||
ApplicationsAPI.read.mockResolvedValue(applications);
|
||||
ApplicationsAPI.readOptions.mockResolvedValue(options);
|
||||
@ -69,14 +70,20 @@ describe('<ApplicationsList/>', () => {
|
||||
waitForElement(wrapper, 'ApplicationsList', el => el.length > 0);
|
||||
|
||||
wrapper
|
||||
.find('input#select-application-1')
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.simulate('change', applications.data.results[0]);
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('input#select-application-1').prop('checked')).toBe(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.prop('checked')
|
||||
).toBe(true);
|
||||
await act(async () =>
|
||||
wrapper.find('Button[aria-label="Delete"]').prop('onClick')()
|
||||
);
|
||||
@ -131,13 +138,21 @@ describe('<ApplicationsList/>', () => {
|
||||
});
|
||||
waitForElement(wrapper, 'ApplicationsList', el => el.length > 0);
|
||||
|
||||
wrapper.find('input#select-application-1').simulate('change', 'a');
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.simulate('change', 'a');
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('input#select-application-1').prop('checked')).toBe(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.prop('checked')
|
||||
).toBe(true);
|
||||
await act(async () =>
|
||||
wrapper.find('Button[aria-label="Delete"]').prop('onClick')()
|
||||
);
|
||||
@ -163,6 +178,7 @@ describe('<ApplicationsList/>', () => {
|
||||
waitForElement(wrapper, 'ApplicationsList', el => el.length > 0);
|
||||
expect(wrapper.find('ToolbarAddButton').length).toBe(0);
|
||||
});
|
||||
|
||||
test('should not render edit button for first list item', async () => {
|
||||
applications.data.results[0].summary_fields.user_capabilities.edit = false;
|
||||
ApplicationsAPI.read.mockResolvedValue(applications);
|
||||
|
||||
@ -1,104 +1,65 @@
|
||||
import React from 'react';
|
||||
import { string, bool, func } from 'prop-types';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import {
|
||||
Button,
|
||||
DataListAction as _DataListAction,
|
||||
DataListCheck,
|
||||
DataListItem,
|
||||
DataListItemCells,
|
||||
DataListItemRow,
|
||||
Tooltip,
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import { Button } from '@patternfly/react-core';
|
||||
import { Tr, Td } from '@patternfly/react-table';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import { PencilAltIcon } from '@patternfly/react-icons';
|
||||
import { ActionsTd, ActionItem } from '../../../components/PaginatedTable';
|
||||
import { formatDateString } from '../../../util/dates';
|
||||
import { Application } from '../../../types';
|
||||
import DataListCell from '../../../components/DataListCell';
|
||||
|
||||
const DataListAction = styled(_DataListAction)`
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: 40px;
|
||||
`;
|
||||
|
||||
const Label = styled.b`
|
||||
margin-right: 20px;
|
||||
`;
|
||||
|
||||
function ApplicationListItem({
|
||||
application,
|
||||
isSelected,
|
||||
onSelect,
|
||||
detailUrl,
|
||||
rowIndex,
|
||||
i18n,
|
||||
}) {
|
||||
const labelId = `check-action-${application.id}`;
|
||||
return (
|
||||
<DataListItem
|
||||
key={application.id}
|
||||
aria-labelledby={labelId}
|
||||
id={`${application.id}`}
|
||||
>
|
||||
<DataListItemRow>
|
||||
<DataListCheck
|
||||
id={`select-application-${application.id}`}
|
||||
checked={isSelected}
|
||||
onChange={onSelect}
|
||||
aria-labelledby={labelId}
|
||||
/>
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell
|
||||
key="divider"
|
||||
aria-label={i18n._(t`application name`)}
|
||||
>
|
||||
<Link to={`${detailUrl}`}>
|
||||
<b>{application.name}</b>
|
||||
</Link>
|
||||
</DataListCell>,
|
||||
<DataListCell
|
||||
key="organization"
|
||||
aria-label={i18n._(t`organization name`)}
|
||||
>
|
||||
<Link
|
||||
to={`/organizations/${application.summary_fields.organization.id}`}
|
||||
>
|
||||
<b>{application.summary_fields.organization.name}</b>
|
||||
</Link>
|
||||
</DataListCell>,
|
||||
<DataListCell key="modified" aria-label={i18n._(t`last modified`)}>
|
||||
<Label>{i18n._(t`Last Modified`)}</Label>
|
||||
<span>{formatDateString(application.modified)}</span>
|
||||
</DataListCell>,
|
||||
]}
|
||||
/>
|
||||
<DataListAction
|
||||
aria-label={i18n._(t`actions`)}
|
||||
aria-labelledby={labelId}
|
||||
id={labelId}
|
||||
<Tr id={`application-row-${application.id}`}>
|
||||
<Td
|
||||
select={{
|
||||
rowIndex,
|
||||
isSelected,
|
||||
onSelect,
|
||||
}}
|
||||
dataLabel={i18n._(t`Selected`)}
|
||||
/>
|
||||
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
|
||||
<Link to={`${detailUrl}`}>
|
||||
<b>{application.name}</b>
|
||||
</Link>
|
||||
</Td>
|
||||
<Td dataLabel={i18n._(t`Organization`)}>
|
||||
<Link
|
||||
to={`/organizations/${application.summary_fields.organization.id}`}
|
||||
>
|
||||
{application.summary_fields.user_capabilities.edit ? (
|
||||
<Tooltip content={i18n._(t`Edit application`)} position="top">
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit application`)}
|
||||
variant="plain"
|
||||
component={Link}
|
||||
to={`/applications/${application.id}/edit`}
|
||||
>
|
||||
<PencilAltIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</DataListAction>
|
||||
</DataListItemRow>
|
||||
</DataListItem>
|
||||
<b>{application.summary_fields.organization.name}</b>
|
||||
</Link>
|
||||
</Td>
|
||||
<Td dataLabel={i18n._(t`Last Modified`)}>
|
||||
{formatDateString(application.modified)}
|
||||
</Td>
|
||||
<ActionsTd dataLabel={i18n._(t`Actions`)}>
|
||||
<ActionItem
|
||||
visible={application.summary_fields.user_capabilities.edit}
|
||||
tooltip={i18n._(t`Edit application`)}
|
||||
>
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit application`)}
|
||||
variant="plain"
|
||||
component={Link}
|
||||
to={`/applications/${application.id}/edit`}
|
||||
>
|
||||
<PencilAltIcon />
|
||||
</Button>
|
||||
</ActionItem>
|
||||
</ActionsTd>
|
||||
</Tr>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -18,12 +18,16 @@ describe('<ApplicationListItem/>', () => {
|
||||
test('should mount successfully', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<ApplicationListItem
|
||||
application={application}
|
||||
detailUrl="/organizations/2/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<ApplicationListItem
|
||||
application={application}
|
||||
detailUrl="/organizations/2/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('ApplicationListItem').length).toBe(1);
|
||||
@ -31,38 +35,30 @@ describe('<ApplicationListItem/>', () => {
|
||||
test('should render the proper data', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<ApplicationListItem
|
||||
application={application}
|
||||
detailUrl="/organizations/2/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<ApplicationListItem
|
||||
application={application}
|
||||
detailUrl="/organizations/2/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
});
|
||||
expect(
|
||||
wrapper.find('DataListCell[aria-label="application name"]').text()
|
||||
wrapper
|
||||
.find('Td')
|
||||
.at(1)
|
||||
.text()
|
||||
).toBe('Foo');
|
||||
expect(
|
||||
wrapper.find('DataListCell[aria-label="organization name"]').text()
|
||||
wrapper
|
||||
.find('Td')
|
||||
.at(2)
|
||||
.text()
|
||||
).toBe('Organization');
|
||||
expect(wrapper.find('input#select-application-1').prop('checked')).toBe(
|
||||
false
|
||||
);
|
||||
expect(wrapper.find('PencilAltIcon').length).toBe(1);
|
||||
});
|
||||
test('should be checked', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<ApplicationListItem
|
||||
application={application}
|
||||
detailUrl="/organizations/2/details"
|
||||
isSelected
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('input#select-application-1').prop('checked')).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -11,7 +11,11 @@ import AlertModal from '../../../components/AlertModal';
|
||||
|
||||
import DatalistToolbar from '../../../components/DataListToolbar';
|
||||
import { ApplicationsAPI } from '../../../api';
|
||||
import PaginatedDataList, {
|
||||
import PaginatedTable, {
|
||||
HeaderRow,
|
||||
HeaderCell,
|
||||
} from '../../../components/PaginatedTable';
|
||||
import {
|
||||
ToolbarDeleteButton,
|
||||
ToolbarAddButton,
|
||||
} from '../../../components/PaginatedDataList';
|
||||
@ -104,7 +108,7 @@ function ApplicationsList({ i18n }) {
|
||||
<>
|
||||
<PageSection>
|
||||
<Card>
|
||||
<PaginatedDataList
|
||||
<PaginatedTable
|
||||
contentError={error}
|
||||
hasContentLoading={isLoading || deleteLoading}
|
||||
items={applications}
|
||||
@ -123,24 +127,6 @@ function ApplicationsList({ i18n }) {
|
||||
key: 'description__icontains',
|
||||
},
|
||||
]}
|
||||
toolbarSortColumns={[
|
||||
{
|
||||
name: i18n._(t`Name`),
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Created`),
|
||||
key: 'created',
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Organization`),
|
||||
key: 'organization',
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Description`),
|
||||
key: 'description',
|
||||
},
|
||||
]}
|
||||
toolbarSearchableKeys={searchableKeys}
|
||||
toolbarRelatedSearchableKeys={relatedSearchableKeys}
|
||||
renderToolbar={props => (
|
||||
@ -170,7 +156,17 @@ function ApplicationsList({ i18n }) {
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
renderItem={application => (
|
||||
headerRow={
|
||||
<HeaderRow qsConfig={QS_CONFIG}>
|
||||
<HeaderCell sortKey="name">{i18n._(t`Name`)}</HeaderCell>
|
||||
<HeaderCell sortKey="organization">
|
||||
{i18n._(t`Organization`)}
|
||||
</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Last Modified`)}</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Actions`)}</HeaderCell>
|
||||
</HeaderRow>
|
||||
}
|
||||
renderRow={(application, index) => (
|
||||
<ApplicationListItem
|
||||
key={application.id}
|
||||
value={application.name}
|
||||
@ -178,6 +174,7 @@ function ApplicationsList({ i18n }) {
|
||||
detailUrl={`${match.url}/${application.id}/details`}
|
||||
onSelect={() => handleSelect(application)}
|
||||
isSelected={selected.some(row => row.id === application.id)}
|
||||
rowIndex={index}
|
||||
/>
|
||||
)}
|
||||
emptyStateControls={
|
||||
|
||||
@ -8,10 +8,14 @@ import { CredentialTypesAPI } from '../../../api';
|
||||
import { getQSConfig, parseQueryString } from '../../../util/qs';
|
||||
import useRequest, { useDeleteItems } from '../../../util/useRequest';
|
||||
import useSelected from '../../../util/useSelected';
|
||||
import PaginatedDataList, {
|
||||
import {
|
||||
ToolbarDeleteButton,
|
||||
ToolbarAddButton,
|
||||
} from '../../../components/PaginatedDataList';
|
||||
import PaginatedTable, {
|
||||
HeaderRow,
|
||||
HeaderCell,
|
||||
} from '../../../components/PaginatedTable';
|
||||
import ErrorDetail from '../../../components/ErrorDetail';
|
||||
import AlertModal from '../../../components/AlertModal';
|
||||
import DatalistToolbar from '../../../components/DataListToolbar';
|
||||
@ -106,7 +110,7 @@ function CredentialTypeList({ i18n }) {
|
||||
<>
|
||||
<PageSection>
|
||||
<Card>
|
||||
<PaginatedDataList
|
||||
<PaginatedTable
|
||||
contentError={contentError}
|
||||
hasContentLoading={isLoading || deleteLoading}
|
||||
items={credentialTypes}
|
||||
@ -162,7 +166,13 @@ function CredentialTypeList({ i18n }) {
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
renderItem={credentialType => (
|
||||
headerRow={
|
||||
<HeaderRow qsConfig={QS_CONFIG}>
|
||||
<HeaderCell sortKey="name">{i18n._(t`Name`)}</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Actions`)}</HeaderCell>
|
||||
</HeaderRow>
|
||||
}
|
||||
renderRow={(credentialType, index) => (
|
||||
<CredentialTypeListItem
|
||||
key={credentialType.id}
|
||||
value={credentialType.name}
|
||||
@ -170,6 +180,7 @@ function CredentialTypeList({ i18n }) {
|
||||
detailUrl={`${match.url}/${credentialType.id}/details`}
|
||||
onSelect={() => handleSelect(credentialType)}
|
||||
isSelected={selected.some(row => row.id === credentialType.id)}
|
||||
rowIndex={index}
|
||||
/>
|
||||
)}
|
||||
emptyStateControls={
|
||||
|
||||
@ -72,12 +72,18 @@ describe('<CredentialTypeList', () => {
|
||||
await waitForElement(wrapper, 'CredentialTypeList', el => el.length > 0);
|
||||
|
||||
wrapper
|
||||
.find('input#select-credential-types-1')
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.simulate('change', credentialTypes.data.results[0]);
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find('input#select-credential-types-1').prop('checked')
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.prop('checked')
|
||||
).toBe(true);
|
||||
|
||||
await act(async () => {
|
||||
@ -133,10 +139,18 @@ describe('<CredentialTypeList', () => {
|
||||
});
|
||||
waitForElement(wrapper, 'CredentialTypeList', el => el.length > 0);
|
||||
|
||||
wrapper.find('input#select-credential-types-1').simulate('change', 'a');
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.simulate('change', 'a');
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper.find('input#select-credential-types-1').prop('checked')
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.prop('checked')
|
||||
).toBe(true);
|
||||
|
||||
await act(async () =>
|
||||
|
||||
@ -3,82 +3,53 @@ 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 { CredentialType } from '../../../types';
|
||||
|
||||
const DataListAction = styled(_DataListAction)`
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: 40px;
|
||||
`;
|
||||
|
||||
function CredentialTypeListItem({
|
||||
credentialType,
|
||||
detailUrl,
|
||||
isSelected,
|
||||
onSelect,
|
||||
rowIndex,
|
||||
i18n,
|
||||
}) {
|
||||
const labelId = `check-action-${credentialType.id}`;
|
||||
|
||||
return (
|
||||
<DataListItem
|
||||
key={credentialType.id}
|
||||
aria-labelledby={labelId}
|
||||
id={`${credentialType.id} `}
|
||||
>
|
||||
<DataListItemRow>
|
||||
<DataListCheck
|
||||
id={`select-credential-types-${credentialType.id}`}
|
||||
checked={isSelected}
|
||||
onChange={onSelect}
|
||||
aria-labelledby={labelId}
|
||||
/>
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell
|
||||
key="name"
|
||||
aria-label={i18n._(t`credential type name`)}
|
||||
>
|
||||
<Link to={`${detailUrl}`}>
|
||||
<b>{credentialType.name}</b>
|
||||
</Link>
|
||||
</DataListCell>,
|
||||
]}
|
||||
/>
|
||||
<DataListAction
|
||||
aria-label={i18n._(t`actions`)}
|
||||
aria-labelledby={labelId}
|
||||
id={labelId}
|
||||
<Tr id={`credential-type-row-${credentialType.id}`}>
|
||||
<Td
|
||||
select={{
|
||||
rowIndex,
|
||||
isSelected,
|
||||
onSelect,
|
||||
}}
|
||||
dataLabel={i18n._(t`Selected`)}
|
||||
/>
|
||||
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
|
||||
<Link to={`${detailUrl}`}>
|
||||
<b>{credentialType.name}</b>
|
||||
</Link>
|
||||
</Td>
|
||||
<ActionsTd dataLabel={i18n._(t`Actions`)}>
|
||||
<ActionItem
|
||||
visible={credentialType.summary_fields.user_capabilities.edit}
|
||||
tooltip={i18n._(t`Edit credential type`)}
|
||||
>
|
||||
{credentialType.summary_fields.user_capabilities.edit && (
|
||||
<Tooltip content={i18n._(t`Edit credential type`)} position="top">
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit credential type`)}
|
||||
variant="plain"
|
||||
component={Link}
|
||||
to={`/credential_types/${credentialType.id}/edit`}
|
||||
>
|
||||
<PencilAltIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</DataListAction>
|
||||
</DataListItemRow>
|
||||
</DataListItem>
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit credential type`)}
|
||||
variant="plain"
|
||||
component={Link}
|
||||
to={`/credential_types/${credentialType.id}/edit`}
|
||||
>
|
||||
<PencilAltIcon />
|
||||
</Button>
|
||||
</ActionItem>
|
||||
</ActionsTd>
|
||||
</Tr>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -17,12 +17,16 @@ describe('<CredentialTypeListItem/>', () => {
|
||||
test('should mount successfully', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<CredentialTypeListItem
|
||||
credentialType={credential_type}
|
||||
detailUrl="credential_types/1/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<CredentialTypeListItem
|
||||
credentialType={credential_type}
|
||||
detailUrl="credential_types/1/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('CredentialTypeListItem').length).toBe(1);
|
||||
@ -31,48 +35,38 @@ describe('<CredentialTypeListItem/>', () => {
|
||||
test('should render the proper data', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<CredentialTypeListItem
|
||||
credentialType={credential_type}
|
||||
detailsUrl="credential_types/1/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<CredentialTypeListItem
|
||||
credentialType={credential_type}
|
||||
detailsUrl="credential_types/1/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
});
|
||||
expect(
|
||||
wrapper.find('DataListCell[aria-label="credential type name"]').text()
|
||||
).toBe('Foo');
|
||||
expect(wrapper.find('Td[dataLabel="Name"]').text()).toBe('Foo');
|
||||
expect(wrapper.find('PencilAltIcon').length).toBe(1);
|
||||
expect(
|
||||
wrapper.find('input#select-credential-types-1').prop('checked')
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test('should be checked', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<CredentialTypeListItem
|
||||
credentialType={credential_type}
|
||||
detailsUrl="credential_types/1/details"
|
||||
isSelected
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
expect(
|
||||
wrapper.find('input#select-credential-types-1').prop('checked')
|
||||
).toBe(true);
|
||||
expect(wrapper.find('.pf-c-table__check input').prop('checked')).toBe(
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
test('edit button shown to users with edit capabilities', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<CredentialTypeListItem
|
||||
credentialType={credential_type}
|
||||
detailsUrl="credential_types/1/details"
|
||||
isSelected
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<CredentialTypeListItem
|
||||
credentialType={credential_type}
|
||||
detailsUrl="credential_types/1/details"
|
||||
isSelected
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
});
|
||||
|
||||
@ -82,15 +76,19 @@ describe('<CredentialTypeListItem/>', () => {
|
||||
test('edit button hidden from users without edit capabilities', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<CredentialTypeListItem
|
||||
credentialType={{
|
||||
...credential_type,
|
||||
summary_fields: { user_capabilities: { edit: false } },
|
||||
}}
|
||||
detailsUrl="credential_types/1/details"
|
||||
isSelected
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<CredentialTypeListItem
|
||||
credentialType={{
|
||||
...credential_type,
|
||||
summary_fields: { user_capabilities: { edit: false } },
|
||||
}}
|
||||
detailsUrl="credential_types/1/details"
|
||||
isSelected
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -8,9 +8,11 @@ import { InstanceGroupsAPI } from '../../../api';
|
||||
import { getQSConfig, parseQueryString } from '../../../util/qs';
|
||||
import useRequest, { useDeleteItems } from '../../../util/useRequest';
|
||||
import useSelected from '../../../util/useSelected';
|
||||
import PaginatedDataList, {
|
||||
ToolbarDeleteButton,
|
||||
} from '../../../components/PaginatedDataList';
|
||||
import PaginatedTable, {
|
||||
HeaderRow,
|
||||
HeaderCell,
|
||||
} from '../../../components/PaginatedTable';
|
||||
import { ToolbarDeleteButton } from '../../../components/PaginatedDataList';
|
||||
import ErrorDetail from '../../../components/ErrorDetail';
|
||||
import AlertModal from '../../../components/AlertModal';
|
||||
import DatalistToolbar from '../../../components/DataListToolbar';
|
||||
@ -189,7 +191,7 @@ function InstanceGroupList({ i18n }) {
|
||||
<>
|
||||
<PageSection>
|
||||
<Card>
|
||||
<PaginatedDataList
|
||||
<PaginatedTable
|
||||
contentError={contentError}
|
||||
hasContentLoading={isLoading || deleteLoading}
|
||||
items={instanceGroups}
|
||||
@ -220,7 +222,18 @@ function InstanceGroupList({ i18n }) {
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
renderItem={instanceGroup => (
|
||||
headerRow={
|
||||
<HeaderRow qsConfig={QS_CONFIG}>
|
||||
<HeaderCell sortKey="name">{i18n._(t`Name`)}</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Type`)}</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Running Jobs`)}</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Total Jobs`)}</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Instances`)}</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Capacity`)}</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Actions`)}</HeaderCell>
|
||||
</HeaderRow>
|
||||
}
|
||||
renderRow={(instanceGroup, index) => (
|
||||
<InstanceGroupListItem
|
||||
key={instanceGroup.id}
|
||||
value={instanceGroup.name}
|
||||
@ -228,6 +241,7 @@ function InstanceGroupList({ i18n }) {
|
||||
detailUrl={getDetailUrl(instanceGroup)}
|
||||
onSelect={() => handleSelect(instanceGroup)}
|
||||
isSelected={selected.some(row => row.id === instanceGroup.id)}
|
||||
rowIndex={index}
|
||||
/>
|
||||
)}
|
||||
emptyStateControls={canAdd && addButton}
|
||||
|
||||
@ -71,13 +71,19 @@ describe('<InstanceGroupList />', () => {
|
||||
await waitForElement(wrapper, 'InstanceGroupList', el => el.length > 0);
|
||||
|
||||
wrapper
|
||||
.find('input#select-instance-groups-1')
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.simulate('change', instanceGroups);
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('input#select-instance-groups-1').prop('checked')).toBe(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.prop('checked')
|
||||
).toBe(true);
|
||||
|
||||
await act(async () => {
|
||||
wrapper.find('Button[aria-label="Delete"]').prop('onClick')();
|
||||
@ -102,16 +108,22 @@ describe('<InstanceGroupList />', () => {
|
||||
});
|
||||
await waitForElement(wrapper, 'InstanceGroupList', el => el.length > 0);
|
||||
|
||||
const instanceGroupIndex = [1, 2, 3];
|
||||
const instanceGroupIndex = [0, 1, 2];
|
||||
|
||||
instanceGroupIndex.forEach(element => {
|
||||
wrapper
|
||||
.find(`input#select-instance-groups-${element}`)
|
||||
.find('.pf-c-table__check')
|
||||
.at(element)
|
||||
.find('input')
|
||||
.simulate('change', instanceGroups);
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find(`input#select-instance-groups-${element}`).prop('checked')
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.at(element)
|
||||
.find('input')
|
||||
.prop('checked')
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
@ -159,11 +171,19 @@ describe('<InstanceGroupList />', () => {
|
||||
});
|
||||
waitForElement(wrapper, 'InstanceGroupList', el => el.length > 0);
|
||||
|
||||
wrapper.find('input#select-instance-groups-1').simulate('change', 'a');
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.simulate('change', 'a');
|
||||
wrapper.update();
|
||||
expect(wrapper.find('input#select-instance-groups-1').prop('checked')).toBe(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.prop('checked')
|
||||
).toBe(true);
|
||||
|
||||
await act(async () =>
|
||||
wrapper.find('Button[aria-label="Delete"]').prop('onClick')()
|
||||
|
||||
@ -5,48 +5,18 @@ import { t } from '@lingui/macro';
|
||||
import { Link } from 'react-router-dom';
|
||||
import 'styled-components/macro';
|
||||
import {
|
||||
Badge as PFBadge,
|
||||
Button,
|
||||
DataListAction as _DataListAction,
|
||||
DataListCheck,
|
||||
DataListItem,
|
||||
DataListItemCells,
|
||||
DataListItemRow,
|
||||
Label,
|
||||
Progress,
|
||||
ProgressMeasureLocation,
|
||||
ProgressSize,
|
||||
Tooltip,
|
||||
} 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 { InstanceGroup } from '../../../types';
|
||||
|
||||
const DataListCell = styled(_DataListCell)`
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const Badge = styled(PFBadge)`
|
||||
margin-left: 8px;
|
||||
`;
|
||||
|
||||
const ListGroup = styled.span`
|
||||
margin-left: 12px;
|
||||
|
||||
&:first-of-type {
|
||||
margin-left: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const DataListAction = styled(_DataListAction)`
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: 40px;
|
||||
`;
|
||||
|
||||
const Unavailable = styled.span`
|
||||
color: var(--pf-global--danger-color--200);
|
||||
`;
|
||||
@ -56,6 +26,7 @@ function InstanceGroupListItem({
|
||||
detailUrl,
|
||||
isSelected,
|
||||
onSelect,
|
||||
rowIndex,
|
||||
i18n,
|
||||
}) {
|
||||
const labelId = `check-action-${instanceGroup.id}`;
|
||||
@ -104,98 +75,50 @@ function InstanceGroupListItem({
|
||||
};
|
||||
|
||||
return (
|
||||
<DataListItem
|
||||
key={instanceGroup.id}
|
||||
aria-labelledby={labelId}
|
||||
id={`${instanceGroup.id} `}
|
||||
>
|
||||
<DataListItemRow>
|
||||
<DataListCheck
|
||||
id={`select-instance-groups-${instanceGroup.id}`}
|
||||
checked={isSelected}
|
||||
onChange={onSelect}
|
||||
aria-labelledby={labelId}
|
||||
/>
|
||||
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell
|
||||
key="name"
|
||||
aria-label={i18n._(t`instance group name`)}
|
||||
>
|
||||
<span id={labelId}>
|
||||
<Link to={`${detailUrl}`}>
|
||||
<b>{instanceGroup.name}</b>
|
||||
</Link>
|
||||
</span>
|
||||
{verifyInstanceGroup(instanceGroup)}
|
||||
</DataListCell>,
|
||||
|
||||
<DataListCell
|
||||
key="type"
|
||||
aria-label={i18n._(t`instance group type`)}
|
||||
>
|
||||
<b css="margin-right: 24px">{i18n._(t`Type`)}</b>
|
||||
<span id={labelId}>
|
||||
{isContainerGroup(instanceGroup)
|
||||
? i18n._(t`Container group`)
|
||||
: i18n._(t`Instance group`)}
|
||||
</span>
|
||||
</DataListCell>,
|
||||
<DataListCell
|
||||
key="related-field-counts"
|
||||
aria-label={i18n._(t`instance counts`)}
|
||||
width={2}
|
||||
>
|
||||
<ListGroup>
|
||||
<b>{i18n._(t`Running jobs`)}</b>
|
||||
<Badge isRead>{instanceGroup.jobs_running}</Badge>
|
||||
</ListGroup>
|
||||
<ListGroup>
|
||||
<b>{i18n._(t`Total jobs`)}</b>
|
||||
<Badge isRead>{instanceGroup.jobs_total}</Badge>
|
||||
</ListGroup>
|
||||
|
||||
{!instanceGroup.is_containerized ? (
|
||||
<ListGroup>
|
||||
<b>{i18n._(t`Instances`)}</b>
|
||||
<Badge isRead>{instanceGroup.instances}</Badge>
|
||||
</ListGroup>
|
||||
) : null}
|
||||
</DataListCell>,
|
||||
|
||||
<DataListCell
|
||||
key="capacity"
|
||||
aria-label={i18n._(t`instance group used capacity`)}
|
||||
>
|
||||
{usedCapacity(instanceGroup)}
|
||||
</DataListCell>,
|
||||
]}
|
||||
/>
|
||||
<DataListAction
|
||||
aria-label={i18n._(t`actions`)}
|
||||
aria-labelledby={labelId}
|
||||
id={labelId}
|
||||
<Tr id={`ig-row-${instanceGroup.id}`}>
|
||||
<Td
|
||||
select={{
|
||||
rowIndex,
|
||||
isSelected,
|
||||
onSelect,
|
||||
}}
|
||||
dataLabel={i18n._(t`Selected`)}
|
||||
/>
|
||||
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
|
||||
<Link to={`${detailUrl}`}>
|
||||
<b>{instanceGroup.name}</b>
|
||||
{verifyInstanceGroup(instanceGroup)}
|
||||
</Link>
|
||||
</Td>
|
||||
<Td dataLabel={i18n._(t`Type`)}>
|
||||
{isContainerGroup(instanceGroup)
|
||||
? i18n._(t`Container group`)
|
||||
: i18n._(t`Instance group`)}
|
||||
</Td>
|
||||
<Td dataLabel={i18n._(t`Running jobs`)}>{instanceGroup.jobs_running}</Td>
|
||||
<Td dataLabel={i18n._(t`Total jobs`)}>{instanceGroup.jobs_total}</Td>
|
||||
<Td dataLabel={i18n._(t`Instances`)}>{instanceGroup.instances}</Td>
|
||||
<Td dataLabel={i18n._(t`Capacity`)}>{usedCapacity(instanceGroup)}</Td>
|
||||
<ActionsTd dataLabel={i18n._(t`Actions`)}>
|
||||
<ActionItem
|
||||
visible={instanceGroup.summary_fields.user_capabilities.edit}
|
||||
tooltip={i18n._(t`Edit instance group`)}
|
||||
>
|
||||
{instanceGroup.summary_fields.user_capabilities.edit && (
|
||||
<Tooltip content={i18n._(t`Edit instance group`)} position="top">
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit instance group`)}
|
||||
variant="plain"
|
||||
component={Link}
|
||||
to={
|
||||
isContainerGroup(instanceGroup)
|
||||
? `/instance_groups/container_group/${instanceGroup.id}/edit`
|
||||
: `/instance_groups/${instanceGroup.id}/edit`
|
||||
}
|
||||
>
|
||||
<PencilAltIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</DataListAction>
|
||||
</DataListItemRow>
|
||||
</DataListItem>
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit instance group`)}
|
||||
variant="plain"
|
||||
component={Link}
|
||||
to={
|
||||
isContainerGroup(instanceGroup)
|
||||
? `/instance_groups/container_group/${instanceGroup.id}/edit`
|
||||
: `/instance_groups/${instanceGroup.id}/edit`
|
||||
}
|
||||
>
|
||||
<PencilAltIcon />
|
||||
</Button>
|
||||
</ActionItem>
|
||||
</ActionsTd>
|
||||
</Tr>
|
||||
);
|
||||
}
|
||||
InstanceGroupListItem.prototype = {
|
||||
|
||||
@ -47,12 +47,16 @@ describe('<InstanceGroupListItem/>', () => {
|
||||
test('should mount successfully', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<InstanceGroupListItem
|
||||
instanceGroup={instanceGroups[1]}
|
||||
detailUrl="instance_groups/1/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<InstanceGroupListItem
|
||||
instanceGroup={instanceGroups[1]}
|
||||
detailUrl="instance_groups/1/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('InstanceGroupListItem').length).toBe(1);
|
||||
@ -61,73 +65,81 @@ describe('<InstanceGroupListItem/>', () => {
|
||||
test('should render the proper data instance group', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<InstanceGroupListItem
|
||||
instanceGroup={instanceGroups[0]}
|
||||
detailUrl="instance_groups/1/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<InstanceGroupListItem
|
||||
instanceGroup={instanceGroups[0]}
|
||||
detailUrl="instance_groups/1/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
});
|
||||
expect(
|
||||
wrapper.find('PFDataListCell[aria-label="instance group name"]').text()
|
||||
wrapper
|
||||
.find('Td')
|
||||
.at(1)
|
||||
.text()
|
||||
).toBe('Foo');
|
||||
expect(wrapper.find('Progress').prop('value')).toBe(40);
|
||||
expect(
|
||||
wrapper.find('PFDataListCell[aria-label="instance group type"]').text()
|
||||
).toBe('TypeInstance group');
|
||||
wrapper
|
||||
.find('Td')
|
||||
.at(2)
|
||||
.text()
|
||||
).toBe('Instance group');
|
||||
expect(wrapper.find('PencilAltIcon').length).toBe(1);
|
||||
expect(wrapper.find('input#select-instance-groups-1').prop('checked')).toBe(
|
||||
false
|
||||
expect(wrapper.find('.pf-c-table__check input').prop('checked')).toBe(
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
test('should render the proper data container group', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<InstanceGroupListItem
|
||||
instanceGroup={instanceGroups[1]}
|
||||
detailUrl="instance_groups/2/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<InstanceGroupListItem
|
||||
instanceGroup={instanceGroups[1]}
|
||||
detailUrl="instance_groups/2/details"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
});
|
||||
expect(
|
||||
wrapper.find('PFDataListCell[aria-label="instance group name"]').text()
|
||||
wrapper
|
||||
.find('Td')
|
||||
.at(1)
|
||||
.text()
|
||||
).toBe('Bar');
|
||||
|
||||
expect(
|
||||
wrapper.find('PFDataListCell[aria-label="instance group type"]').text()
|
||||
).toBe('TypeContainer group');
|
||||
wrapper
|
||||
.find('Td')
|
||||
.at(2)
|
||||
.text()
|
||||
).toBe('Container group');
|
||||
expect(wrapper.find('PencilAltIcon').length).toBe(0);
|
||||
});
|
||||
|
||||
test('should be checked', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<InstanceGroupListItem
|
||||
instanceGroup={instanceGroups[0]}
|
||||
detailUrl="instance_groups/1/details"
|
||||
isSelected
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('input#select-instance-groups-1').prop('checked')).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
test('edit button shown to users with edit capabilities', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<InstanceGroupListItem
|
||||
instanceGroup={instanceGroups[0]}
|
||||
detailUrl="instance_groups/1/details"
|
||||
isSelected
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<InstanceGroupListItem
|
||||
instanceGroup={instanceGroups[0]}
|
||||
detailUrl="instance_groups/1/details"
|
||||
isSelected
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
});
|
||||
|
||||
@ -137,12 +149,16 @@ describe('<InstanceGroupListItem/>', () => {
|
||||
test('edit button hidden from users without edit capabilities', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<InstanceGroupListItem
|
||||
instanceGroup={instanceGroups[1]}
|
||||
detailsUrl="instance_group/2/details"
|
||||
isSelected
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<InstanceGroupListItem
|
||||
instanceGroup={instanceGroups[1]}
|
||||
detailsUrl="instance_group/2/details"
|
||||
isSelected
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -4,7 +4,11 @@ import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Card, PageSection } from '@patternfly/react-core';
|
||||
import { NotificationTemplatesAPI } from '../../../api';
|
||||
import PaginatedDataList, {
|
||||
import PaginatedTable, {
|
||||
HeaderRow,
|
||||
HeaderCell,
|
||||
} from '../../../components/PaginatedTable';
|
||||
import {
|
||||
ToolbarAddButton,
|
||||
ToolbarDeleteButton,
|
||||
} from '../../../components/PaginatedDataList';
|
||||
@ -104,7 +108,7 @@ function NotificationTemplatesList({ i18n }) {
|
||||
<>
|
||||
<PageSection>
|
||||
<Card>
|
||||
<PaginatedDataList
|
||||
<PaginatedTable
|
||||
contentError={contentError}
|
||||
hasContentLoading={isTemplatesLoading || isDeleteLoading}
|
||||
items={templates}
|
||||
@ -149,16 +153,6 @@ function NotificationTemplatesList({ i18n }) {
|
||||
]}
|
||||
toolbarSearchableKeys={searchableKeys}
|
||||
toolbarRelatedSearchableKeys={relatedSearchableKeys}
|
||||
toolbarSortColumns={[
|
||||
{
|
||||
name: i18n._(t`Name`),
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Type`),
|
||||
key: 'notification_type',
|
||||
},
|
||||
]}
|
||||
renderToolbar={props => (
|
||||
<DataListToolbar
|
||||
{...props}
|
||||
@ -179,7 +173,17 @@ function NotificationTemplatesList({ i18n }) {
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
renderItem={template => (
|
||||
headerRow={
|
||||
<HeaderRow qsConfig={QS_CONFIG}>
|
||||
<HeaderCell sortKey="name">{i18n._(t`Name`)}</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Status`)}</HeaderCell>
|
||||
<HeaderCell sortKey="notification_type">
|
||||
{i18n._(t`Type`)}
|
||||
</HeaderCell>
|
||||
<HeaderCell>{i18n._(t`Actions`)}</HeaderCell>
|
||||
</HeaderRow>
|
||||
}
|
||||
renderRow={(template, index) => (
|
||||
<NotificationTemplateListItem
|
||||
key={template.id}
|
||||
fetchTemplates={fetchTemplates}
|
||||
@ -187,6 +191,7 @@ function NotificationTemplatesList({ i18n }) {
|
||||
detailUrl={`${match.url}/${template.id}`}
|
||||
isSelected={selected.some(row => row.id === template.id)}
|
||||
onSelect={() => handleSelect(template)}
|
||||
rowIndex={index}
|
||||
/>
|
||||
)}
|
||||
emptyStateControls={
|
||||
|
||||
@ -89,21 +89,33 @@ describe('<NotificationTemplateList />', () => {
|
||||
});
|
||||
|
||||
test('should select item', async () => {
|
||||
const itemCheckboxInput = 'input#select-template-1';
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<NotificationTemplateList />);
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find(itemCheckboxInput).prop('checked')).toEqual(false);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.prop('checked')
|
||||
).toEqual(false);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find(itemCheckboxInput)
|
||||
.closest('DataListCheck')
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.props()
|
||||
.onChange();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find(itemCheckboxInput).prop('checked')).toEqual(true);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.prop('checked')
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('should delete notifications', async () => {
|
||||
@ -135,7 +147,6 @@ describe('<NotificationTemplateList />', () => {
|
||||
});
|
||||
|
||||
test('should show error dialog shown for failed deletion', async () => {
|
||||
const itemCheckboxInput = 'input#select-template-1';
|
||||
OrganizationsAPI.destroy.mockRejectedValue(
|
||||
new Error({
|
||||
response: {
|
||||
@ -153,8 +164,9 @@ describe('<NotificationTemplateList />', () => {
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find(itemCheckboxInput)
|
||||
.closest('DataListCheck')
|
||||
.find('.pf-c-table__check')
|
||||
.first()
|
||||
.find('input')
|
||||
.props()
|
||||
.onChange();
|
||||
});
|
||||
|
||||
@ -3,32 +3,17 @@ import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
Button,
|
||||
DataListAction as _DataListAction,
|
||||
DataListCheck,
|
||||
DataListItem,
|
||||
DataListItemCells,
|
||||
DataListItemRow,
|
||||
Tooltip,
|
||||
} from '@patternfly/react-core';
|
||||
import { Button } from '@patternfly/react-core';
|
||||
import { Tr, Td } from '@patternfly/react-table';
|
||||
import { PencilAltIcon, BellIcon } from '@patternfly/react-icons';
|
||||
import { ActionsTd, ActionItem } from '../../../components/PaginatedTable';
|
||||
import { timeOfDay } from '../../../util/dates';
|
||||
import { NotificationTemplatesAPI, NotificationsAPI } from '../../../api';
|
||||
import DataListCell from '../../../components/DataListCell';
|
||||
import StatusLabel from '../../../components/StatusLabel';
|
||||
import CopyButton from '../../../components/CopyButton';
|
||||
import useRequest from '../../../util/useRequest';
|
||||
import { NOTIFICATION_TYPES } from '../constants';
|
||||
|
||||
const DataListAction = styled(_DataListAction)`
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: repeat(3, 40px);
|
||||
`;
|
||||
|
||||
const NUM_RETRIES = 25;
|
||||
const RETRY_TIMEOUT = 5000;
|
||||
|
||||
@ -38,6 +23,7 @@ function NotificationTemplateListItem({
|
||||
fetchTemplates,
|
||||
isSelected,
|
||||
onSelect,
|
||||
rowIndex,
|
||||
i18n,
|
||||
}) {
|
||||
const recentNotifications = template.summary_fields?.recent_notifications;
|
||||
@ -102,76 +88,65 @@ function NotificationTemplateListItem({
|
||||
const labelId = `template-name-${template.id}`;
|
||||
|
||||
return (
|
||||
<DataListItem key={template.id} aria-labelledby={labelId} id={template.id}>
|
||||
<DataListItemRow>
|
||||
<DataListCheck
|
||||
id={`select-template-${template.id}`}
|
||||
checked={isSelected}
|
||||
onChange={onSelect}
|
||||
aria-labelledby={labelId}
|
||||
/>
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell key="name" id={labelId}>
|
||||
<Link to={detailUrl}>
|
||||
<b>{template.name}</b>
|
||||
</Link>
|
||||
</DataListCell>,
|
||||
<DataListCell key="status">
|
||||
{status && <StatusLabel status={status} />}
|
||||
</DataListCell>,
|
||||
<DataListCell key="type">
|
||||
<strong>{i18n._(t`Type:`)}</strong>{' '}
|
||||
{NOTIFICATION_TYPES[template.notification_type] ||
|
||||
template.notification_type}
|
||||
</DataListCell>,
|
||||
]}
|
||||
/>
|
||||
<DataListAction
|
||||
aria-label={i18n._(t`actions`)}
|
||||
aria-labelledby={labelId}
|
||||
<Tr id={`notification-template-row-${template.id}`}>
|
||||
<Td
|
||||
select={{
|
||||
rowIndex,
|
||||
isSelected,
|
||||
onSelect,
|
||||
}}
|
||||
dataLabel={i18n._(t`Selected`)}
|
||||
/>
|
||||
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
|
||||
<Link to={`${detailUrl}`}>
|
||||
<b>{template.name}</b>
|
||||
</Link>
|
||||
</Td>
|
||||
<Td dataLabel={i18n._(t`Status`)}>
|
||||
{status && <StatusLabel status={status} />}
|
||||
</Td>
|
||||
<Td dataLabel={i18n._(t`Type`)}>
|
||||
{NOTIFICATION_TYPES[template.notification_type] ||
|
||||
template.notification_type}
|
||||
</Td>
|
||||
<ActionsTd dataLabel={i18n._(t`Actions`)}>
|
||||
<ActionItem visible tooltip={i18n._(t`Test notification`)}>
|
||||
<Button
|
||||
aria-label={i18n._(t`Test Notification`)}
|
||||
variant="plain"
|
||||
onClick={sendTestNotification}
|
||||
isDisabled={isLoading || status === 'running'}
|
||||
>
|
||||
<BellIcon />
|
||||
</Button>
|
||||
</ActionItem>
|
||||
<ActionItem
|
||||
visible={template.summary_fields.user_capabilities.edit}
|
||||
tooltip={i18n._(t`Edit`)}
|
||||
>
|
||||
<Tooltip content={i18n._(t`Test Notification`)} position="top">
|
||||
<Button
|
||||
aria-label={i18n._(t`Test Notification`)}
|
||||
variant="plain"
|
||||
onClick={sendTestNotification}
|
||||
isDisabled={isLoading || status === 'running'}
|
||||
>
|
||||
<BellIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
{template.summary_fields.user_capabilities.edit ? (
|
||||
<Tooltip
|
||||
content={i18n._(t`Edit Notification Template`)}
|
||||
position="top"
|
||||
>
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit Notification Template`)}
|
||||
variant="plain"
|
||||
component={Link}
|
||||
to={`/notification_templates/${template.id}/edit`}
|
||||
>
|
||||
<PencilAltIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
{template.summary_fields.user_capabilities.copy && (
|
||||
<Tooltip content={i18n._(t`Copy Notification Template`)}>
|
||||
<CopyButton
|
||||
copyItem={copyTemplate}
|
||||
isCopyDisabled={isCopyDisabled}
|
||||
onCopyStart={handleCopyStart}
|
||||
onCopyFinish={handleCopyFinish}
|
||||
errorMessage={i18n._(t`Failed to copy template.`)}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</DataListAction>
|
||||
</DataListItemRow>
|
||||
</DataListItem>
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit Notification Template`)}
|
||||
variant="plain"
|
||||
component={Link}
|
||||
to={`/notification_templates/${template.id}/edit`}
|
||||
>
|
||||
<PencilAltIcon />
|
||||
</Button>
|
||||
</ActionItem>
|
||||
<ActionItem
|
||||
visible={template.summary_fields.user_capabilities.copy}
|
||||
tooltip={i18n._(t`Copy Notification Template`)}
|
||||
>
|
||||
<CopyButton
|
||||
copyItem={copyTemplate}
|
||||
isCopyDisabled={isCopyDisabled}
|
||||
onCopyStart={handleCopyStart}
|
||||
onCopyFinish={handleCopyFinish}
|
||||
errorMessage={i18n._(t`Failed to copy template.`)}
|
||||
/>
|
||||
</ActionItem>
|
||||
</ActionsTd>
|
||||
</Tr>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -26,17 +26,21 @@ const template = {
|
||||
describe('<NotificationTemplateListItem />', () => {
|
||||
test('should render template row', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<NotificationTemplateListItem
|
||||
template={template}
|
||||
detailUrl="/notification_templates/3/detail"
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationTemplateListItem
|
||||
template={template}
|
||||
detailUrl="/notification_templates/3/detail"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
const cells = wrapper.find('DataListCell');
|
||||
expect(cells).toHaveLength(3);
|
||||
expect(cells.at(0).text()).toEqual('Test Notification');
|
||||
expect(cells.at(1).text()).toEqual('Success');
|
||||
expect(cells.at(2).text()).toEqual('Type: Slack');
|
||||
const cells = wrapper.find('Td');
|
||||
expect(cells).toHaveLength(5);
|
||||
expect(cells.at(1).text()).toEqual('Test Notification');
|
||||
expect(cells.at(2).text()).toEqual('Success');
|
||||
expect(cells.at(3).text()).toEqual('Slack');
|
||||
});
|
||||
|
||||
test('should send test notification', async () => {
|
||||
@ -45,10 +49,14 @@ describe('<NotificationTemplateListItem />', () => {
|
||||
});
|
||||
|
||||
const wrapper = mountWithContexts(
|
||||
<NotificationTemplateListItem
|
||||
template={template}
|
||||
detailUrl="/notification_templates/3/detail"
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationTemplateListItem
|
||||
template={template}
|
||||
detailUrl="/notification_templates/3/detail"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
@ -59,8 +67,8 @@ describe('<NotificationTemplateListItem />', () => {
|
||||
expect(NotificationTemplatesAPI.test).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
wrapper
|
||||
.find('DataListCell')
|
||||
.at(1)
|
||||
.find('Td')
|
||||
.at(2)
|
||||
.text()
|
||||
).toEqual('Running');
|
||||
});
|
||||
@ -69,10 +77,14 @@ describe('<NotificationTemplateListItem />', () => {
|
||||
NotificationTemplatesAPI.copy.mockResolvedValue();
|
||||
|
||||
const wrapper = mountWithContexts(
|
||||
<NotificationTemplateListItem
|
||||
template={template}
|
||||
detailUrl="/notification_templates/3/detail"
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationTemplateListItem
|
||||
template={template}
|
||||
detailUrl="/notification_templates/3/detail"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
await act(async () =>
|
||||
@ -86,10 +98,14 @@ describe('<NotificationTemplateListItem />', () => {
|
||||
NotificationTemplatesAPI.copy.mockRejectedValue(new Error());
|
||||
|
||||
const wrapper = mountWithContexts(
|
||||
<NotificationTemplateListItem
|
||||
template={template}
|
||||
detailUrl="/notification_templates/3/detail"
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationTemplateListItem
|
||||
template={template}
|
||||
detailUrl="/notification_templates/3/detail"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
await act(async () =>
|
||||
wrapper.find('Button[aria-label="Copy"]').prop('onClick')()
|
||||
@ -101,18 +117,22 @@ describe('<NotificationTemplateListItem />', () => {
|
||||
|
||||
test('should not render copy button', async () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<NotificationTemplateListItem
|
||||
template={{
|
||||
...template,
|
||||
summary_fields: {
|
||||
user_capabilities: {
|
||||
copy: false,
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
}}
|
||||
detailUrl="/notification_templates/3/detail"
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<NotificationTemplateListItem
|
||||
template={{
|
||||
...template,
|
||||
summary_fields: {
|
||||
user_capabilities: {
|
||||
copy: false,
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
}}
|
||||
detailUrl="/notification_templates/3/detail"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
expect(wrapper.find('CopyButton').length).toBe(0);
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user