Merge pull request #5982 from marshmalien/5866-remove-datalist-alert-modal

Remove DataList component overrides

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
softwarefactory-project-zuul[bot]
2020-02-20 23:17:05 +00:00
committed by GitHub
91 changed files with 1149 additions and 1455 deletions

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<body> <body>
<div id="app"></div> <div id="app" style="height: 100%"></div>
<script src="/bundle.js"></script> <script src="/bundle.js"></script>
</body> </body>
</html> </html>

View File

@@ -204,7 +204,7 @@ class App extends Component {
/> />
<AlertModal <AlertModal
isOpen={configError} isOpen={configError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={this.handleConfigErrorClose} onClose={this.handleConfigErrorClose}
> >

View File

@@ -1,117 +0,0 @@
// https://github.com/patternfly/patternfly-react/issues/1294
#app {
height: 100%;
}
//
// data list overrides
//
.pf-c-data-list {
--pf-global--target-size--MinHeight: 32px;
--pf-global--target-size--MinWidth: 32px;
--pf-global--FontSize--md: 14px;
.pf-c-badge:not(:last-child),
.pf-c-switch:not(:last-child) {
margin-right: 18px;
}
}
.pf-c-data-list__item-row {
--pf-c-data-list__item-row--PaddingRight: 20px;
--pf-c-data-list__item-row--PaddingLeft: 20px;
}
.pf-c-data-list__item-content {
--pf-c-data-list__item-content--PaddingBottom: 16px;
min-height: 59px;
align-items: center;
}
.pf-c-data-list__item-control {
--pf-c-data-list__item-control--PaddingTop: 16px;
--pf-c-data-list__item-control--MarginRight: 8px;
--pf-c-data-list__item-control--PaddingBottom: 16px;
}
.pf-c-data-list__item {
--pf-c-data-list__item--PaddingLeft: 20px;
--pf-c-data-list__item--PaddingRight: 20px;
}
.pf-c-data-list__cell {
--pf-c-data-list__cell--PaddingTop: 16px;
--pf-c-data-list__cell-cell--PaddingTop: 16px;
&.pf-c-data-list__cell--divider {
--pf-c-data-list__cell-cell--MarginRight: 0;
--pf-c-data-list__cell--PaddingTop: 12px;
flex-grow: 0;
}
}
//
// AlertModal styles
//
.at-c-alertModal.pf-c-modal-box {
border: 0;
border-left: 56px solid black;
.at-c-alertModal__icon {
position: absolute;
font-size: 23px;
top: 28px;
left: -39px;
}
}
.at-c-alertModal--warning.pf-c-modal-box {
border-color: var(--pf-global--warning-color--100);
.pf-c-title {
color: var(--pf-global--warning-color--200);
}
.at-c-alertModal__icon {
color: var(--pf-global--warning-color--200);
}
}
.at-c-alertModal--danger.pf-c-modal-box {
border-color: var(--pf-global--danger-color--100);
.pf-c-title {
color: var(--pf-global--danger-color--200);
}
.at-c-alertModal__icon {
color: white;
}
}
.at-c-alertModal--info.pf-c-modal-box {
border-color: var(--pf-global--info-color--100);
.pf-c-title {
color: var(--pf-global--info-color--200);
}
.at-c-alertModal__icon {
color: var(--pf-global--info-color--200);
}
}
.at-c-alertModal--success.pf-c-modal-box {
border-color: var(--pf-global--success-color--100);
.pf-c-title {
color: var(--pf-global--success-color--200);
}
.at-c-alertModal__icon {
color: var(--pf-global--success-color--200);
}
}

View File

@@ -1,42 +1,47 @@
import React from 'react'; import React from 'react';
import { Modal, Title } from '@patternfly/react-core';
import { Modal } from '@patternfly/react-core';
import { import {
ExclamationTriangleIcon,
ExclamationCircleIcon,
InfoCircleIcon,
CheckCircleIcon, CheckCircleIcon,
ExclamationCircleIcon,
ExclamationTriangleIcon,
InfoCircleIcon,
TimesCircleIcon,
} from '@patternfly/react-icons'; } from '@patternfly/react-icons';
import styled from 'styled-components';
const getIcon = variant => { const Header = styled.div`
let icon; display: flex;
if (variant === 'warning') { svg {
icon = <ExclamationTriangleIcon className="at-c-alertModal__icon" />; margin-right: 16px;
} else if (variant === 'danger') {
icon = <ExclamationCircleIcon className="at-c-alertModal__icon" />;
} }
if (variant === 'info') { `;
icon = <InfoCircleIcon className="at-c-alertModal__icon" />;
} export default ({ isOpen = null, title, variant, children, ...props }) => {
if (variant === 'success') { const variantIcons = {
icon = <CheckCircleIcon className="at-c-alertModal__icon" />; danger: <ExclamationCircleIcon size="lg" css="color: #c9190b" />,
} error: <TimesCircleIcon size="lg" css="color: #c9190b" />,
return icon; info: <InfoCircleIcon size="lg" css="color: #73bcf7" />,
}; success: <CheckCircleIcon size="lg" css="color: #92d400" />,
warning: <ExclamationTriangleIcon size="lg" css="color: #f0ab00" />,
};
const customHeader = (
<Header>
{variant ? variantIcons[variant] : null}
<Title size="2xl">{title}</Title>
</Header>
);
export default ({ variant, children, ...props }) => {
const { isOpen = null } = props;
props.isOpen = Boolean(isOpen);
return ( return (
<Modal <Modal
isLarge header={customHeader}
className={`awx-c-modal${variant && isFooterLeftAligned
` at-c-alertModal at-c-alertModal--${variant}`}`} isOpen={Boolean(isOpen)}
isSmall
title={title}
{...props} {...props}
> >
{children} {children}
{getIcon(variant)}
</Modal> </Modal>
); );
}; };

View File

@@ -5,7 +5,9 @@ import AlertModal from './AlertModal';
describe('AlertModal', () => { describe('AlertModal', () => {
test('renders the expected content', () => { test('renders the expected content', () => {
const wrapper = mount(<AlertModal title="Danger!" />); const wrapper = mount(
<AlertModal title="Danger!">Are you sure?</AlertModal>
);
expect(wrapper).toHaveLength(1); expect(wrapper).toHaveLength(1);
}); });
}); });

View File

@@ -6,19 +6,21 @@ import {
DataListItemCells, DataListItemCells,
DataListCell, DataListCell,
DataListCheck, DataListCheck,
Radio,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import DataListRadio from '@components/DataListRadio';
const CheckboxListItem = ({ const CheckboxListItem = ({
isDisabled = false,
isRadio = false,
isSelected = false,
itemId, itemId,
name,
label, label,
isSelected, name,
onSelect,
onDeselect, onDeselect,
isRadio, onSelect,
}) => { }) => {
const CheckboxRadio = isRadio ? DataListRadio : DataListCheck; const CheckboxRadio = isRadio ? Radio : DataListCheck;
return ( return (
<DataListItem <DataListItem
key={itemId} key={itemId}
@@ -27,11 +29,14 @@ const CheckboxListItem = ({
> >
<DataListItemRow> <DataListItemRow>
<CheckboxRadio <CheckboxRadio
id={`selected-${itemId}`} aria-label={`check-action-item-${itemId}`}
checked={isSelected}
onChange={isSelected ? onDeselect : onSelect}
aria-labelledby={`check-action-item-${itemId}`} aria-labelledby={`check-action-item-${itemId}`}
checked={isSelected}
disabled={isDisabled}
id={`selected-${itemId}`}
isChecked={isSelected}
name={name} name={name}
onChange={isSelected ? onDeselect : onSelect}
value={itemId} value={itemId}
/> />
<DataListItemCells <DataListItemCells
@@ -53,12 +58,12 @@ const CheckboxListItem = ({
}; };
CheckboxListItem.propTypes = { CheckboxListItem.propTypes = {
itemId: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
isSelected: PropTypes.bool.isRequired, isSelected: PropTypes.bool.isRequired,
onSelect: PropTypes.func.isRequired, itemId: PropTypes.number.isRequired,
label: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
onDeselect: PropTypes.func.isRequired, onDeselect: PropTypes.func.isRequired,
onSelect: PropTypes.func.isRequired,
}; };
export default CheckboxListItem; export default CheckboxListItem;

View File

@@ -2,9 +2,9 @@ import React, { useState } from 'react';
import { string, func, bool, number } from 'prop-types'; import { string, func, bool, number } from 'prop-types';
import { Button, Split, SplitItem } from '@patternfly/react-core'; import { Button, Split, SplitItem } from '@patternfly/react-core';
import styled from 'styled-components'; import styled from 'styled-components';
import ButtonGroup from '@components/ButtonGroup';
import { yamlToJson, jsonToYaml, isJson } from '@util/yaml'; import { yamlToJson, jsonToYaml, isJson } from '@util/yaml';
import CodeMirrorInput from './CodeMirrorInput'; import CodeMirrorInput from './CodeMirrorInput';
import ButtonGroup from './ButtonGroup';
import { JSON_MODE, YAML_MODE } from './constants'; import { JSON_MODE, YAML_MODE } from './constants';
function formatJson(jsonString) { function formatJson(jsonString) {

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { oneOf, func } from 'prop-types'; import { oneOf, func } from 'prop-types';
import styled from 'styled-components'; import styled from 'styled-components';
import { Button } from '@patternfly/react-core'; import { Button } from '@patternfly/react-core';
import ButtonGroup from '../ButtonGroup'; import ButtonGroup from './ButtonGroup';
const SmallButton = styled(Button)` const SmallButton = styled(Button)`
padding: 3px 8px; padding: 3px 8px;

View File

@@ -1,12 +1,11 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import styled from 'styled-components';
import { Link, Redirect } from 'react-router-dom'; import { Link, Redirect } from 'react-router-dom';
import { bool, instanceOf } from 'prop-types'; import { bool, instanceOf } from 'prop-types';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { import {
Title, Title,
EmptyState as PFEmptyState, EmptyState,
EmptyStateIcon, EmptyStateIcon,
EmptyStateBody, EmptyStateBody,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
@@ -14,11 +13,6 @@ import { ExclamationTriangleIcon } from '@patternfly/react-icons';
import { RootAPI } from '@api'; import { RootAPI } from '@api';
import ErrorDetail from '@components/ErrorDetail'; import ErrorDetail from '@components/ErrorDetail';
const EmptyState = styled(PFEmptyState)`
width: var(--pf-c-empty-state--m-lg--MaxWidth);
margin: 0 auto;
`;
async function logout() { async function logout() {
await RootAPI.logout(); await RootAPI.logout();
window.location.replace('/#/login'); window.location.replace('/#/login');

View File

@@ -1,14 +0,0 @@
import { DataListCell as PFDataListCell } from '@patternfly/react-core';
import styled from 'styled-components';
const DataListCell = styled(PFDataListCell)`
display: flex;
align-items: center;
padding-bottom: ${props => (props.righthalf ? '16px' : '8px')};
@media screen and (min-width: 768px) {
padding-bottom: 0;
justify-content: ${props => (props.lastcolumn ? 'flex-end' : 'inherit')};
}
`;
export default DataListCell;

View File

@@ -1,11 +0,0 @@
import React from 'react';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import DataListCell from './DataListCell';
describe('DataListCell', () => {
test('renders without failing', () => {
const wrapper = mountWithContexts(<DataListCell />);
expect(wrapper).toHaveLength(1);
});
});

View File

@@ -1 +0,0 @@
export { default } from './DataListCell';

View File

@@ -1,15 +0,0 @@
import { DataListCheck as PFDataListCheck } from '@patternfly/react-core';
import styled from 'styled-components';
PFDataListCheck.displayName = 'PFDataListCheck';
export default styled(PFDataListCheck)`
padding-top: 18px;
@media screen and (min-width: 768px) {
padding-top: 16px;
justify-content: ${props => (props.lastcolumn ? 'flex-end' : 'inherit')};
.pf-c-data-list__check {
display: flex;
align-items: center;
}
}
`;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import DataListCheck from './DataListCheck';
describe('DataListCheck', () => {
test('renders the expected content', () => {
const wrapper = mount(<DataListCheck checked aria-labelledby="Checkbox" />);
expect(wrapper).toHaveLength(1);
});
});

View File

@@ -1 +0,0 @@
export { default } from './DataListCheck';

View File

@@ -1,47 +0,0 @@
import * as React from 'react';
import { string, bool, func } from 'prop-types';
function DataListRadio({
className = '',
onChange,
isValid = true,
isDisabled = false,
isChecked = null,
checked = null,
...props
}) {
return (
<div className={`pf-c-data-list__item-control ${className}`}>
<div className="pf-c-data-list__check">
<input
{...props}
type="radio"
onChange={event => onChange(event.currentTarget.checked, event)}
aria-invalid={!isValid}
disabled={isDisabled}
checked={isChecked || checked}
/>
</div>
</div>
);
}
DataListRadio.propTypes = {
className: string,
isValid: bool,
isDisabled: bool,
isChecked: bool,
checked: bool,
onChange: func,
'aria-labelledby': string,
};
DataListRadio.defaultProps = {
className: '',
isValid: true,
isDisabled: false,
isChecked: false,
checked: false,
onChange: () => {},
'aria-labelledby': '',
};
export default DataListRadio;

View File

@@ -1,36 +0,0 @@
import React from 'react';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import DataListRadio from './DataListRadio';
describe('DataListRadio', () => {
test('should call onChange', () => {
const onChange = jest.fn();
const wrapper = mountWithContexts(<DataListRadio onChange={onChange} />);
wrapper.find('input[type="radio"]').prop('onChange')({
currentTarget: { checked: true },
});
expect(onChange).toHaveBeenCalledWith(true, {
currentTarget: { checked: true },
});
});
test('should pass props to correct children', () => {
const onChange = jest.fn();
const wrapper = mountWithContexts(
<DataListRadio
onChange={onChange}
className="foo"
isValid
isDisabled
checked
/>
);
const div = wrapper.find('.pf-c-data-list__item-control');
const input = wrapper.find('input[type="radio"]');
expect(div.prop('className')).toEqual('pf-c-data-list__item-control foo');
expect(input.prop('disabled')).toBe(true);
expect(input.prop('checked')).toBe(true);
expect(input.prop('aria-invalid')).toBe(false);
});
});

View File

@@ -1 +0,0 @@
export { default } from './DataListRadio';

View File

@@ -7,10 +7,10 @@ import styled from 'styled-components';
import { SearchIcon } from '@patternfly/react-icons'; import { SearchIcon } from '@patternfly/react-icons';
import { import {
DataToolbar, DataToolbar,
DataToolbarContent, DataToolbarContent as _DataToolbarContent,
DataToolbarGroup, DataToolbarGroup as _DataToolbarGroup,
DataToolbarToggleGroup,
DataToolbarItem, DataToolbarItem,
DataToolbarToggleGroup,
} from '@patternfly/react-core/dist/umd/experimental'; } from '@patternfly/react-core/dist/umd/experimental';
import ExpandCollapse from '../ExpandCollapse'; import ExpandCollapse from '../ExpandCollapse';
import Search from '../Search'; import Search from '../Search';
@@ -18,27 +18,12 @@ import Sort from '../Sort';
import { SearchColumns, SortColumns, QSConfig } from '@types'; import { SearchColumns, SortColumns, QSConfig } from '@types';
const AdditionalControlsWrapper = styled.div` const DataToolbarContent = styled(_DataToolbarContent)`
display: flex; --pf-c-data-toolbar__content--PaddingLeft: 24px;
flex-grow: 1; --pf-c-data-toolbar__content--PaddingRight: 8px;
justify-content: flex-end;
align-items: center;
& > :not(:first-child) {
margin-left: 20px;
}
`; `;
const DataToolbarGroup = styled(_DataToolbarGroup)`
const AdditionalControlsDataToolbarGroup = styled(DataToolbarGroup)` --pf-c-data-toolbar__group--spacer: 24px;
margin-left: auto;
margin-right: 0 !important;
`;
const DataToolbarSeparator = styled(DataToolbarItem)`
width: 1px !important;
height: 30px !important;
margin-left: 3px !important;
margin-right: 10px !important;
`; `;
class DataListToolbar extends React.Component { class DataListToolbar extends React.Component {
@@ -80,7 +65,6 @@ class DataListToolbar extends React.Component {
id="select-all" id="select-all"
/> />
</DataToolbarItem> </DataToolbarItem>
<DataToolbarSeparator variant="separator" />
</DataToolbarGroup> </DataToolbarGroup>
)} )}
<DataToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="lg"> <DataToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="lg">
@@ -110,13 +94,11 @@ class DataListToolbar extends React.Component {
</Fragment> </Fragment>
)} )}
</DataToolbarGroup> </DataToolbarGroup>
<AdditionalControlsDataToolbarGroup> <DataToolbarGroup css="margin-left: auto">
<DataToolbarItem> {additionalControls.map(control => (
<AdditionalControlsWrapper> <DataToolbarItem key={control.key}>{control}</DataToolbarItem>
{additionalControls} ))}
</AdditionalControlsWrapper> </DataToolbarGroup>
</DataToolbarItem>
</AdditionalControlsDataToolbarGroup>
</DataToolbarContent> </DataToolbarContent>
</DataToolbar> </DataToolbar>
); );

View File

@@ -3,7 +3,6 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Button } from '@patternfly/react-core'; import { Button } from '@patternfly/react-core';
import AlertModal from '@components/AlertModal'; import AlertModal from '@components/AlertModal';
import { CardActionsRow } from '@components/Card';
function DeleteButton({ function DeleteButton({
onConfirm, onConfirm,
@@ -29,26 +28,28 @@ function DeleteButton({
title={modalTitle} title={modalTitle}
variant="danger" variant="danger"
onClose={() => setIsOpen(false)} onClose={() => setIsOpen(false)}
> actions={[
{i18n._(t`Are you sure you want to delete:`)}
<br />
<strong>{name}</strong>
<CardActionsRow>
<Button
variant="secondary"
aria-label={i18n._(t`Cancel`)}
onClick={() => setIsOpen(false)}
>
{i18n._(t`Cancel`)}
</Button>
<Button <Button
key="delete"
variant="danger" variant="danger"
aria-label={i18n._(t`Delete`)} aria-label={i18n._(t`Delete`)}
onClick={onConfirm} onClick={onConfirm}
> >
{i18n._(t`Delete`)} {i18n._(t`Delete`)}
</Button> </Button>,
</CardActionsRow> <Button
key="cancel"
variant="secondary"
aria-label={i18n._(t`Cancel`)}
onClick={() => setIsOpen(false)}
>
{i18n._(t`Cancel`)}
</Button>,
]}
>
{i18n._(t`Are you sure you want to delete:`)}
<br />
<strong>{name}</strong>
</AlertModal> </AlertModal>
</> </>
); );

View File

@@ -128,7 +128,7 @@ class LaunchButton extends React.Component {
{launchError && ( {launchError && (
<AlertModal <AlertModal
isOpen={launchError} isOpen={launchError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={this.handleLaunchErrorClose} onClose={this.handleLaunchErrorClose}
> >

View File

@@ -8,7 +8,6 @@ import { CredentialsAPI, CredentialTypesAPI } from '@api';
import AnsibleSelect from '@components/AnsibleSelect'; import AnsibleSelect from '@components/AnsibleSelect';
import { FieldTooltip } from '@components/FormField'; import { FieldTooltip } from '@components/FormField';
import CredentialChip from '@components/CredentialChip'; import CredentialChip from '@components/CredentialChip';
import VerticalSeperator from '@components/VerticalSeparator';
import { getQSConfig, parseQueryString } from '@util/qs'; import { getQSConfig, parseQueryString } from '@util/qs';
import Lookup from './Lookup'; import Lookup from './Lookup';
import OptionsList from './shared/OptionsList'; import OptionsList from './shared/OptionsList';
@@ -97,8 +96,9 @@ function MultiCredentialsLookup(props) {
<Fragment> <Fragment>
{credentialTypes && credentialTypes.length > 0 && ( {credentialTypes && credentialTypes.length > 0 && (
<ToolbarItem css=" display: flex; align-items: center;"> <ToolbarItem css=" display: flex; align-items: center;">
<div css="flex: 0 0 25%;">{i18n._(t`Selected Category`)}</div> <div css="flex: 0 0 25%; margin-right: 32px">
<VerticalSeperator /> {i18n._(t`Selected Category`)}
</div>
<AnsibleSelect <AnsibleSelect
css="flex: 1 1 75%;" css="flex: 1 1 75%;"
id="multiCredentialsLookUp-select" id="multiCredentialsLookUp-select"

View File

@@ -250,7 +250,7 @@ class NotificationList extends Component {
)} )}
/> />
<AlertModal <AlertModal
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
isOpen={toggleError && !toggleLoading} isOpen={toggleError && !toggleLoading}
onClose={this.handleNotificationErrorClose} onClose={this.handleNotificationErrorClose}

View File

@@ -4,24 +4,20 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { import {
DataListAction as _DataListAction,
DataListCell,
DataListItem, DataListItem,
DataListItemRow,
DataListItemCells, DataListItemCells,
DataListCell as PFDataListCell, DataListItemRow,
Switch, Switch,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import styled from 'styled-components'; import styled from 'styled-components';
const DataListCell = styled(PFDataListCell)` const DataListAction = styled(_DataListAction)`
display: flex; align-items: center;
justify-content: ${props => (props.righthalf ? 'flex-start' : 'inherit')}; display: grid;
padding-bottom: ${props => (props.righthalf ? '16px' : '8px')}; grid-gap: 16px;
grid-template-columns: repeat(3, max-content);
@media screen and (min-width: 768px) {
justify-content: ${props => (props.righthalf ? 'flex-end' : 'inherit')};
padding-bottom: 0;
}
`; `;
function NotificationListItem(props) { function NotificationListItem(props) {
@@ -51,7 +47,6 @@ function NotificationListItem(props) {
to={{ to={{
pathname: detailUrl, pathname: detailUrl,
}} }}
css="margin-right: 1.5em;"
> >
<b id={`items-list-item-${notification.id}`}> <b id={`items-list-item-${notification.id}`}>
{notification.name} {notification.name}
@@ -61,51 +56,47 @@ function NotificationListItem(props) {
<DataListCell key="type"> <DataListCell key="type">
{typeLabels[notification.notification_type]} {typeLabels[notification.notification_type]}
</DataListCell>, </DataListCell>,
<DataListCell righthalf="true" key="toggles">
<Switch
id={`notification-${notification.id}-started-toggle`}
label={i18n._(t`Start`)}
labelOff={i18n._(t`Start`)}
isChecked={startedTurnedOn}
isDisabled={!canToggleNotifications}
onChange={() =>
toggleNotification(
notification.id,
startedTurnedOn,
'started'
)
}
aria-label={i18n._(t`Toggle notification start`)}
/>
<Switch
id={`notification-${notification.id}-success-toggle`}
label={i18n._(t`Success`)}
labelOff={i18n._(t`Success`)}
isChecked={successTurnedOn}
isDisabled={!canToggleNotifications}
onChange={() =>
toggleNotification(
notification.id,
successTurnedOn,
'success'
)
}
aria-label={i18n._(t`Toggle notification success`)}
/>
<Switch
id={`notification-${notification.id}-error-toggle`}
label={i18n._(t`Failure`)}
labelOff={i18n._(t`Failure`)}
isChecked={errorTurnedOn}
isDisabled={!canToggleNotifications}
onChange={() =>
toggleNotification(notification.id, errorTurnedOn, 'error')
}
aria-label={i18n._(t`Toggle notification failure`)}
/>
</DataListCell>,
]} ]}
/> />
<DataListAction
aria-label="actions"
aria-labelledby={`items-list-item-${notification.id}`}
id={`items-list-item-${notification.id}`}
>
<Switch
id={`notification-${notification.id}-started-toggle`}
label={i18n._(t`Start`)}
labelOff={i18n._(t`Start`)}
isChecked={startedTurnedOn}
isDisabled={!canToggleNotifications}
onChange={() =>
toggleNotification(notification.id, startedTurnedOn, 'started')
}
aria-label={i18n._(t`Toggle notification start`)}
/>
<Switch
id={`notification-${notification.id}-success-toggle`}
label={i18n._(t`Success`)}
labelOff={i18n._(t`Success`)}
isChecked={successTurnedOn}
isDisabled={!canToggleNotifications}
onChange={() =>
toggleNotification(notification.id, successTurnedOn, 'success')
}
aria-label={i18n._(t`Toggle notification success`)}
/>
<Switch
id={`notification-${notification.id}-error-toggle`}
label={i18n._(t`Failure`)}
labelOff={i18n._(t`Failure`)}
isChecked={errorTurnedOn}
isDisabled={!canToggleNotifications}
onChange={() =>
toggleNotification(notification.id, errorTurnedOn, 'error')
}
aria-label={i18n._(t`Toggle notification failure`)}
/>
</DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -42,7 +42,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
<DataListItemCells <DataListItemCells
dataListCells={ dataListCells={
Array [ Array [
<ForwardRef> <DataListCell>
<ForwardRef <ForwardRef
to={ to={
Object { Object {
@@ -56,41 +56,10 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
Foo Foo
</b> </b>
</ForwardRef> </ForwardRef>
</ForwardRef>, </DataListCell>,
<ForwardRef> <DataListCell>
Slack Slack
</ForwardRef>, </DataListCell>,
<ForwardRef
righthalf="true"
>
<Unknown
aria-label="Toggle notification start"
id="notification-9000-started-toggle"
isChecked={false}
isDisabled={false}
label="Start"
labelOff="Start"
onChange={[Function]}
/>
<Unknown
aria-label="Toggle notification success"
id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Success"
labelOff="Success"
onChange={[Function]}
/>
<Unknown
aria-label="Toggle notification failure"
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
labelOff="Failure"
onChange={[Function]}
/>
</ForwardRef>,
] ]
} }
key=".0" key=".0"
@@ -99,453 +68,324 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
<div <div
className="pf-c-data-list__item-content" className="pf-c-data-list__item-content"
> >
<NotificationListItem__DataListCell <DataListCell
key="name" key="name"
> >
<StyledComponent <div
forwardedComponent={ className="pf-c-data-list__cell"
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-w674ng-0",
"isStatic": false,
"lastClassName": "dXsFLF",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-w674ng-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
> >
<DataListCell <Link
className="NotificationListItem__DataListCell-w674ng-0 faYgxF" to={
Object {
"pathname": "/foo",
}
}
> >
<div <LinkAnchor
className="pf-c-data-list__cell NotificationListItem__DataListCell-w674ng-0 faYgxF" href="/foo"
navigate={[Function]}
> >
<Styled(Link) <a
to={ href="/foo"
Object { onClick={[Function]}
"pathname": "/foo",
}
}
> >
<StyledComponent <b
forwardedComponent={ id="items-list-item-9000"
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bdVaJa",
"isStatic": true,
"lastClassName": "eBseNd",
"rules": Array [
"margin-right: 1.5em;",
],
},
"displayName": "Styled(Link)",
"foldedComponentIds": Array [],
"propTypes": Object {
"innerRef": [Function],
"onClick": [Function],
"replace": [Function],
"target": [Function],
"to": [Function],
},
"render": [Function],
"styledComponentId": "sc-bdVaJa",
"target": Object {
"$$typeof": Symbol(react.forward_ref),
"displayName": "Link",
"propTypes": Object {
"innerRef": [Function],
"onClick": [Function],
"replace": [Function],
"target": [Function],
"to": [Function],
},
"render": [Function],
},
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
to={
Object {
"pathname": "/foo",
}
}
> >
<Link Foo
className="sc-bdVaJa eBseNd" </b>
to={ </a>
Object { </LinkAnchor>
"pathname": "/foo", </Link>
} </div>
} </DataListCell>
> <DataListCell
<LinkAnchor
className="sc-bdVaJa eBseNd"
href="/foo"
navigate={[Function]}
>
<a
className="sc-bdVaJa eBseNd"
href="/foo"
onClick={[Function]}
>
<b
id="items-list-item-9000"
>
Foo
</b>
</a>
</LinkAnchor>
</Link>
</StyledComponent>
</Styled(Link)>
</div>
</DataListCell>
</StyledComponent>
</NotificationListItem__DataListCell>
<NotificationListItem__DataListCell
key="type" key="type"
> >
<StyledComponent <div
forwardedComponent={ className="pf-c-data-list__cell"
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-w674ng-0",
"isStatic": false,
"lastClassName": "dXsFLF",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-w674ng-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
> >
<DataListCell Slack
className="NotificationListItem__DataListCell-w674ng-0 faYgxF" </div>
> </DataListCell>
<div </div>
className="pf-c-data-list__cell NotificationListItem__DataListCell-w674ng-0 faYgxF" </DataListItemCells>
> <NotificationListItem__DataListAction
Slack aria-label="actions"
</div> aria-labelledby="items-list-item-9000"
</DataListCell> id="items-list-item-9000"
</StyledComponent> key=".1"
</NotificationListItem__DataListCell> rowid="items-list-item-9000"
<NotificationListItem__DataListCell >
key="toggles" <StyledComponent
righthalf="true" aria-label="actions"
aria-labelledby="items-list-item-9000"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListAction-w674ng-0",
"isStatic": true,
"lastClassName": "hhZchj",
"rules": Array [
"align-items:center;display:grid;grid-gap:16px;grid-template-columns:repeat(3,max-content);",
],
},
"displayName": "NotificationListItem__DataListAction",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListAction-w674ng-0",
"target": [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="NotificationListItem__DataListAction-w674ng-0 hhZchj"
id="items-list-item-9000"
rowid="items-list-item-9000"
> >
<StyledComponent <div
forwardedComponent={ className="pf-c-data-list__item-action NotificationListItem__DataListAction-w674ng-0 hhZchj"
Object { rowid="items-list-item-9000"
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-w674ng-0",
"isStatic": false,
"lastClassName": "dXsFLF",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-w674ng-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
righthalf="true"
> >
<DataListCell <Component
className="NotificationListItem__DataListCell-w674ng-0 dXsFLF" aria-label="Toggle notification start"
righthalf="true" id="notification-9000-started-toggle"
isChecked={false}
isDisabled={false}
label="Start"
labelOff="Start"
onChange={[Function]}
> >
<div <ComponentWithOuia
className="pf-c-data-list__cell NotificationListItem__DataListCell-w674ng-0 dXsFLF" component={[Function]}
righthalf="true" componentProps={
Object {
"aria-label": "Toggle notification start",
"id": "notification-9000-started-toggle",
"isChecked": false,
"isDisabled": false,
"label": "Start",
"labelOff": "Start",
"onChange": [Function],
}
}
consumerContext={null}
> >
<Component <Switch
aria-label="Toggle notification start" aria-label="Toggle notification start"
className=""
id="notification-9000-started-toggle" id="notification-9000-started-toggle"
isChecked={false} isChecked={false}
isDisabled={false} isDisabled={false}
label="Start" label="Start"
labelOff="Start" labelOff="Start"
onChange={[Function]} onChange={[Function]}
> ouiaContext={
<ComponentWithOuia Object {
component={[Function]} "isOuia": false,
componentProps={ "ouiaId": null,
Object {
"aria-label": "Toggle notification start",
"id": "notification-9000-started-toggle",
"isChecked": false,
"isDisabled": false,
"label": "Start",
"labelOff": "Start",
"onChange": [Function],
}
} }
consumerContext={null} }
>
<label
className="pf-c-switch"
htmlFor="notification-9000-started-toggle"
> >
<Switch <input
aria-label="Toggle notification start" aria-label="Toggle notification start"
className="" aria-labelledby={null}
checked={false}
className="pf-c-switch__input"
disabled={false}
id="notification-9000-started-toggle" id="notification-9000-started-toggle"
isChecked={false}
isDisabled={false}
label="Start"
labelOff="Start"
onChange={[Function]} onChange={[Function]}
ouiaContext={ type="checkbox"
Object { />
"isOuia": false, <span
"ouiaId": null, className="pf-c-switch__toggle"
} />
} <span
aria-hidden="true"
className="pf-c-switch__label pf-m-on"
id={null}
> >
<label Start
className="pf-c-switch" </span>
htmlFor="notification-9000-started-toggle" <span
> aria-hidden="true"
<input className="pf-c-switch__label pf-m-off"
aria-label="Toggle notification start" id={null}
aria-labelledby={null} >
checked={false} Start
className="pf-c-switch__input" </span>
disabled={false} </label>
id="notification-9000-started-toggle" </Switch>
onChange={[Function]} </ComponentWithOuia>
type="checkbox" </Component>
/> <Component
<span aria-label="Toggle notification success"
className="pf-c-switch__toggle" id="notification-9000-success-toggle"
/> isChecked={false}
<span isDisabled={false}
aria-hidden="true" label="Success"
className="pf-c-switch__label pf-m-on" labelOff="Success"
id={null} onChange={[Function]}
> >
Start <ComponentWithOuia
</span> component={[Function]}
<span componentProps={
aria-hidden="true" Object {
className="pf-c-switch__label pf-m-off" "aria-label": "Toggle notification success",
id={null} "id": "notification-9000-success-toggle",
> "isChecked": false,
Start "isDisabled": false,
</span> "label": "Success",
</label> "labelOff": "Success",
</Switch> "onChange": [Function],
</ComponentWithOuia> }
</Component> }
<Component consumerContext={null}
>
<Switch
aria-label="Toggle notification success" aria-label="Toggle notification success"
className=""
id="notification-9000-success-toggle" id="notification-9000-success-toggle"
isChecked={false} isChecked={false}
isDisabled={false} isDisabled={false}
label="Success" label="Success"
labelOff="Success" labelOff="Success"
onChange={[Function]} onChange={[Function]}
> ouiaContext={
<ComponentWithOuia Object {
component={[Function]} "isOuia": false,
componentProps={ "ouiaId": null,
Object {
"aria-label": "Toggle notification success",
"id": "notification-9000-success-toggle",
"isChecked": false,
"isDisabled": false,
"label": "Success",
"labelOff": "Success",
"onChange": [Function],
}
} }
consumerContext={null} }
>
<label
className="pf-c-switch"
htmlFor="notification-9000-success-toggle"
> >
<Switch <input
aria-label="Toggle notification success" aria-label="Toggle notification success"
className="" aria-labelledby={null}
checked={false}
className="pf-c-switch__input"
disabled={false}
id="notification-9000-success-toggle" id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Success"
labelOff="Success"
onChange={[Function]} onChange={[Function]}
ouiaContext={ type="checkbox"
Object { />
"isOuia": false, <span
"ouiaId": null, className="pf-c-switch__toggle"
} />
} <span
aria-hidden="true"
className="pf-c-switch__label pf-m-on"
id={null}
> >
<label Success
className="pf-c-switch" </span>
htmlFor="notification-9000-success-toggle" <span
> aria-hidden="true"
<input className="pf-c-switch__label pf-m-off"
aria-label="Toggle notification success" id={null}
aria-labelledby={null} >
checked={false} Success
className="pf-c-switch__input" </span>
disabled={false} </label>
id="notification-9000-success-toggle" </Switch>
onChange={[Function]} </ComponentWithOuia>
type="checkbox" </Component>
/> <Component
<span aria-label="Toggle notification failure"
className="pf-c-switch__toggle" id="notification-9000-error-toggle"
/> isChecked={false}
<span isDisabled={false}
aria-hidden="true" label="Failure"
className="pf-c-switch__label pf-m-on" labelOff="Failure"
id={null} onChange={[Function]}
> >
Success <ComponentWithOuia
</span> component={[Function]}
<span componentProps={
aria-hidden="true" Object {
className="pf-c-switch__label pf-m-off" "aria-label": "Toggle notification failure",
id={null} "id": "notification-9000-error-toggle",
> "isChecked": false,
Success "isDisabled": false,
</span> "label": "Failure",
</label> "labelOff": "Failure",
</Switch> "onChange": [Function],
</ComponentWithOuia> }
</Component> }
<Component consumerContext={null}
>
<Switch
aria-label="Toggle notification failure" aria-label="Toggle notification failure"
className=""
id="notification-9000-error-toggle" id="notification-9000-error-toggle"
isChecked={false} isChecked={false}
isDisabled={false} isDisabled={false}
label="Failure" label="Failure"
labelOff="Failure" labelOff="Failure"
onChange={[Function]} onChange={[Function]}
> ouiaContext={
<ComponentWithOuia Object {
component={[Function]} "isOuia": false,
componentProps={ "ouiaId": null,
Object {
"aria-label": "Toggle notification failure",
"id": "notification-9000-error-toggle",
"isChecked": false,
"isDisabled": false,
"label": "Failure",
"labelOff": "Failure",
"onChange": [Function],
}
} }
consumerContext={null} }
>
<label
className="pf-c-switch"
htmlFor="notification-9000-error-toggle"
> >
<Switch <input
aria-label="Toggle notification failure" aria-label="Toggle notification failure"
className="" aria-labelledby={null}
checked={false}
className="pf-c-switch__input"
disabled={false}
id="notification-9000-error-toggle" id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
labelOff="Failure"
onChange={[Function]} onChange={[Function]}
ouiaContext={ type="checkbox"
Object { />
"isOuia": false, <span
"ouiaId": null, className="pf-c-switch__toggle"
} />
} <span
aria-hidden="true"
className="pf-c-switch__label pf-m-on"
id={null}
> >
<label Failure
className="pf-c-switch" </span>
htmlFor="notification-9000-error-toggle" <span
> aria-hidden="true"
<input className="pf-c-switch__label pf-m-off"
aria-label="Toggle notification failure" id={null}
aria-labelledby={null} >
checked={false} Failure
className="pf-c-switch__input" </span>
disabled={false} </label>
id="notification-9000-error-toggle" </Switch>
onChange={[Function]} </ComponentWithOuia>
type="checkbox" </Component>
/> </div>
<span </DataListAction>
className="pf-c-switch__toggle" </StyledComponent>
/> </NotificationListItem__DataListAction>
<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>
</ComponentWithOuia>
</Component>
</div>
</DataListCell>
</StyledComponent>
</NotificationListItem__DataListCell>
</div>
</DataListItemCells>
</div> </div>
</DataListItemRow> </DataListItemRow>
</li> </li>

View File

@@ -137,6 +137,7 @@ class ToolbarDeleteButton extends React.Component {
render() { render() {
const { itemsToDelete, pluralizedItemName, i18n } = this.props; const { itemsToDelete, pluralizedItemName, i18n } = this.props;
const { isModalOpen } = this.state; const { isModalOpen } = this.state;
const modalTitle = i18n._(t`Delete ${pluralizedItemName}?`);
const isDisabled = const isDisabled =
itemsToDelete.length === 0 || itemsToDelete.some(cannotDelete); itemsToDelete.length === 0 || itemsToDelete.some(cannotDelete);
@@ -161,7 +162,7 @@ class ToolbarDeleteButton extends React.Component {
{isModalOpen && ( {isModalOpen && (
<AlertModal <AlertModal
variant="danger" variant="danger"
title={pluralizedItemName} title={modalTitle}
isOpen={isModalOpen} isOpen={isModalOpen}
onClose={this.handleCancelDelete} onClose={this.handleCancelDelete}
actions={[ actions={[
@@ -183,15 +184,13 @@ class ToolbarDeleteButton extends React.Component {
</Button>, </Button>,
]} ]}
> >
{i18n._(t`Are you sure you want to delete:`)} <div>{i18n._(t`This action will delete the following:`)}</div>
<br />
{itemsToDelete.map(item => ( {itemsToDelete.map(item => (
<span key={item.id}> <span key={item.id}>
<strong>{item.name || item.username}</strong> <strong>{item.name || item.username}</strong>
<br /> <br />
</span> </span>
))} ))}
<br />
</AlertModal> </AlertModal>
)} )}
</Fragment> </Fragment>

View File

@@ -232,7 +232,7 @@ class ResourceAccessList extends React.Component {
)} )}
<AlertModal <AlertModal
isOpen={hasDeletionError} isOpen={hasDeletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={this.handleDeleteErrorClose} onClose={this.handleDeleteErrorClose}
> >

View File

@@ -73,7 +73,7 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
aria-describedby="pf-modal-0" aria-describedby="pf-modal-0"
aria-label="Remove {0} Access" aria-label="Remove {0} Access"
aria-modal="true" aria-modal="true"
class="pf-c-modal-box awx-c-modal at-c-alertModal at-c-alertModal--danger pf-m-lg" class="pf-c-modal-box pf-m-sm"
role="dialog" role="dialog"
> >
<button <button
@@ -96,13 +96,34 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
/> />
</svg> </svg>
</button> </button>
<h1 <div
class="pf-c-title pf-m-2xl" class="pf-c-title"
> >
<div
Remove {0} Access class="AlertModal__Header-sc-9waqvl-0 dYqVFx"
>
</h1> <svg
aria-hidden="true"
class="sc-bdVaJa bkDjEn"
fill="currentColor"
height="2em"
role="img"
style="vertical-align: -0.25em;"
viewBox="0 0 512 512"
width="2em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
transform=""
/>
</svg>
<h1
class="pf-c-title pf-m-2xl"
>
Remove {0} Access
</h1>
</div>
</div>
<div <div
class="pf-c-modal-box__body" class="pf-c-modal-box__body"
id="pf-modal-0" id="pf-modal-0"
@@ -111,24 +132,9 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
<br /> <br />
<br /> <br />
If you only want to remove access for this particular user, please remove them from the team. If you only want to remove access for this particular user, please remove them from the team.
<svg
aria-hidden="true"
class="at-c-alertModal__icon"
fill="currentColor"
height="1em"
role="img"
style="vertical-align: -0.125em;"
viewBox="0 0 512 512"
width="1em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
transform=""
/>
</svg>
</div> </div>
<div <div
class="pf-c-modal-box__footer" class="pf-c-modal-box__footer pf-m-align-left"
> >
<button <button
aria-label="Confirm delete" aria-label="Confirm delete"
@@ -151,12 +157,24 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
</body> </body>
} }
ariaDescribedById="" ariaDescribedById=""
className="awx-c-modal at-c-alertModal at-c-alertModal--danger" className=""
header={
<ForwardRef>
<ForwardRef
size="lg"
/>
<Title
size="2xl"
>
Remove {0} Access
</Title>
</ForwardRef>
}
hideTitle={false} hideTitle={false}
isFooterLeftAligned={false} isFooterLeftAligned={true}
isLarge={true} isLarge={false}
isOpen={true} isOpen={true}
isSmall={false} isSmall={true}
onClose={[Function]} onClose={[Function]}
showClose={true} showClose={true}
title="Remove {0} Access" title="Remove {0} Access"
@@ -174,7 +192,7 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
aria-describedby="pf-modal-0" aria-describedby="pf-modal-0"
aria-label="Remove {0} Access" aria-label="Remove {0} Access"
aria-modal="true" aria-modal="true"
class="pf-c-modal-box awx-c-modal at-c-alertModal at-c-alertModal--danger pf-m-lg" class="pf-c-modal-box pf-m-sm"
role="dialog" role="dialog"
> >
<button <button
@@ -197,13 +215,34 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
/> />
</svg> </svg>
</button> </button>
<h1 <div
class="pf-c-title pf-m-2xl" class="pf-c-title"
> >
<div
Remove {0} Access class="AlertModal__Header-sc-9waqvl-0 dYqVFx"
>
</h1> <svg
aria-hidden="true"
class="sc-bdVaJa bkDjEn"
fill="currentColor"
height="2em"
role="img"
style="vertical-align: -0.25em;"
viewBox="0 0 512 512"
width="2em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
transform=""
/>
</svg>
<h1
class="pf-c-title pf-m-2xl"
>
Remove {0} Access
</h1>
</div>
</div>
<div <div
class="pf-c-modal-box__body" class="pf-c-modal-box__body"
id="pf-modal-0" id="pf-modal-0"
@@ -212,24 +251,9 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
<br /> <br />
<br /> <br />
If you only want to remove access for this particular user, please remove them from the team. If you only want to remove access for this particular user, please remove them from the team.
<svg
aria-hidden="true"
class="at-c-alertModal__icon"
fill="currentColor"
height="1em"
role="img"
style="vertical-align: -0.125em;"
viewBox="0 0 512 512"
width="1em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
transform=""
/>
</svg>
</div> </div>
<div <div
class="pf-c-modal-box__footer" class="pf-c-modal-box__footer pf-m-align-left"
> >
<button <button
aria-label="Confirm delete" aria-label="Confirm delete"
@@ -270,13 +294,25 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
] ]
} }
ariaDescribedById="" ariaDescribedById=""
className="awx-c-modal at-c-alertModal at-c-alertModal--danger" className=""
header={
<ForwardRef>
<ForwardRef
size="lg"
/>
<Title
size="2xl"
>
Remove {0} Access
</Title>
</ForwardRef>
}
hideTitle={false} hideTitle={false}
id="pf-modal-0" id="pf-modal-0"
isFooterLeftAligned={false} isFooterLeftAligned={true}
isLarge={true} isLarge={false}
isOpen={true} isOpen={true}
isSmall={false} isSmall={true}
onClose={[Function]} onClose={[Function]}
showClose={true} showClose={true}
title="Remove {0} Access" title="Remove {0} Access"
@@ -301,10 +337,10 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
className="pf-l-bullseye" className="pf-l-bullseye"
> >
<ModalBox <ModalBox
className="awx-c-modal at-c-alertModal at-c-alertModal--danger" className=""
id="pf-modal-0" id="pf-modal-0"
isLarge={true} isLarge={false}
isSmall={false} isSmall={true}
style={Object {}} style={Object {}}
title="Remove {0} Access" title="Remove {0} Access"
> >
@@ -312,7 +348,7 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
aria-describedby="pf-modal-0" aria-describedby="pf-modal-0"
aria-label="Remove {0} Access" aria-label="Remove {0} Access"
aria-modal="true" aria-modal="true"
className="pf-c-modal-box awx-c-modal at-c-alertModal at-c-alertModal--danger pf-m-lg" className="pf-c-modal-box pf-m-sm"
role="dialog" role="dialog"
style={Object {}} style={Object {}}
> >
@@ -395,23 +431,110 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
</ComponentWithOuia> </ComponentWithOuia>
</Component> </Component>
</ModalBoxCloseButton> </ModalBoxCloseButton>
<ModalBoxHeader <div
hideTitle={false} className="pf-c-title"
> >
<Title <AlertModal__Header>
className="" <StyledComponent
headingLevel="h1" forwardedComponent={
size="2xl" Object {
> "$$typeof": Symbol(react.forward_ref),
<h1 "attrs": Array [],
className="pf-c-title pf-m-2xl" "componentStyle": ComponentStyle {
"componentId": "AlertModal__Header-sc-9waqvl-0",
"isStatic": true,
"lastClassName": "dYqVFx",
"rules": Array [
"display:flex;svg{margin-right:16px;}",
],
},
"displayName": "AlertModal__Header",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "AlertModal__Header-sc-9waqvl-0",
"target": "div",
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
> >
<div
Remove {0} Access className="AlertModal__Header-sc-9waqvl-0 dYqVFx"
>
</h1> <Styled(ExclamationCircleIcon)
</Title> size="lg"
</ModalBoxHeader> >
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bdVaJa",
"isStatic": true,
"lastClassName": "bkDjEn",
"rules": Array [
"color: #c9190b",
],
},
"displayName": "Styled(ExclamationCircleIcon)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bdVaJa",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
size="lg"
>
<ExclamationCircleIcon
className="sc-bdVaJa bkDjEn"
color="currentColor"
noVerticalAlign={false}
size="lg"
title={null}
>
<svg
aria-hidden={true}
aria-labelledby={null}
className="sc-bdVaJa bkDjEn"
fill="currentColor"
height="2em"
role="img"
style={
Object {
"verticalAlign": "-0.25em",
}
}
viewBox="0 0 512 512"
width="2em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
transform=""
/>
</svg>
</ExclamationCircleIcon>
</StyledComponent>
</Styled(ExclamationCircleIcon)>
<Title
size="2xl"
>
<h1
className="pf-c-title pf-m-2xl"
>
Remove {0} Access
</h1>
</Title>
</div>
</StyledComponent>
</AlertModal__Header>
</div>
<ModalBoxBody <ModalBoxBody
id="pf-modal-0" id="pf-modal-0"
> >
@@ -423,41 +546,13 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
<br /> <br />
<br /> <br />
If you only want to remove access for this particular user, please remove them from the team. If you only want to remove access for this particular user, please remove them from the team.
<ExclamationCircleIcon
className="at-c-alertModal__icon"
color="currentColor"
noVerticalAlign={false}
size="sm"
title={null}
>
<svg
aria-hidden={true}
aria-labelledby={null}
className="at-c-alertModal__icon"
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
}
viewBox="0 0 512 512"
width="1em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
transform=""
/>
</svg>
</ExclamationCircleIcon>
</div> </div>
</ModalBoxBody> </ModalBoxBody>
<ModalBoxFooter <ModalBoxFooter
isLeftAligned={false} isLeftAligned={true}
> >
<div <div
className="pf-c-modal-box__footer" className="pf-c-modal-box__footer pf-m-align-left"
> >
<Component <Component
aria-label="Confirm delete" aria-label="Confirm delete"

View File

@@ -7,7 +7,6 @@ import {
SplitItem, SplitItem,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import styled from 'styled-components'; import styled from 'styled-components';
import VerticalSeparator from '../VerticalSeparator';
const Split = styled(PFSplit)` const Split = styled(PFSplit)`
margin: 20px 0px; margin: 20px 0px;
@@ -16,6 +15,7 @@ const Split = styled(PFSplit)`
const SplitLabelItem = styled(SplitItem)` const SplitLabelItem = styled(SplitItem)`
font-weight: bold; font-weight: bold;
margin-right: 32px;
word-break: initial; word-break: initial;
`; `;
@@ -41,7 +41,6 @@ class SelectedList extends Component {
return ( return (
<Split> <Split>
<SplitLabelItem>{label}</SplitLabelItem> <SplitLabelItem>{label}</SplitLabelItem>
<VerticalSeparator />
<SplitItem> <SplitItem>
<ChipGroup numChips={5}> <ChipGroup numChips={5}>
{selected.map(item => {selected.map(item =>

View File

@@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
import { arrayOf, object } from 'prop-types'; import { arrayOf, object } from 'prop-types';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { Link as _Link } from 'react-router-dom'; import { Link as _Link } from 'react-router-dom';
import { StatusIcon } from '@components/Sparkline'; import StatusIcon from '@components/StatusIcon';
import { Tooltip } from '@patternfly/react-core'; import { Tooltip } from '@patternfly/react-core';
import styled from 'styled-components'; import styled from 'styled-components';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
@@ -13,6 +13,10 @@ import { JOB_TYPE_URL_SEGMENTS } from '@constants';
const Link = styled(props => <_Link {...props} />)` const Link = styled(props => <_Link {...props} />)`
margin-right: 5px; margin-right: 5px;
`; `;
const Wrapper = styled.div`
display: inline-flex;
`;
/* eslint-enable react/jsx-pascal-case */ /* eslint-enable react/jsx-pascal-case */
const Sparkline = ({ i18n, jobs }) => { const Sparkline = ({ i18n, jobs }) => {
@@ -32,13 +36,15 @@ const Sparkline = ({ i18n, jobs }) => {
</Fragment> </Fragment>
); );
return jobs.map(job => ( const statusIcons = jobs.map(job => (
<Tooltip position="top" content={generateTooltip(job)} key={job.id}> <Tooltip position="top" content={generateTooltip(job)} key={job.id}>
<Link to={`/jobs/${JOB_TYPE_URL_SEGMENTS[job.type]}/${job.id}`}> <Link to={`/jobs/${JOB_TYPE_URL_SEGMENTS[job.type]}/${job.id}`}>
<StatusIcon status={job.status} /> <StatusIcon status={job.status} />
</Link> </Link>
</Tooltip> </Tooltip>
)); ));
return <Wrapper>{statusIcons}</Wrapper>;
}; };
Sparkline.propTypes = { Sparkline.propTypes = {

View File

@@ -1,2 +1 @@
export { default as Sparkline } from './Sparkline'; export { default } from './Sparkline';
export { default as StatusIcon } from './StatusIcon';

View File

@@ -12,8 +12,12 @@ const Pulse = keyframes`
`; `;
const Wrapper = styled.div` const Wrapper = styled.div`
width: 14px; align-items: center;
display: flex;
flex-flow: column nowrap;
height: 14px; height: 14px;
margin: 5px 0;
width: 14px;
`; `;
const WhiteTop = styled.div` const WhiteTop = styled.div`

View File

@@ -0,0 +1 @@
export { default } from './StatusIcon';

View File

@@ -1,20 +0,0 @@
import React from 'react';
import styled from 'styled-components';
const Separator = styled.span`
display: inline-block;
width: 1px;
height: 30px;
margin-right: 20px;
margin-left: 20px;
background-color: #d7d7d7;
vertical-align: middle;
`;
const VerticalSeparator = () => (
<div>
<Separator />
</div>
);
export default VerticalSeparator;

View File

@@ -1,11 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import VerticalSeparator from './VerticalSeparator';
describe('VerticalSeparator', () => {
test('renders the expected content', () => {
const wrapper = mount(<VerticalSeparator />);
expect(wrapper).toHaveLength(1);
});
});

View File

@@ -1 +0,0 @@
export { default } from './VerticalSeparator';

View File

@@ -4,7 +4,7 @@ import WorkflowActionTooltipItem from './WorkflowActionTooltipItem';
describe('WorkflowActionTooltipItem', () => { describe('WorkflowActionTooltipItem', () => {
test('successfully mounts', () => { test('successfully mounts', () => {
const wrapper = mount(<WorkflowActionTooltipItem />); const wrapper = mount(<WorkflowActionTooltipItem id="node" />);
expect(wrapper).toHaveLength(1); expect(wrapper).toHaveLength(1);
}); });
}); });

View File

@@ -5,7 +5,6 @@ import { I18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import '@patternfly/react-core/dist/styles/base.css'; import '@patternfly/react-core/dist/styles/base.css';
import './app.scss';
import { isAuthenticated } from '@util/auth'; import { isAuthenticated } from '@util/auth';
import Background from '@components/Background'; import Background from '@components/Background';

View File

@@ -158,7 +158,7 @@ function CredentialDetail({ i18n, credential }) {
{deletionError && ( {deletionError && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={() => setDeletionError(null)}
> >

View File

@@ -125,9 +125,9 @@ function CredentialList({ i18n }) {
itemsToDelete={selected} itemsToDelete={selected}
pluralizedItemName={i18n._(t`Credentials`)} pluralizedItemName={i18n._(t`Credentials`)}
/>, />,
canAdd && ( ...(canAdd
<ToolbarAddButton key="add" linkTo="/credentials/add" /> ? [<ToolbarAddButton key="add" linkTo="/credentials/add" />]
), : []),
]} ]}
/> />
)} )}
@@ -135,7 +135,7 @@ function CredentialList({ i18n }) {
</Card> </Card>
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={clearDeletionError} onClose={clearDeletionError}
> >

View File

@@ -54,44 +54,44 @@ describe('<CredentialList />', () => {
test('should check and uncheck the row item', async () => { test('should check and uncheck the row item', async () => {
expect( expect(
wrapper.find('PFDataListCheck[id="select-credential-1"]').props().checked wrapper.find('DataListCheck[id="select-credential-1"]').props().checked
).toBe(false); ).toBe(false);
await act(async () => { await act(async () => {
wrapper wrapper
.find('PFDataListCheck[id="select-credential-1"]') .find('DataListCheck[id="select-credential-1"]')
.invoke('onChange')(true); .invoke('onChange')(true);
}); });
wrapper.update(); wrapper.update();
expect( expect(
wrapper.find('PFDataListCheck[id="select-credential-1"]').props().checked wrapper.find('DataListCheck[id="select-credential-1"]').props().checked
).toBe(true); ).toBe(true);
await act(async () => { await act(async () => {
wrapper wrapper
.find('PFDataListCheck[id="select-credential-1"]') .find('DataListCheck[id="select-credential-1"]')
.invoke('onChange')(false); .invoke('onChange')(false);
}); });
wrapper.update(); wrapper.update();
expect( expect(
wrapper.find('PFDataListCheck[id="select-credential-1"]').props().checked wrapper.find('DataListCheck[id="select-credential-1"]').props().checked
).toBe(false); ).toBe(false);
}); });
test('should check all row items when select all is checked', async () => { test('should check all row items when select all is checked', async () => {
wrapper.find('PFDataListCheck').forEach(el => { wrapper.find('DataListCheck').forEach(el => {
expect(el.props().checked).toBe(false); expect(el.props().checked).toBe(false);
}); });
await act(async () => { await act(async () => {
wrapper.find('Checkbox#select-all').invoke('onChange')(true); wrapper.find('Checkbox#select-all').invoke('onChange')(true);
}); });
wrapper.update(); wrapper.update();
wrapper.find('PFDataListCheck').forEach(el => { wrapper.find('DataListCheck').forEach(el => {
expect(el.props().checked).toBe(true); expect(el.props().checked).toBe(true);
}); });
await act(async () => { await act(async () => {
wrapper.find('Checkbox#select-all').invoke('onChange')(false); wrapper.find('Checkbox#select-all').invoke('onChange')(false);
}); });
wrapper.update(); wrapper.update();
wrapper.find('PFDataListCheck').forEach(el => { wrapper.find('DataListCheck').forEach(el => {
expect(el.props().checked).toBe(false); expect(el.props().checked).toBe(false);
}); });
}); });
@@ -102,7 +102,7 @@ describe('<CredentialList />', () => {
await act(async () => { await act(async () => {
wrapper wrapper
.find('PFDataListCheck[id="select-credential-3"]') .find('DataListCheck[id="select-credential-3"]')
.invoke('onChange')(); .invoke('onChange')();
}); });
wrapper.update(); wrapper.update();
@@ -119,7 +119,7 @@ describe('<CredentialList />', () => {
); );
await act(async () => { await act(async () => {
wrapper wrapper
.find('PFDataListCheck[id="select-credential-2"]') .find('DataListCheck[id="select-credential-2"]')
.invoke('onChange')(); .invoke('onChange')();
}); });
wrapper.update(); wrapper.update();

View File

@@ -5,25 +5,18 @@ import { t } from '@lingui/macro';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { import {
Button, Button,
DataListAction,
DataListCell,
DataListCheck,
DataListItem, DataListItem,
DataListItemRow, DataListItemRow,
DataListItemCells as _DataListItemCells, DataListItemCells,
Tooltip, Tooltip,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { PencilAltIcon } from '@patternfly/react-icons'; import { PencilAltIcon } from '@patternfly/react-icons';
import DataListCell from '@components/DataListCell';
import DataListCheck from '@components/DataListCheck';
import VerticalSeparator from '@components/VerticalSeparator';
import styled from 'styled-components';
import { Credential } from '@types'; import { Credential } from '@types';
const DataListItemCells = styled(_DataListItemCells)`
${DataListCell}:first-child {
flex-grow: 2;
}
`;
function CredentialListItem({ function CredentialListItem({
credential, credential,
detailUrl, detailUrl,
@@ -50,7 +43,6 @@ function CredentialListItem({
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="name"> <DataListCell key="name">
<VerticalSeparator />
<Link to={`${detailUrl}`}> <Link to={`${detailUrl}`}>
<b>{credential.name}</b> <b>{credential.name}</b>
</Link> </Link>
@@ -58,21 +50,25 @@ function CredentialListItem({
<DataListCell key="type"> <DataListCell key="type">
{credential.summary_fields.credential_type.name} {credential.summary_fields.credential_type.name}
</DataListCell>, </DataListCell>,
<DataListCell key="edit" alignRight isFilled={false}>
{canEdit && (
<Tooltip content={i18n._(t`Edit Credential`)} position="top">
<Button
variant="plain"
component={Link}
to={`/credentials/${credential.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListCell>,
]} ]}
/> />
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
{canEdit && (
<Tooltip content={i18n._(t`Edit Credential`)} position="top">
<Button
variant="plain"
component={Link}
to={`/credentials/${credential.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -9,7 +9,7 @@ import AlertModal from '@components/AlertModal';
import ErrorDetail from '@components/ErrorDetail'; import ErrorDetail from '@components/ErrorDetail';
import { DetailList, Detail, UserDateDetail } from '@components/DetailList'; import { DetailList, Detail, UserDateDetail } from '@components/DetailList';
import { VariablesDetail } from '@components/CodeMirrorInput'; import { VariablesDetail } from '@components/CodeMirrorInput';
import { Sparkline } from '@components/Sparkline'; import Sparkline from '@components/Sparkline';
import DeleteButton from '@components/DeleteButton'; import DeleteButton from '@components/DeleteButton';
import { HostsAPI } from '@api'; import { HostsAPI } from '@api';
@@ -69,7 +69,7 @@ function HostDetail({ host, i18n, onUpdateHost }) {
if (toggleError && !toggleLoading) { if (toggleError && !toggleLoading) {
return ( return (
<AlertModal <AlertModal
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
isOpen={toggleError && !toggleLoading} isOpen={toggleError && !toggleLoading}
onClose={() => setToggleError(false)} onClose={() => setToggleError(false)}
@@ -83,7 +83,7 @@ function HostDetail({ host, i18n, onUpdateHost }) {
return ( return (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(false)} onClose={() => setDeletionError(false)}
> >
@@ -107,7 +107,6 @@ function HostDetail({ host, i18n, onUpdateHost }) {
<DetailList gutter="sm"> <DetailList gutter="sm">
<Detail label={i18n._(t`Name`)} value={name} /> <Detail label={i18n._(t`Name`)} value={name} />
<Detail <Detail
css="display: flex; flex: 1;"
value={<Sparkline jobs={recentPlaybookJobs} />} value={<Sparkline jobs={recentPlaybookJobs} />}
label={i18n._(t`Activity`)} label={i18n._(t`Activity`)}
/> />

View File

@@ -224,9 +224,14 @@ class HostsList extends Component {
itemsToDelete={selected} itemsToDelete={selected}
pluralizedItemName={i18n._(t`Hosts`)} pluralizedItemName={i18n._(t`Hosts`)}
/>, />,
canAdd ? ( ...(canAdd
<ToolbarAddButton key="add" linkTo={`${match.url}/add`} /> ? [
) : null, <ToolbarAddButton
key="add"
linkTo={`${match.url}/add`}
/>,
]
: []),
]} ]}
/> />
)} )}
@@ -250,7 +255,7 @@ class HostsList extends Component {
</Card> </Card>
{toggleError && !toggleLoading && ( {toggleError && !toggleLoading && (
<AlertModal <AlertModal
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
isOpen={toggleError && !toggleLoading} isOpen={toggleError && !toggleLoading}
onClose={this.handleHostToggleErrorClose} onClose={this.handleHostToggleErrorClose}
@@ -262,7 +267,7 @@ class HostsList extends Component {
{deletionError && ( {deletionError && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={this.handleDeleteErrorClose} onClose={this.handleDeleteErrorClose}
> >

View File

@@ -4,6 +4,9 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
Button, Button,
DataListAction as _DataListAction,
DataListCell,
DataListCheck,
DataListItem, DataListItem,
DataListItemRow, DataListItemRow,
DataListItemCells, DataListItemCells,
@@ -13,11 +16,16 @@ import {
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { PencilAltIcon } from '@patternfly/react-icons'; import { PencilAltIcon } from '@patternfly/react-icons';
import DataListCell from '@components/DataListCell'; import Sparkline from '@components/Sparkline';
import DataListCheck from '@components/DataListCheck';
import { Sparkline } from '@components/Sparkline';
import VerticalSeparator from '@components/VerticalSeparator';
import { Host } from '@types'; import { Host } from '@types';
import styled from 'styled-components';
const DataListAction = styled(_DataListAction)`
align-items: center;
display: grid;
grid-gap: 24px;
grid-template-columns: min-content 40px;
`;
class HostListItem extends React.Component { class HostListItem extends React.Component {
static propTypes = { static propTypes = {
@@ -56,7 +64,6 @@ class HostListItem extends React.Component {
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="name"> <DataListCell key="name">
<VerticalSeparator />
<Link to={`${detailUrl}`}> <Link to={`${detailUrl}`}>
<b>{host.name}</b> <b>{host.name}</b>
</Link> </Link>
@@ -67,9 +74,7 @@ class HostListItem extends React.Component {
<DataListCell key="inventory"> <DataListCell key="inventory">
{host.summary_fields.inventory && ( {host.summary_fields.inventory && (
<Fragment> <Fragment>
<b style={{ marginRight: '20px' }}> <b css="margin-right: 24px">{i18n._(t`Inventory`)}</b>
{i18n._(t`Inventory`)}
</b>
<Link <Link
to={`/inventories/${ to={`/inventories/${
host.summary_fields.inventory.kind === 'smart' host.summary_fields.inventory.kind === 'smart'
@@ -82,43 +87,44 @@ class HostListItem extends React.Component {
</Fragment> </Fragment>
)} )}
</DataListCell>, </DataListCell>,
<DataListCell key="enable" alignRight isFilled={false}>
<Tooltip
content={i18n._(
t`Indicates if a host is available and should be included in running jobs. For hosts that are part of an external inventory, this may be reset by the inventory sync process.`
)}
position="top"
>
<Switch
css="display: inline-flex;"
id={`host-${host.id}-toggle`}
label={i18n._(t`On`)}
labelOff={i18n._(t`Off`)}
isChecked={host.enabled}
isDisabled={
toggleLoading ||
!host.summary_fields.user_capabilities.edit
}
onChange={() => onToggleHost(host)}
aria-label={i18n._(t`Toggle host`)}
/>
</Tooltip>
</DataListCell>,
<DataListCell key="edit" alignRight isFilled={false}>
{host.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Host`)} position="top">
<Button
variant="plain"
component={Link}
to={`/hosts/${host.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListCell>,
]} ]}
/> />
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
<Tooltip
content={i18n._(
t`Indicates if a host is available and should be included in running jobs. For hosts that are part of an external inventory, this may be reset by the inventory sync process.`
)}
position="top"
>
<Switch
css="display: inline-flex;"
id={`host-${host.id}-toggle`}
label={i18n._(t`On`)}
labelOff={i18n._(t`Off`)}
isChecked={host.enabled}
isDisabled={
toggleLoading || !host.summary_fields.user_capabilities.edit
}
onChange={() => onToggleHost(host)}
aria-label={i18n._(t`Toggle host`)}
/>
</Tooltip>
{host.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Host`)} position="top">
<Button
variant="plain"
component={Link}
to={`/hosts/${host.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -98,7 +98,7 @@ function InventoryGroupDetail({ i18n, inventoryGroup }) {
)} )}
{error && ( {error && (
<AlertModal <AlertModal
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
isOpen={error} isOpen={error}
onClose={() => setError(false)} onClose={() => setError(false)}

View File

@@ -6,18 +6,17 @@ import { Group } from '@types';
import { import {
Button, Button,
DataListAction,
DataListCell,
DataListCheck,
DataListItem, DataListItem,
DataListItemRow,
DataListItemCells, DataListItemCells,
DataListItemRow,
Tooltip, Tooltip,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { PencilAltIcon } from '@patternfly/react-icons'; import { PencilAltIcon } from '@patternfly/react-icons';
import DataListCell from '@components/DataListCell';
import DataListCheck from '@components/DataListCheck';
import VerticalSeparator from '@components/VerticalSeparator';
function InventoryGroupItem({ function InventoryGroupItem({
i18n, i18n,
group, group,
@@ -40,23 +39,26 @@ function InventoryGroupItem({
/> />
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="name">
<VerticalSeparator />
<Link to={`${detailUrl}`} id={labelId}> <Link to={`${detailUrl}`} id={labelId}>
<b>{group.name}</b> <b>{group.name}</b>
</Link> </Link>
</DataListCell>, </DataListCell>,
<DataListCell key="edit" alignRight isFilled={false}>
{group.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Group`)} position="top">
<Button variant="plain" component={Link} to={editUrl}>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListCell>,
]} ]}
/> />
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
{group.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Group`)} position="top">
<Button variant="plain" component={Link} to={editUrl}>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -238,12 +238,14 @@ function InventoryGroupsList({ i18n, location, match }) {
</DeleteButton> </DeleteButton>
</div> </div>
</Tooltip>, </Tooltip>,
canAdd && ( ...(canAdd
<ToolbarAddButton ? [
key="add" <ToolbarAddButton
linkTo={`/inventories/inventory/${inventoryId}/groups/add`} key="add"
/> linkTo={`/inventories/inventory/${inventoryId}/groups/add`}
), />,
]
: []),
]} ]}
/> />
)} )}
@@ -259,7 +261,7 @@ function InventoryGroupsList({ i18n, location, match }) {
{deletionError && ( {deletionError && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={() => setDeletionError(null)}
> >

View File

@@ -98,46 +98,46 @@ describe('<InventoryGroupsList />', () => {
test('should check and uncheck the row item', async () => { test('should check and uncheck the row item', async () => {
expect( expect(
wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked wrapper.find('DataListCheck[id="select-group-1"]').props().checked
).toBe(false); ).toBe(false);
await act(async () => { await act(async () => {
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')( wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')(
true true
); );
}); });
wrapper.update(); wrapper.update();
expect( expect(
wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked wrapper.find('DataListCheck[id="select-group-1"]').props().checked
).toBe(true); ).toBe(true);
await act(async () => { await act(async () => {
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')( wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')(
false false
); );
}); });
wrapper.update(); wrapper.update();
expect( expect(
wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked wrapper.find('DataListCheck[id="select-group-1"]').props().checked
).toBe(false); ).toBe(false);
}); });
test('should check all row items when select all is checked', async () => { test('should check all row items when select all is checked', async () => {
wrapper.find('PFDataListCheck').forEach(el => { wrapper.find('DataListCheck').forEach(el => {
expect(el.props().checked).toBe(false); expect(el.props().checked).toBe(false);
}); });
await act(async () => { await act(async () => {
wrapper.find('Checkbox#select-all').invoke('onChange')(true); wrapper.find('Checkbox#select-all').invoke('onChange')(true);
}); });
wrapper.update(); wrapper.update();
wrapper.find('PFDataListCheck').forEach(el => { wrapper.find('DataListCheck').forEach(el => {
expect(el.props().checked).toBe(true); expect(el.props().checked).toBe(true);
}); });
await act(async () => { await act(async () => {
wrapper.find('Checkbox#select-all').invoke('onChange')(false); wrapper.find('Checkbox#select-all').invoke('onChange')(false);
}); });
wrapper.update(); wrapper.update();
wrapper.find('PFDataListCheck').forEach(el => { wrapper.find('DataListCheck').forEach(el => {
expect(el.props().checked).toBe(false); expect(el.props().checked).toBe(false);
}); });
}); });
@@ -157,7 +157,7 @@ describe('<InventoryGroupsList />', () => {
Promise.reject(new Error()) Promise.reject(new Error())
); );
await act(async () => { await act(async () => {
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')(); wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')();
}); });
wrapper.update(); wrapper.update();
await act(async () => { await act(async () => {
@@ -191,7 +191,7 @@ describe('<InventoryGroupsList />', () => {
}) })
); );
await act(async () => { await act(async () => {
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')(); wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')();
}); });
wrapper.update(); wrapper.update();
await act(async () => { await act(async () => {
@@ -213,7 +213,7 @@ describe('<InventoryGroupsList />', () => {
.find('ModalBoxFooter Button[aria-label="Delete"]') .find('ModalBoxFooter Button[aria-label="Delete"]')
.invoke('onClick')(); .invoke('onClick')();
}); });
await waitForElement(wrapper, { title: 'Error!', variant: 'danger' }); await waitForElement(wrapper, { title: 'Error!', variant: 'error' });
await act(async () => { await act(async () => {
wrapper.find('ModalBoxCloseButton').invoke('onClose')(); wrapper.find('ModalBoxCloseButton').invoke('onClose')();
}); });

View File

@@ -4,20 +4,27 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
Button, Button,
DataListAction as _DataListAction,
DataListCell,
DataListCheck,
DataListItem, DataListItem,
DataListItemRow,
DataListItemCells, DataListItemCells,
DataListItemRow,
Switch, Switch,
Tooltip, Tooltip,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { PencilAltIcon } from '@patternfly/react-icons'; import { PencilAltIcon } from '@patternfly/react-icons';
import Sparkline from '@components/Sparkline';
import DataListCell from '@components/DataListCell';
import DataListCheck from '@components/DataListCheck';
import { Sparkline } from '@components/Sparkline';
import VerticalSeparator from '@components/VerticalSeparator';
import { Host } from '@types'; import { Host } from '@types';
import styled from 'styled-components';
const DataListAction = styled(_DataListAction)`
align-items: center;
display: grid;
grid-gap: 24px;
grid-template-columns: min-content 40px;
`;
function InventoryHostItem(props) { function InventoryHostItem(props) {
const { const {
@@ -50,7 +57,6 @@ function InventoryHostItem(props) {
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="divider">
<VerticalSeparator />
<Link to={`${detailUrl}`}> <Link to={`${detailUrl}`}>
<b>{host.name}</b> <b>{host.name}</b>
</Link> </Link>
@@ -58,41 +64,42 @@ function InventoryHostItem(props) {
<DataListCell key="recentJobs"> <DataListCell key="recentJobs">
<Sparkline jobs={recentPlaybookJobs} /> <Sparkline jobs={recentPlaybookJobs} />
</DataListCell>, </DataListCell>,
<DataListCell key="enable" alignRight isFilled={false}>
<Tooltip
content={i18n._(
t`Indicates if a host is available and should be included
in running jobs. For hosts that are part of an external
inventory, this may be reset by the inventory sync process.`
)}
position="top"
>
<Switch
css="display: inline-flex;"
id={`host-${host.id}-toggle`}
label={i18n._(t`On`)}
labelOff={i18n._(t`Off`)}
isChecked={host.enabled}
isDisabled={
toggleLoading ||
!host.summary_fields.user_capabilities?.edit
}
onChange={() => toggleHost(host)}
aria-label={i18n._(t`Toggle host`)}
/>
</Tooltip>
</DataListCell>,
<DataListCell key="edit" alignRight isFilled={false}>
{host.summary_fields.user_capabilities?.edit && (
<Tooltip content={i18n._(t`Edit Host`)} position="top">
<Button variant="plain" component={Link} to={`${editUrl}`}>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListCell>,
]} ]}
/> />
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
<Tooltip
content={i18n._(
t`Indicates if a host is available and should be included
in running jobs. For hosts that are part of an external
inventory, this may be reset by the inventory sync process.`
)}
position="top"
>
<Switch
css="display: inline-flex;"
id={`host-${host.id}-toggle`}
label={i18n._(t`On`)}
labelOff={i18n._(t`Off`)}
isChecked={host.enabled}
isDisabled={
toggleLoading || !host.summary_fields.user_capabilities?.edit
}
onChange={() => toggleHost(host)}
aria-label={i18n._(t`Toggle host`)}
/>
</Tooltip>
{host.summary_fields.user_capabilities?.edit && (
<Tooltip content={i18n._(t`Edit Host`)} position="top">
<Button variant="plain" component={Link} to={`${editUrl}`}>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -165,12 +165,14 @@ function InventoryHostList({ i18n, location, match }) {
itemsToDelete={selected} itemsToDelete={selected}
pluralizedItemName={i18n._(t`Hosts`)} pluralizedItemName={i18n._(t`Hosts`)}
/>, />,
canAdd && ( ...(canAdd
<ToolbarAddButton ? [
key="add" <ToolbarAddButton
linkTo={`/inventories/inventory/${match.params.id}/hosts/add`} key="add"
/> linkTo={`/inventories/inventory/${match.params.id}/hosts/add`}
), />,
]
: []),
]} ]}
/> />
)} )}
@@ -198,7 +200,7 @@ function InventoryHostList({ i18n, location, match }) {
{toggleError && !toggleLoading && ( {toggleError && !toggleLoading && (
<AlertModal <AlertModal
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
isOpen={toggleError && !toggleLoading} isOpen={toggleError && !toggleLoading}
onClose={() => setToggleError(false)} onClose={() => setToggleError(false)}
@@ -211,7 +213,7 @@ function InventoryHostList({ i18n, location, match }) {
{deletionError && ( {deletionError && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={() => setDeletionError(null)}
> >

View File

@@ -104,48 +104,48 @@ describe('<InventoryHostList />', () => {
test('should check and uncheck the row item', async () => { test('should check and uncheck the row item', async () => {
expect( expect(
wrapper.find('PFDataListCheck[id="select-host-1"]').props().checked wrapper.find('DataListCheck[id="select-host-1"]').props().checked
).toBe(false); ).toBe(false);
await act(async () => { await act(async () => {
wrapper.find('PFDataListCheck[id="select-host-1"]').invoke('onChange')( wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')(
true true
); );
}); });
wrapper.update(); wrapper.update();
expect( expect(
wrapper.find('PFDataListCheck[id="select-host-1"]').props().checked wrapper.find('DataListCheck[id="select-host-1"]').props().checked
).toBe(true); ).toBe(true);
await act(async () => { await act(async () => {
wrapper.find('PFDataListCheck[id="select-host-1"]').invoke('onChange')( wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')(
false false
); );
}); });
wrapper.update(); wrapper.update();
expect( expect(
wrapper.find('PFDataListCheck[id="select-host-1"]').props().checked wrapper.find('DataListCheck[id="select-host-1"]').props().checked
).toBe(false); ).toBe(false);
}); });
test('should check all row items when select all is checked', async () => { test('should check all row items when select all is checked', async () => {
wrapper.find('PFDataListCheck').forEach(el => { wrapper.find('DataListCheck').forEach(el => {
expect(el.props().checked).toBe(false); expect(el.props().checked).toBe(false);
}); });
await act(async () => { await act(async () => {
wrapper.find('Checkbox#select-all').invoke('onChange')(true); wrapper.find('Checkbox#select-all').invoke('onChange')(true);
}); });
wrapper.update(); wrapper.update();
wrapper.find('PFDataListCheck').forEach(el => { wrapper.find('DataListCheck').forEach(el => {
expect(el.props().checked).toBe(true); expect(el.props().checked).toBe(true);
}); });
await act(async () => { await act(async () => {
wrapper.find('Checkbox#select-all').invoke('onChange')(false); wrapper.find('Checkbox#select-all').invoke('onChange')(false);
}); });
wrapper.update(); wrapper.update();
wrapper.find('PFDataListCheck').forEach(el => { wrapper.find('DataListCheck').forEach(el => {
expect(el.props().checked).toBe(false); expect(el.props().checked).toBe(false);
}); });
}); });
@@ -186,7 +186,7 @@ describe('<InventoryHostList />', () => {
test('delete button is disabled if user does not have delete capabilities on a selected host', async () => { test('delete button is disabled if user does not have delete capabilities on a selected host', async () => {
await act(async () => { await act(async () => {
wrapper.find('PFDataListCheck[id="select-host-3"]').invoke('onChange')(); wrapper.find('DataListCheck[id="select-host-3"]').invoke('onChange')();
}); });
wrapper.update(); wrapper.update();
expect(wrapper.find('ToolbarDeleteButton button').props().disabled).toBe( expect(wrapper.find('ToolbarDeleteButton button').props().disabled).toBe(
@@ -197,7 +197,7 @@ describe('<InventoryHostList />', () => {
test('should call api delete hosts for each selected host', async () => { test('should call api delete hosts for each selected host', async () => {
HostsAPI.destroy = jest.fn(); HostsAPI.destroy = jest.fn();
await act(async () => { await act(async () => {
wrapper.find('PFDataListCheck[id="select-host-1"]').invoke('onChange')(); wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')();
}); });
wrapper.update(); wrapper.update();
await act(async () => { await act(async () => {
@@ -220,7 +220,7 @@ describe('<InventoryHostList />', () => {
}) })
); );
await act(async () => { await act(async () => {
wrapper.find('PFDataListCheck[id="select-host-1"]').invoke('onChange')(); wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')();
}); });
wrapper.update(); wrapper.update();
await act(async () => { await act(async () => {
@@ -242,7 +242,7 @@ describe('<InventoryHostList />', () => {
Promise.reject(new Error()) Promise.reject(new Error())
); );
await act(async () => { await act(async () => {
wrapper.find('PFDataListCheck[id="select-host-1"]').invoke('onChange')(); wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')();
}); });
wrapper.update(); wrapper.update();
await act(async () => { await act(async () => {

View File

@@ -208,7 +208,7 @@ class InventoriesList extends Component {
itemsToDelete={selected} itemsToDelete={selected}
pluralizedItemName="Inventories" pluralizedItemName="Inventories"
/>, />,
canAdd && addButton, ...(canAdd ? [addButton] : []),
]} ]}
/> />
)} )}
@@ -231,7 +231,7 @@ class InventoriesList extends Component {
</Card> </Card>
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={this.handleDeleteErrorClose} onClose={this.handleDeleteErrorClose}
> >

View File

@@ -3,18 +3,18 @@ import { string, bool, func } from 'prop-types';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { import {
Button, Button,
DataListAction,
DataListCell,
DataListCheck,
DataListItem, DataListItem,
DataListItemRow,
DataListItemCells, DataListItemCells,
DataListItemRow,
Tooltip, Tooltip,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { PencilAltIcon } from '@patternfly/react-icons'; import { PencilAltIcon } from '@patternfly/react-icons';
import DataListCell from '@components/DataListCell';
import DataListCheck from '@components/DataListCheck';
import VerticalSeparator from '@components/VerticalSeparator';
import { Inventory } from '@types'; import { Inventory } from '@types';
class InventoryListItem extends React.Component { class InventoryListItem extends React.Component {
@@ -44,7 +44,6 @@ class InventoryListItem extends React.Component {
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="divider">
<VerticalSeparator />
<Link to={`${detailUrl}`}> <Link to={`${detailUrl}`}>
<b>{inventory.name}</b> <b>{inventory.name}</b>
</Link> </Link>
@@ -54,25 +53,27 @@ class InventoryListItem extends React.Component {
? i18n._(t`Smart Inventory`) ? i18n._(t`Smart Inventory`)
: i18n._(t`Inventory`)} : i18n._(t`Inventory`)}
</DataListCell>, </DataListCell>,
<DataListCell key="edit" alignRight isFilled={false}>
{inventory.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Inventory`)} position="top">
<Button
variant="plain"
component={Link}
to={`/inventories/${
inventory.kind === 'smart'
? 'smart_inventory'
: 'inventory'
}/${inventory.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListCell>,
]} ]}
/> />
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
{inventory.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Inventory`)} position="top">
<Button
variant="plain"
component={Link}
to={`/inventories/${
inventory.kind === 'smart' ? 'smart_inventory' : 'inventory'
}/${inventory.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -27,7 +27,7 @@ const InventoryGroupsDeleteModal = ({
isOpen={isModalOpen} isOpen={isModalOpen}
variant="danger" variant="danger"
title={ title={
groups.length > 1 ? i18n._(t`Delete Groups`) : i18n._(t`Delete Group`) groups.length > 1 ? i18n._(t`Delete Groups?`) : i18n._(t`Delete Group?`)
} }
onClose={onClose} onClose={onClose}
actions={[ actions={[
@@ -60,7 +60,7 @@ const InventoryGroupsDeleteModal = ({
return <ListItem key={group.id}>{group.name}</ListItem>; return <ListItem key={group.id}>{group.name}</ListItem>;
})} })}
</div> </div>
<div css="padding-left: 1px;"> <div>
<Radio <Radio
id="radio-delete" id="radio-delete"
key="radio-delete" key="radio-delete"

View File

@@ -13,7 +13,7 @@ import { VariablesInput as _VariablesInput } from '@components/CodeMirrorInput';
import DeleteButton from '@components/DeleteButton'; import DeleteButton from '@components/DeleteButton';
import ErrorDetail from '@components/ErrorDetail'; import ErrorDetail from '@components/ErrorDetail';
import LaunchButton from '@components/LaunchButton'; import LaunchButton from '@components/LaunchButton';
import { StatusIcon } from '@components/Sparkline'; import StatusIcon from '@components/StatusIcon';
import { toTitleCase } from '@util/strings'; import { toTitleCase } from '@util/strings';
import { formatDateString } from '@util/dates'; import { formatDateString } from '@util/dates';
import { Job } from '@types'; import { Job } from '@types';
@@ -275,7 +275,7 @@ function JobDetail({ job, i18n }) {
{errorMsg && ( {errorMsg && (
<AlertModal <AlertModal
isOpen={errorMsg} isOpen={errorMsg}
variant="danger" variant="error"
onClose={() => setErrorMsg()} onClose={() => setErrorMsg()}
title={i18n._(t`Job Delete Error`)} title={i18n._(t`Job Delete Error`)}
> >

View File

@@ -236,7 +236,7 @@ function JobList({ i18n }) {
</Card> </Card>
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={clearDeletionError} onClose={clearDeletionError}
> >

View File

@@ -23,6 +23,7 @@ const mockResults = [
summary_fields: { summary_fields: {
user_capabilities: { user_capabilities: {
delete: true, delete: true,
start: true,
}, },
}, },
}, },
@@ -34,6 +35,7 @@ const mockResults = [
summary_fields: { summary_fields: {
user_capabilities: { user_capabilities: {
delete: true, delete: true,
start: true,
}, },
}, },
}, },
@@ -45,6 +47,7 @@ const mockResults = [
summary_fields: { summary_fields: {
user_capabilities: { user_capabilities: {
delete: true, delete: true,
start: true,
}, },
}, },
}, },
@@ -56,6 +59,7 @@ const mockResults = [
summary_fields: { summary_fields: {
user_capabilities: { user_capabilities: {
delete: true, delete: true,
start: true,
}, },
}, },
}, },
@@ -67,6 +71,7 @@ const mockResults = [
summary_fields: { summary_fields: {
user_capabilities: { user_capabilities: {
delete: true, delete: true,
edit: true,
}, },
}, },
}, },
@@ -78,6 +83,7 @@ const mockResults = [
summary_fields: { summary_fields: {
user_capabilities: { user_capabilities: {
delete: true, delete: true,
edit: true,
}, },
}, },
}, },

View File

@@ -1,10 +1,10 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import styled from 'styled-components';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
Button, Button,
DataListAction,
DataListCell, DataListCell,
DataListCheck, DataListCheck,
DataListItem, DataListItem,
@@ -14,32 +14,31 @@ import {
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { RocketIcon } from '@patternfly/react-icons'; import { RocketIcon } from '@patternfly/react-icons';
import LaunchButton from '@components/LaunchButton'; import LaunchButton from '@components/LaunchButton';
import { StatusIcon } from '@components/Sparkline'; import StatusIcon from '@components/StatusIcon';
import { toTitleCase } from '@util/strings'; import { toTitleCase } from '@util/strings';
import { formatDateString } from '@util/dates'; import { formatDateString } from '@util/dates';
import { JOB_TYPE_URL_SEGMENTS } from '@constants'; import { JOB_TYPE_URL_SEGMENTS } from '@constants';
const PaddedIcon = styled(StatusIcon)`
margin-right: 20px;
`;
class JobListItem extends Component { class JobListItem extends Component {
render() { render() {
const { i18n, job, isSelected, onSelect } = this.props; const { i18n, job, isSelected, onSelect } = this.props;
const labelId = `check-action-${job.id}`;
return ( return (
<DataListItem aria-labelledby={`check-action-${job.id}`} id={`${job.id}`}> <DataListItem aria-labelledby={labelId} id={`${job.id}`}>
<DataListItemRow> <DataListItemRow>
<DataListCheck <DataListCheck
id={`select-job-${job.id}`} id={`select-job-${job.id}`}
checked={isSelected} checked={isSelected}
onChange={onSelect} onChange={onSelect}
aria-labelledby={`check-action-${job.id}`} aria-labelledby={labelId}
/> />
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="status" isFilled={false}>
{job.status && <PaddedIcon status={job.status} />} {job.status && <StatusIcon status={job.status} />}
</DataListCell>,
<DataListCell key="name" css="display: inline-flex;">
<span> <span>
<Link <Link
to={`/jobs/${JOB_TYPE_URL_SEGMENTS[job.type]}/${job.id}`} to={`/jobs/${JOB_TYPE_URL_SEGMENTS[job.type]}/${job.id}`}
@@ -54,22 +53,26 @@ class JobListItem extends Component {
<DataListCell key="finished"> <DataListCell key="finished">
{formatDateString(job.finished)} {formatDateString(job.finished)}
</DataListCell>, </DataListCell>,
<DataListCell isFilled={false} alignRight key="relaunch">
{job.type !== 'system_job' &&
job.summary_fields.user_capabilities.start && (
<Tooltip content={i18n._(t`Relaunch Job`)} position="top">
<LaunchButton resource={job}>
{({ handleRelaunch }) => (
<Button variant="plain" onClick={handleRelaunch}>
<RocketIcon />
</Button>
)}
</LaunchButton>
</Tooltip>
)}
</DataListCell>,
]} ]}
/> />
{job.type !== 'system_job' &&
job.summary_fields?.user_capabilities?.start && (
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
<Tooltip content={i18n._(t`Relaunch Job`)} position="top">
<LaunchButton resource={job}>
{({ handleRelaunch }) => (
<Button variant="plain" onClick={handleRelaunch}>
<RocketIcon />
</Button>
)}
</LaunchButton>
</Tooltip>
</DataListAction>
)}
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -4,7 +4,7 @@ import CodeMirrorInput from '@components/CodeMirrorInput';
import ContentEmpty from '@components/ContentEmpty'; import ContentEmpty from '@components/ContentEmpty';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { DetailList, Detail } from '@components/DetailList'; import { DetailList, Detail } from '@components/DetailList';
import { StatusIcon } from '@components/Sparkline'; import StatusIcon from '@components/StatusIcon';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import styled from 'styled-components'; import styled from 'styled-components';

View File

@@ -16,7 +16,7 @@ import { CardBody } from '@components/Card';
import ContentError from '@components/ContentError'; import ContentError from '@components/ContentError';
import ContentLoading from '@components/ContentLoading'; import ContentLoading from '@components/ContentLoading';
import ErrorDetail from '@components/ErrorDetail'; import ErrorDetail from '@components/ErrorDetail';
import { StatusIcon } from '@components/Sparkline'; import StatusIcon from '@components/StatusIcon';
import JobEvent from './JobEvent'; import JobEvent from './JobEvent';
import JobEventSkeleton from './JobEventSkeleton'; import JobEventSkeleton from './JobEventSkeleton';

View File

@@ -9,7 +9,6 @@ import {
TrashAltIcon, TrashAltIcon,
} from '@patternfly/react-icons'; } from '@patternfly/react-icons';
import { Badge as PFBadge, Button, Tooltip } from '@patternfly/react-core'; import { Badge as PFBadge, Button, Tooltip } from '@patternfly/react-core';
import VerticalSeparator from '@components/VerticalSeparator';
import DeleteButton from '@components/DeleteButton'; import DeleteButton from '@components/DeleteButton';
import LaunchButton from '@components/LaunchButton'; import LaunchButton from '@components/LaunchButton';
@@ -123,8 +122,6 @@ const OutputToolbar = ({ i18n, job, onDelete }) => {
</Tooltip> </Tooltip>
</BadgeGroup> </BadgeGroup>
<VerticalSeparator />
{job.type !== 'system_job' && {job.type !== 'system_job' &&
job.summary_fields.user_capabilities?.start && ( job.summary_fields.user_capabilities?.start && (
<Tooltip content={i18n._(t`Relaunch Job`)}> <Tooltip content={i18n._(t`Relaunch Job`)}>

View File

@@ -5,7 +5,7 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import styled from 'styled-components'; import styled from 'styled-components';
import { func, shape } from 'prop-types'; import { func, shape } from 'prop-types';
import { StatusIcon } from '@components/Sparkline'; import StatusIcon from '@components/StatusIcon';
import { WorkflowNodeTypeLetter } from '@components/Workflow'; import { WorkflowNodeTypeLetter } from '@components/Workflow';
import { secondsToHHMMSS } from '@util/dates'; import { secondsToHHMMSS } from '@util/dates';
import { constants as wfConstants } from '@components/Workflow/WorkflowUtils'; import { constants as wfConstants } from '@components/Workflow/WorkflowUtils';

View File

@@ -8,12 +8,11 @@ import { t } from '@lingui/macro';
import { shape } from 'prop-types'; import { shape } from 'prop-types';
import { Badge as PFBadge, Button, Tooltip } from '@patternfly/react-core'; import { Badge as PFBadge, Button, Tooltip } from '@patternfly/react-core';
import { CompassIcon, WrenchIcon } from '@patternfly/react-icons'; import { CompassIcon, WrenchIcon } from '@patternfly/react-icons';
import { StatusIcon } from '@components/Sparkline'; import StatusIcon from '@components/StatusIcon';
import VerticalSeparator from '@components/VerticalSeparator';
import styled from 'styled-components'; import styled from 'styled-components';
const Toolbar = styled.div` const Toolbar = styled.div`
align-items: center align-items: center;
border-bottom: 1px solid grey; border-bottom: 1px solid grey;
display: flex; display: flex;
height: 56px; height: 56px;
@@ -73,7 +72,6 @@ function WorkflowOutputToolbar({ i18n, job }) {
<ToolbarActions> <ToolbarActions>
<div>{i18n._(t`Total Nodes`)}</div> <div>{i18n._(t`Total Nodes`)}</div>
<Badge isRead>{totalNodes}</Badge> <Badge isRead>{totalNodes}</Badge>
<VerticalSeparator />
<Tooltip content={i18n._(t`Toggle Legend`)} position="bottom"> <Tooltip content={i18n._(t`Toggle Legend`)} position="bottom">
<ActionButton <ActionButton
id="workflow-output-toggle-legend" id="workflow-output-toggle-legend"

View File

@@ -132,7 +132,7 @@ function OrganizationDetail({ i18n, organization }) {
{deletionError && ( {deletionError && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={() => setDeletionError(null)}
> >

View File

@@ -146,9 +146,9 @@ function OrganizationsList({ i18n }) {
itemsToDelete={selected} itemsToDelete={selected}
pluralizedItemName="Organizations" pluralizedItemName="Organizations"
/>, />,
canAdd ? ( ...(canAdd
<ToolbarAddButton key="add" linkTo={addUrl} /> ? [<ToolbarAddButton key="add" linkTo={addUrl} />]
) : null, : []),
]} ]}
/> />
)} )}
@@ -169,7 +169,7 @@ function OrganizationsList({ i18n }) {
</PageSection> </PageSection>
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={clearDeletionError} onClose={clearDeletionError}
> >

View File

@@ -23,6 +23,7 @@ const mockOrganizations = {
}, },
user_capabilities: { user_capabilities: {
delete: true, delete: true,
edit: true,
}, },
}, },
}, },
@@ -37,6 +38,7 @@ const mockOrganizations = {
}, },
user_capabilities: { user_capabilities: {
delete: true, delete: true,
edit: true,
}, },
}, },
}, },
@@ -51,6 +53,7 @@ const mockOrganizations = {
}, },
user_capabilities: { user_capabilities: {
delete: true, delete: true,
edit: true,
}, },
}, },
}, },

View File

@@ -5,37 +5,29 @@ import { t } from '@lingui/macro';
import { import {
Badge as PFBadge, Badge as PFBadge,
Button, Button,
DataListAction,
DataListCell,
DataListCheck,
DataListItem, DataListItem,
DataListItemRow,
DataListItemCells, DataListItemCells,
DataListItemRow,
Tooltip, Tooltip,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import styled from 'styled-components'; import styled from 'styled-components';
import { PencilAltIcon } from '@patternfly/react-icons'; import { PencilAltIcon } from '@patternfly/react-icons';
import DataListCell from '@components/DataListCell';
import DataListCheck from '@components/DataListCheck';
import VerticalSeparator from '@components/VerticalSeparator';
import { Organization } from '@types'; import { Organization } from '@types';
const Badge = styled(PFBadge)` const Badge = styled(PFBadge)`
align-items: center; margin-left: 8px;
display: flex;
justify-content: center;
margin-left: 10px;
`; `;
const ListGroup = styled.span` const ListGroup = styled.span`
display: flex; margin-left: 24px;
margin-left: 40px;
@media screen and (min-width: 768px) { &:first-of-type {
margin-left: 20px; margin-left: 0;
&:first-of-type {
margin-left: 0;
}
} }
`; `;
@@ -63,7 +55,6 @@ function OrganizationListItem({
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="divider">
<VerticalSeparator />
<span id={labelId}> <span id={labelId}>
<Link to={`${detailUrl}`}> <Link to={`${detailUrl}`}>
<b>{organization.name}</b> <b>{organization.name}</b>
@@ -84,21 +75,25 @@ function OrganizationListItem({
</Badge> </Badge>
</ListGroup> </ListGroup>
</DataListCell>, </DataListCell>,
<DataListCell key="edit" alignRight isFilled={false}>
{organization.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Organization`)} position="top">
<Button
variant="plain"
component={Link}
to={`/organizations/${organization.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListCell>,
]} ]}
/> />
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
{organization.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Organization`)} position="top">
<Button
variant="plain"
component={Link}
to={`/organizations/${organization.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -177,7 +177,7 @@ function ProjectDetail({ project, i18n }) {
{deletionError && ( {deletionError && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={() => setDeletionError(null)}
> >

View File

@@ -206,9 +206,14 @@ class ProjectsList extends Component {
itemsToDelete={selected} itemsToDelete={selected}
pluralizedItemName={i18n._(t`Projects`)} pluralizedItemName={i18n._(t`Projects`)}
/>, />,
canAdd ? ( ...(canAdd
<ToolbarAddButton key="add" linkTo={`${match.url}/add`} /> ? [
) : null, <ToolbarAddButton
key="add"
linkTo={`${match.url}/add`}
/>,
]
: []),
]} ]}
/> />
)} )}
@@ -231,7 +236,7 @@ class ProjectsList extends Component {
</PageSection> </PageSection>
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={this.handleDeleteErrorClose} onClose={this.handleDeleteErrorClose}
> >

View File

@@ -3,6 +3,9 @@ import { string, bool, func } from 'prop-types';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { import {
Button, Button,
DataListAction as _DataListAction,
DataListCell,
DataListCheck,
DataListItem, DataListItem,
DataListItemRow, DataListItemRow,
DataListItemCells, DataListItemCells,
@@ -11,16 +14,20 @@ import {
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { PencilAltIcon, SyncIcon } from '@patternfly/react-icons'; import { PencilAltIcon, SyncIcon } from '@patternfly/react-icons';
import styled from 'styled-components';
import ClipboardCopyButton from '@components/ClipboardCopyButton'; import ClipboardCopyButton from '@components/ClipboardCopyButton';
import DataListCell from '@components/DataListCell';
import DataListCheck from '@components/DataListCheck';
import ProjectSyncButton from '../shared/ProjectSyncButton'; import ProjectSyncButton from '../shared/ProjectSyncButton';
import { StatusIcon } from '@components/Sparkline'; import StatusIcon from '@components/StatusIcon';
import VerticalSeparator from '@components/VerticalSeparator';
import { toTitleCase } from '@util/strings'; import { toTitleCase } from '@util/strings';
import { Project } from '@types'; import { Project } from '@types';
const DataListAction = styled(_DataListAction)`
align-items: center;
display: grid;
grid-gap: 16px;
grid-template-columns: repeat(2, 40px);
`;
class ProjectListItem extends React.Component { class ProjectListItem extends React.Component {
static propTypes = { static propTypes = {
project: Project.isRequired, project: Project.isRequired,
@@ -73,8 +80,7 @@ class ProjectListItem extends React.Component {
/> />
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="status" isFilled={false}>
<VerticalSeparator />
{project.summary_fields.last_job && ( {project.summary_fields.last_job && (
<Tooltip <Tooltip
position="top" position="top"
@@ -92,11 +98,9 @@ class ProjectListItem extends React.Component {
</Link> </Link>
</Tooltip> </Tooltip>
)} )}
<Link </DataListCell>,
id={labelId} <DataListCell key="name">
to={`${detailUrl}`} <Link id={labelId} to={`${detailUrl}`}>
css={{ marginLeft: '10px' }}
>
<b>{project.name}</b> <b>{project.name}</b>
</Link> </Link>
</DataListCell>, </DataListCell>,
@@ -105,7 +109,7 @@ class ProjectListItem extends React.Component {
? i18n._(t`Manual`) ? i18n._(t`Manual`)
: toTitleCase(project.scm_type)} : toTitleCase(project.scm_type)}
</DataListCell>, </DataListCell>,
<DataListCell alignRight isFilled={false} key="revision"> <DataListCell key="revision">
{project.scm_revision.substring(0, 7)} {project.scm_revision.substring(0, 7)}
{project.scm_revision ? ( {project.scm_revision ? (
<ClipboardCopyButton <ClipboardCopyButton
@@ -115,34 +119,41 @@ class ProjectListItem extends React.Component {
/> />
) : null} ) : null}
</DataListCell>, </DataListCell>,
<DataListCell alignRight isFilled={false} key="sync">
{project.summary_fields.user_capabilities.start && (
<Tooltip content={i18n._(t`Sync Project`)} position="top">
<ProjectSyncButton projectId={project.id}>
{handleSync => (
<Button variant="plain" onClick={handleSync}>
<SyncIcon />
</Button>
)}
</ProjectSyncButton>
</Tooltip>
)}
</DataListCell>,
<DataListCell key="edit" alignRight isFilled={false}>
{project.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Project`)} position="top">
<Button
variant="plain"
component={Link}
to={`/projects/${project.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListCell>,
]} ]}
/> />
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
{project.summary_fields.user_capabilities.start && (
<Tooltip content={i18n._(t`Sync Project`)} position="top">
<ProjectSyncButton projectId={project.id}>
{handleSync => (
<Button
css="grid-column: 1"
variant="plain"
onClick={handleSync}
>
<SyncIcon />
</Button>
)}
</ProjectSyncButton>
</Tooltip>
)}
{project.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Project`)} position="top">
<Button
css="grid-column: 2"
variant="plain"
component={Link}
to={`/projects/${project.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -54,7 +54,7 @@ class ProjectSyncButton extends React.Component {
{syncError && ( {syncError && (
<AlertModal <AlertModal
isOpen={syncError} isOpen={syncError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={this.handleSyncErrorClose} onClose={this.handleSyncErrorClose}
> >

View File

@@ -83,7 +83,7 @@ function TeamDetail({ team, i18n }) {
{deletionError && ( {deletionError && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={() => setDeletionError(null)}
> >

View File

@@ -193,9 +193,14 @@ class TeamsList extends Component {
itemsToDelete={selected} itemsToDelete={selected}
pluralizedItemName={i18n._(t`Teams`)} pluralizedItemName={i18n._(t`Teams`)}
/>, />,
canAdd ? ( ...(canAdd
<ToolbarAddButton key="add" linkTo={`${match.url}/add`} /> ? [
) : null, <ToolbarAddButton
key="add"
linkTo={`${match.url}/add`}
/>,
]
: []),
]} ]}
/> />
)} )}
@@ -218,7 +223,7 @@ class TeamsList extends Component {
</PageSection> </PageSection>
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={this.handleDeleteErrorClose} onClose={this.handleDeleteErrorClose}
> >

View File

@@ -17,6 +17,7 @@ const mockAPITeamsList = {
summary_fields: { summary_fields: {
user_capabilities: { user_capabilities: {
delete: true, delete: true,
edit: true,
}, },
}, },
}, },
@@ -27,6 +28,7 @@ const mockAPITeamsList = {
summary_fields: { summary_fields: {
user_capabilities: { user_capabilities: {
delete: true, delete: true,
edit: true,
}, },
}, },
}, },
@@ -37,6 +39,7 @@ const mockAPITeamsList = {
summary_fields: { summary_fields: {
user_capabilities: { user_capabilities: {
delete: true, delete: true,
edit: true,
}, },
}, },
}, },

View File

@@ -4,17 +4,17 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
Button, Button,
DataListAction,
DataListCell,
DataListCheck,
DataListItem, DataListItem,
DataListItemRow,
DataListItemCells, DataListItemCells,
DataListItemRow,
Tooltip, Tooltip,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { PencilAltIcon } from '@patternfly/react-icons'; import { PencilAltIcon } from '@patternfly/react-icons';
import DataListCell from '@components/DataListCell';
import DataListCheck from '@components/DataListCheck';
import VerticalSeparator from '@components/VerticalSeparator';
import { Team } from '@types'; import { Team } from '@types';
class TeamListItem extends React.Component { class TeamListItem extends React.Component {
@@ -28,6 +28,7 @@ class TeamListItem extends React.Component {
render() { render() {
const { team, isSelected, onSelect, detailUrl, i18n } = this.props; const { team, isSelected, onSelect, detailUrl, i18n } = this.props;
const labelId = `check-action-${team.id}`; const labelId = `check-action-${team.id}`;
return ( return (
<DataListItem key={team.id} aria-labelledby={labelId} id={`${team.id}`}> <DataListItem key={team.id} aria-labelledby={labelId} id={`${team.id}`}>
<DataListItemRow> <DataListItemRow>
@@ -39,8 +40,7 @@ class TeamListItem extends React.Component {
/> />
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="name">
<VerticalSeparator />
<Link id={labelId} to={`${detailUrl}`}> <Link id={labelId} to={`${detailUrl}`}>
<b>{team.name}</b> <b>{team.name}</b>
</Link> </Link>
@@ -48,9 +48,7 @@ class TeamListItem extends React.Component {
<DataListCell key="organization"> <DataListCell key="organization">
{team.summary_fields.organization && ( {team.summary_fields.organization && (
<Fragment> <Fragment>
<b css={{ marginRight: '20px' }}> <b css="margin-right: 24px">{i18n._(t`Organization`)}</b>
{i18n._(t`Organization`)}
</b>
<Link <Link
to={`/organizations/${team.summary_fields.organization.id}/details`} to={`/organizations/${team.summary_fields.organization.id}/details`}
> >
@@ -59,21 +57,25 @@ class TeamListItem extends React.Component {
</Fragment> </Fragment>
)} )}
</DataListCell>, </DataListCell>,
<DataListCell key="edit" alignRight isFilled={false}>
{team.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Team`)} position="top">
<Button
variant="plain"
component={Link}
to={`/teams/${team.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListCell>,
]} ]}
/> />
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
{team.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Team`)} position="top">
<Button
variant="plain"
component={Link}
to={`/teams/${team.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -338,7 +338,7 @@ function JobTemplateDetail({ i18n, template }) {
{deletionError && ( {deletionError && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={() => setDeletionError(null)}
> >

View File

@@ -121,21 +121,25 @@ function TemplateList({ i18n }) {
const canAddWFJT = const canAddWFJT =
wfjtActions && Object.prototype.hasOwnProperty.call(wfjtActions, 'POST'); wfjtActions && Object.prototype.hasOwnProperty.call(wfjtActions, 'POST');
const addButtonOptions = []; const addButtonOptions = [];
if (canAddJT) { if (canAddJT) {
addButtonOptions.push({ addButtonOptions.push({
label: i18n._(t`Template`), label: i18n._(t`Template`),
url: `/templates/job_template/add/`, url: `/templates/job_template/add/`,
}); });
} }
if (canAddWFJT) { if (canAddWFJT) {
addButtonOptions.push({ addButtonOptions.push({
label: i18n._(t`Workflow Template`), label: i18n._(t`Workflow Template`),
url: `/templates/workflow_job_template/add/`, url: `/templates/workflow_job_template/add/`,
}); });
} }
const addButton = ( const addButton = (
<AddDropDownButton key="add" dropdownItems={addButtonOptions} /> <AddDropDownButton key="add" dropdownItems={addButtonOptions} />
); );
return ( return (
<> <>
<Card> <Card>
@@ -215,7 +219,7 @@ function TemplateList({ i18n }) {
itemsToDelete={selected} itemsToDelete={selected}
pluralizedItemName="Templates" pluralizedItemName="Templates"
/>, />,
(canAddJT || canAddWFJT) && addButton, ...(canAddJT || canAddWFJT ? [addButton] : []),
]} ]}
/> />
)} )}
@@ -234,7 +238,7 @@ function TemplateList({ i18n }) {
</Card> </Card>
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={clearDeletionError} onClose={clearDeletionError}
> >

View File

@@ -2,6 +2,9 @@ import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { import {
Button, Button,
DataListAction as _DataListAction,
DataListCell,
DataListCheck,
DataListItem, DataListItem,
DataListItemRow, DataListItemRow,
DataListItemCells, DataListItemCells,
@@ -15,14 +18,20 @@ import {
RocketIcon, RocketIcon,
} from '@patternfly/react-icons'; } from '@patternfly/react-icons';
import DataListCell from '@components/DataListCell';
import DataListCheck from '@components/DataListCheck';
import LaunchButton from '@components/LaunchButton'; import LaunchButton from '@components/LaunchButton';
import VerticalSeparator from '@components/VerticalSeparator'; import Sparkline from '@components/Sparkline';
import { Sparkline } from '@components/Sparkline';
import { toTitleCase } from '@util/strings'; import { toTitleCase } from '@util/strings';
import styled from 'styled-components';
const DataListAction = styled(_DataListAction)`
align-items: center;
display: grid;
grid-gap: 16px;
grid-template-columns: repeat(2, 40px);
`;
function TemplateListItem({ i18n, template, isSelected, onSelect, detailUrl }) { function TemplateListItem({ i18n, template, isSelected, onSelect, detailUrl }) {
const labelId = `check-action-${template.id}`;
const canLaunch = template.summary_fields.user_capabilities.start; const canLaunch = template.summary_fields.user_capabilities.start;
const missingResourceIcon = const missingResourceIcon =
@@ -32,21 +41,17 @@ function TemplateListItem({ i18n, template, isSelected, onSelect, detailUrl }) {
!template.ask_inventory_on_launch)); !template.ask_inventory_on_launch));
return ( return (
<DataListItem <DataListItem aria-labelledby={labelId} id={`${template.id}`}>
aria-labelledby={`check-action-${template.id}`}
id={`${template.id}`}
>
<DataListItemRow> <DataListItemRow>
<DataListCheck <DataListCheck
id={`select-jobTemplate-${template.id}`} id={`select-jobTemplate-${template.id}`}
checked={isSelected} checked={isSelected}
onChange={onSelect} onChange={onSelect}
aria-labelledby={`check-action-${template.id}`} aria-labelledby={labelId}
/> />
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="divider">
<VerticalSeparator />
<span> <span>
<Link to={`${detailUrl}`}> <Link to={`${detailUrl}`}>
<b>{template.name}</b> <b>{template.name}</b>
@@ -71,34 +76,41 @@ function TemplateListItem({ i18n, template, isSelected, onSelect, detailUrl }) {
<DataListCell key="sparkline"> <DataListCell key="sparkline">
<Sparkline jobs={template.summary_fields.recent_jobs} /> <Sparkline jobs={template.summary_fields.recent_jobs} />
</DataListCell>, </DataListCell>,
<DataListCell alignRight isFilled={false} key="launch">
{canLaunch && template.type === 'job_template' && (
<Tooltip content={i18n._(t`Launch Template`)} position="top">
<LaunchButton resource={template}>
{({ handleLaunch }) => (
<Button variant="plain" onClick={handleLaunch}>
<RocketIcon />
</Button>
)}
</LaunchButton>
</Tooltip>
)}
</DataListCell>,
<DataListCell key="edit" alignRight isFilled={false}>
{template.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Template`)} position="top">
<Button
variant="plain"
component={Link}
to={`/templates/${template.type}/${template.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListCell>,
]} ]}
/> />
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
{canLaunch && template.type === 'job_template' && (
<Tooltip content={i18n._(t`Launch Template`)} position="top">
<LaunchButton resource={template}>
{({ handleLaunch }) => (
<Button
css="grid-column: 1"
variant="plain"
onClick={handleLaunch}
>
<RocketIcon />
</Button>
)}
</LaunchButton>
</Tooltip>
)}
{template.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Template`)} position="top">
<Button
css="grid-column: 2"
variant="plain"
component={Link}
to={`/templates/${template.type}/${template.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { Link, useHistory } from 'react-router-dom'; import { Link, useHistory } from 'react-router-dom';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { WorkflowJobTemplatesAPI } from '@api';
import { import {
Chip, Chip,
ChipGroup, ChipGroup,
@@ -13,17 +14,16 @@ import {
Label, Label,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { CardBody, CardActionsRow } from '@components/Card';
import ContentLoading from '@components/ContentLoading';
import { WorkflowJobTemplatesAPI } from '@api';
import AlertModal from '@components/AlertModal'; import AlertModal from '@components/AlertModal';
import ErrorDetail from '@components/ErrorDetail'; import { CardBody, CardActionsRow } from '@components/Card';
import { DetailList, Detail, UserDateDetail } from '@components/DetailList';
import { VariablesDetail } from '@components/CodeMirrorInput'; import { VariablesDetail } from '@components/CodeMirrorInput';
import LaunchButton from '@components/LaunchButton'; import ContentLoading from '@components/ContentLoading';
import DeleteButton from '@components/DeleteButton'; import DeleteButton from '@components/DeleteButton';
import { DetailList, Detail, UserDateDetail } from '@components/DetailList';
import ErrorDetail from '@components/ErrorDetail';
import LaunchButton from '@components/LaunchButton';
import Sparkline from '@components/Sparkline';
import { toTitleCase } from '@util/strings'; import { toTitleCase } from '@util/strings';
import { Sparkline } from '@components/Sparkline';
function WorkflowJobTemplateDetail({ template, i18n, webHookKey }) { function WorkflowJobTemplateDetail({ template, i18n, webHookKey }) {
const { const {
@@ -104,7 +104,6 @@ function WorkflowJobTemplateDetail({ template, i18n, webHookKey }) {
<Detail label={i18n._(t`Description`)} value={description} /> <Detail label={i18n._(t`Description`)} value={description} />
{summary_fields.recent_jobs?.length > 0 && ( {summary_fields.recent_jobs?.length > 0 && (
<Detail <Detail
css="display: flex; flex: 1;"
value={<Sparkline jobs={recentPlaybookJobs} />} value={<Sparkline jobs={recentPlaybookJobs} />}
label={i18n._(t`Activity`)} label={i18n._(t`Activity`)}
/> />
@@ -223,7 +222,7 @@ function WorkflowJobTemplateDetail({ template, i18n, webHookKey }) {
{deletionError && ( {deletionError && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={() => setDeletionError(null)}
> >

View File

@@ -109,7 +109,7 @@ describe('NodeModal', () => {
wrapper.find('button#next-node-modal').simulate('click'); wrapper.find('button#next-node-modal').simulate('click');
}); });
wrapper.update(); wrapper.update();
wrapper.find('DataListRadio').simulate('click'); wrapper.find('Radio').simulate('click');
await act(async () => { await act(async () => {
wrapper.find('button#next-node-modal').simulate('click'); wrapper.find('button#next-node-modal').simulate('click');
}); });
@@ -136,7 +136,7 @@ describe('NodeModal', () => {
wrapper.find('AnsibleSelect').prop('onChange')(null, 'project_sync'); wrapper.find('AnsibleSelect').prop('onChange')(null, 'project_sync');
}); });
wrapper.update(); wrapper.update();
wrapper.find('DataListRadio').simulate('click'); wrapper.find('Radio').simulate('click');
await act(async () => { await act(async () => {
wrapper.find('button#next-node-modal').simulate('click'); wrapper.find('button#next-node-modal').simulate('click');
}); });
@@ -166,7 +166,7 @@ describe('NodeModal', () => {
); );
}); });
wrapper.update(); wrapper.update();
wrapper.find('DataListRadio').simulate('click'); wrapper.find('Radio').simulate('click');
await act(async () => { await act(async () => {
wrapper.find('button#next-node-modal').simulate('click'); wrapper.find('button#next-node-modal').simulate('click');
}); });
@@ -193,7 +193,7 @@ describe('NodeModal', () => {
); );
}); });
wrapper.update(); wrapper.update();
wrapper.find('DataListRadio').simulate('click'); wrapper.find('Radio').simulate('click');
await act(async () => { await act(async () => {
wrapper.find('button#next-node-modal').simulate('click'); wrapper.find('button#next-node-modal').simulate('click');
}); });
@@ -396,7 +396,7 @@ describe('NodeModal', () => {
); );
}); });
wrapper.update(); wrapper.update();
wrapper.find('DataListRadio').simulate('click'); wrapper.find('Radio').simulate('click');
await act(async () => { await act(async () => {
wrapper.find('button#next-node-modal').simulate('click'); wrapper.find('button#next-node-modal').simulate('click');
}); });

View File

@@ -7,19 +7,11 @@ import { Formik, Field } from 'formik';
import { Form, FormGroup, TextInput } from '@patternfly/react-core'; import { Form, FormGroup, TextInput } from '@patternfly/react-core';
import FormRow from '@components/FormRow'; import FormRow from '@components/FormRow';
import AnsibleSelect from '@components/AnsibleSelect'; import AnsibleSelect from '@components/AnsibleSelect';
import VerticalSeperator from '@components/VerticalSeparator';
import InventorySourcesList from './InventorySourcesList'; import InventorySourcesList from './InventorySourcesList';
import JobTemplatesList from './JobTemplatesList'; import JobTemplatesList from './JobTemplatesList';
import ProjectsList from './ProjectsList'; import ProjectsList from './ProjectsList';
import WorkflowJobTemplatesList from './WorkflowJobTemplatesList'; import WorkflowJobTemplatesList from './WorkflowJobTemplatesList';
const Divider = styled.div`
height: 1px;
background-color: var(--pf-global--Color--light-300);
border: 0;
flex-shrink: 0;
`;
const TimeoutInput = styled(TextInput)` const TimeoutInput = styled(TextInput)`
width: 200px; width: 200px;
:not(:first-of-type) { :not(:first-of-type) {
@@ -47,9 +39,8 @@ function NodeTypeStep({
}) { }) {
return ( return (
<> <>
<div css=" display: flex; align-items: center; margin-bottom: 20px;"> <div css="display: flex; align-items: center; margin-bottom: 20px;">
<b>{i18n._(t`Node Type`)}</b> <b css="margin-right: 24px">{i18n._(t`Node Type`)}</b>
<VerticalSeperator />
<div> <div>
<AnsibleSelect <AnsibleSelect
id="nodeResource-select" id="nodeResource-select"
@@ -93,7 +84,6 @@ function NodeTypeStep({
/> />
</div> </div>
</div> </div>
<Divider component="div" />
{nodeType === 'job_template' && ( {nodeType === 'job_template' && (
<JobTemplatesList <JobTemplatesList
nodeResource={nodeResource} nodeResource={nodeResource}

View File

@@ -94,7 +94,7 @@ describe('NodeTypeStep', () => {
wrapper.update(); wrapper.update();
expect(wrapper.find('AnsibleSelect').prop('value')).toBe('job_template'); expect(wrapper.find('AnsibleSelect').prop('value')).toBe('job_template');
expect(wrapper.find('JobTemplatesList').length).toBe(1); expect(wrapper.find('JobTemplatesList').length).toBe(1);
wrapper.find('DataListRadio').simulate('click'); wrapper.find('Radio').simulate('click');
expect(onUpdateNodeResource).toHaveBeenCalledWith({ expect(onUpdateNodeResource).toHaveBeenCalledWith({
id: 1, id: 1,
name: 'Test Job Template', name: 'Test Job Template',
@@ -119,7 +119,7 @@ describe('NodeTypeStep', () => {
wrapper.update(); wrapper.update();
expect(wrapper.find('AnsibleSelect').prop('value')).toBe('project_sync'); expect(wrapper.find('AnsibleSelect').prop('value')).toBe('project_sync');
expect(wrapper.find('ProjectsList').length).toBe(1); expect(wrapper.find('ProjectsList').length).toBe(1);
wrapper.find('DataListRadio').simulate('click'); wrapper.find('Radio').simulate('click');
expect(onUpdateNodeResource).toHaveBeenCalledWith({ expect(onUpdateNodeResource).toHaveBeenCalledWith({
id: 1, id: 1,
name: 'Test Project', name: 'Test Project',
@@ -146,7 +146,7 @@ describe('NodeTypeStep', () => {
'inventory_source_sync' 'inventory_source_sync'
); );
expect(wrapper.find('InventorySourcesList').length).toBe(1); expect(wrapper.find('InventorySourcesList').length).toBe(1);
wrapper.find('DataListRadio').simulate('click'); wrapper.find('Radio').simulate('click');
expect(onUpdateNodeResource).toHaveBeenCalledWith({ expect(onUpdateNodeResource).toHaveBeenCalledWith({
id: 1, id: 1,
name: 'Test Inventory Source', name: 'Test Inventory Source',
@@ -173,7 +173,7 @@ describe('NodeTypeStep', () => {
'workflow_job_template' 'workflow_job_template'
); );
expect(wrapper.find('WorkflowJobTemplatesList').length).toBe(1); expect(wrapper.find('WorkflowJobTemplatesList').length).toBe(1);
wrapper.find('DataListRadio').simulate('click'); wrapper.find('Radio').simulate('click');
expect(onUpdateNodeResource).toHaveBeenCalledWith({ expect(onUpdateNodeResource).toHaveBeenCalledWith({
id: 1, id: 1,
name: 'Test Workflow Job Template', name: 'Test Workflow Job Template',

View File

@@ -234,7 +234,7 @@ function VisualizerGraph({ i18n, readOnly }) {
{linkHelp && <WorkflowLinkHelp link={linkHelp} />} {linkHelp && <WorkflowLinkHelp link={linkHelp} />}
</WorkflowHelp> </WorkflowHelp>
)} )}
<WorkflowSVG id="workflow-svg" ref={svgRef} css=""> <WorkflowSVG id="workflow-svg" ref={svgRef}>
<defs> <defs>
<marker <marker
className="WorkflowChart-noPointerEvents" className="WorkflowChart-noPointerEvents"

View File

@@ -6,7 +6,12 @@ import {
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { func, shape } from 'prop-types'; import { func, shape } from 'prop-types';
import { Badge as PFBadge, Button, Tooltip } from '@patternfly/react-core'; import {
Badge as PFBadge,
Button,
Title,
Tooltip,
} from '@patternfly/react-core';
import { import {
BookIcon, BookIcon,
CompassIcon, CompassIcon,
@@ -15,7 +20,6 @@ import {
TrashAltIcon, TrashAltIcon,
WrenchIcon, WrenchIcon,
} from '@patternfly/react-icons'; } from '@patternfly/react-icons';
import VerticalSeparator from '@components/VerticalSeparator';
import styled from 'styled-components'; import styled from 'styled-components';
const Badge = styled(PFBadge)` const Badge = styled(PFBadge)`
@@ -51,15 +55,12 @@ function VisualizerToolbar({ i18n, onClose, onSave, template }) {
return ( return (
<div id="visualizer-toolbar"> <div id="visualizer-toolbar">
<div css="align-items: center; border-bottom: 1px solid grey; display: flex; height: 56px; padding: 0px 20px;"> <div css="align-items: center; border-bottom: 1px solid grey; display: flex; height: 56px; padding: 0px 20px;">
<div css="display: flex;" id="visualizer-toolbar-template-name"> <Title size="xl">{template.name}</Title>
<b>{template.name}</b>
</div>
<div css="align-items: center; display: flex; flex: 1; justify-content: flex-end"> <div css="align-items: center; display: flex; flex: 1; justify-content: flex-end">
<div>{i18n._(t`Total Nodes`)}</div> <div>{i18n._(t`Total Nodes`)}</div>
<Badge id="visualizer-total-nodes-badge" isRead> <Badge id="visualizer-total-nodes-badge" isRead>
{totalNodes} {totalNodes}
</Badge> </Badge>
<VerticalSeparator />
<Tooltip content={i18n._(t`Toggle Legend`)} position="bottom"> <Tooltip content={i18n._(t`Toggle Legend`)} position="bottom">
<ActionButton <ActionButton
id="visualizer-toggle-legend" id="visualizer-toggle-legend"
@@ -108,16 +109,15 @@ function VisualizerToolbar({ i18n, onClose, onSave, template }) {
<TrashAltIcon /> <TrashAltIcon />
</ActionButton> </ActionButton>
</Tooltip> </Tooltip>
<VerticalSeparator />
<Button <Button
id="visualizer-save" id="visualizer-save"
css="margin: 0 32px"
aria-label={i18n._(t`Save`)} aria-label={i18n._(t`Save`)}
variant="primary" variant="primary"
onClick={onSave} onClick={onSave}
> >
{i18n._(t`Save`)} {i18n._(t`Save`)}
</Button> </Button>
<VerticalSeparator />
<Button <Button
id="visualizer-close" id="visualizer-close"
aria-label={i18n._(t`Close`)} aria-label={i18n._(t`Close`)}

View File

@@ -99,7 +99,7 @@ function UserDetail({ user, i18n }) {
{deletionError && ( {deletionError && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={() => setDeletionError(null)}
> >

View File

@@ -197,9 +197,14 @@ class UsersList extends Component {
itemsToDelete={selected} itemsToDelete={selected}
pluralizedItemName="Users" pluralizedItemName="Users"
/>, />,
canAdd ? ( ...(canAdd
<ToolbarAddButton key="add" linkTo={`${match.url}/add`} /> ? [
) : null, <ToolbarAddButton
key="add"
linkTo={`${match.url}/add`}
/>,
]
: []),
]} ]}
/> />
)} )}
@@ -222,7 +227,7 @@ class UsersList extends Component {
</PageSection> </PageSection>
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={this.handleDeleteErrorClose} onClose={this.handleDeleteErrorClose}
> >

View File

@@ -4,17 +4,17 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
Button, Button,
DataListAction,
DataListCell,
DataListCheck,
DataListItem, DataListItem,
DataListItemRow,
DataListItemCells, DataListItemCells,
DataListItemRow,
Tooltip, Tooltip,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { PencilAltIcon } from '@patternfly/react-icons'; import { PencilAltIcon } from '@patternfly/react-icons';
import DataListCell from '@components/DataListCell';
import DataListCheck from '@components/DataListCheck';
import VerticalSeparator from '@components/VerticalSeparator';
import { User } from '@types'; import { User } from '@types';
class UserListItem extends React.Component { class UserListItem extends React.Component {
@@ -39,8 +39,7 @@ class UserListItem extends React.Component {
/> />
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="username">
<VerticalSeparator />
<Link to={`${detailUrl}`} id={labelId}> <Link to={`${detailUrl}`} id={labelId}>
<b>{user.username}</b> <b>{user.username}</b>
</Link> </Link>
@@ -48,7 +47,7 @@ class UserListItem extends React.Component {
<DataListCell key="first-name"> <DataListCell key="first-name">
{user.first_name && ( {user.first_name && (
<Fragment> <Fragment>
<b css={{ marginRight: '20px' }}>{i18n._(t`First Name`)}</b> <b css="margin-right: 24px">{i18n._(t`First Name`)}</b>
{user.first_name} {user.first_name}
</Fragment> </Fragment>
)} )}
@@ -56,26 +55,30 @@ class UserListItem extends React.Component {
<DataListCell key="last-name"> <DataListCell key="last-name">
{user.last_name && ( {user.last_name && (
<Fragment> <Fragment>
<b css={{ marginRight: '20px' }}>{i18n._(t`Last Name`)}</b> <b css="margin-right: 24px">{i18n._(t`Last Name`)}</b>
{user.last_name} {user.last_name}
</Fragment> </Fragment>
)} )}
</DataListCell>, </DataListCell>,
<DataListCell key="edit" alignRight isFilled={false}>
{user.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit User`)} position="top">
<Button
variant="plain"
component={Link}
to={`/users/${user.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListCell>,
]} ]}
/> />
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
{user.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit User`)} position="top">
<Button
variant="plain"
component={Link}
to={`/users/${user.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow> </DataListItemRow>
</DataListItem> </DataListItem>
); );

View File

@@ -80,11 +80,7 @@ function UserForm({ user, handleCancel, handleSubmit, submitError, i18n }) {
onSubmit={handleValidateAndSubmit} onSubmit={handleValidateAndSubmit}
> >
{formik => ( {formik => (
<Form <Form autoComplete="off" onSubmit={formik.handleSubmit}>
autoComplete="off"
onSubmit={formik.handleSubmit}
css="padding: 0 24px"
>
<FormRow> <FormRow>
<FormField <FormField
id="user-username" id="user-username"