Remove DataList component overrides

This commit is contained in:
Marliana Lara
2020-02-18 10:40:18 -05:00
parent fe9b03a189
commit 4ebc2573a3
45 changed files with 788 additions and 998 deletions

View File

@@ -1,4 +0,0 @@
// https://github.com/patternfly/patternfly-react/issues/1294
#app {
height: 100%;
}

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

@@ -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,20 +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;
`; `;
class DataListToolbar extends React.Component { class DataListToolbar extends React.Component {
@@ -102,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

@@ -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

@@ -15,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;
`; `;
@@ -39,7 +40,7 @@ class SelectedList extends Component {
return ( return (
<Split> <Split>
<SplitLabelItem css="margin-right: 32px">{label}</SplitLabelItem> <SplitLabelItem>{label}</SplitLabelItem>
<SplitItem> <SplitItem>
<ChipGroup numChips={5}> <ChipGroup numChips={5}>
{selected.map(item => {selected.map(item =>

View File

@@ -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

@@ -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

@@ -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" />]
), : []),
]} ]}
/> />
)} )}

View File

@@ -5,24 +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, 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 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,
@@ -56,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

@@ -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`}
/>,
]
: []),
]} ]}
/> />
)} )}

View File

@@ -4,6 +4,8 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
Button, Button,
DataListAction as _DataListAction,
DataListCell,
DataListCheck, DataListCheck,
DataListItem, DataListItem,
DataListItemRow, DataListItemRow,
@@ -14,9 +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 { Sparkline } from '@components/Sparkline';
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 = {
@@ -65,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'
@@ -80,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

@@ -6,17 +6,17 @@ import { Group } from '@types';
import { import {
Button, Button,
DataListAction,
DataListCell,
DataListCheck, 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';
function InventoryGroupItem({ function InventoryGroupItem({
i18n, i18n,
group, group,
@@ -39,22 +39,26 @@ function InventoryGroupItem({
/> />
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="name">
<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`}
), />,
]
: []),
]} ]}
/> />
)} )}

View File

@@ -4,19 +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, 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 DataListCell from '@components/DataListCell';
import { Sparkline } from '@components/Sparkline'; import { Sparkline } from '@components/Sparkline';
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 {
@@ -56,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`}
), />,
]
: []),
]} ]}
/> />
)} )}

View File

@@ -208,7 +208,7 @@ class InventoriesList extends Component {
itemsToDelete={selected} itemsToDelete={selected}
pluralizedItemName="Inventories" pluralizedItemName="Inventories"
/>, />,
canAdd && addButton, ...(canAdd ? [addButton] : []),
]} ]}
/> />
)} )}

View File

@@ -3,17 +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, 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 { Inventory } from '@types'; import { Inventory } from '@types';
class InventoryListItem extends React.Component { class InventoryListItem extends React.Component {
@@ -52,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

@@ -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

@@ -5,6 +5,7 @@ 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,
@@ -20,28 +21,26 @@ import { formatDateString } from '@util/dates';
import { JOB_TYPE_URL_SEGMENTS } from '@constants'; import { JOB_TYPE_URL_SEGMENTS } from '@constants';
const PaddedIcon = styled(StatusIcon)` const PaddedIcon = styled(StatusIcon)`
margin-right: 20px; margin: 6px 20px 0 0;
`; `;
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 <DataListCell key="name" css="display: inline-flex;">
key="name"
css="display: inline-flex; align-items: center;"
>
{job.status && <PaddedIcon status={job.status} />} {job.status && <PaddedIcon status={job.status} />}
<span> <span>
<Link <Link
@@ -57,22 +56,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

@@ -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, : []),
]} ]}
/> />
)} )}

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,36 +5,29 @@ import { t } from '@lingui/macro';
import { import {
Badge as PFBadge, Badge as PFBadge,
Button, Button,
DataListAction,
DataListCell,
DataListCheck, 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 { 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;
}
} }
`; `;
@@ -82,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

@@ -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`}
/>,
]
: []),
]} ]}
/> />
)} )}

View File

@@ -3,6 +3,8 @@ 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, DataListCheck,
DataListItem, DataListItem,
DataListItemRow, DataListItemRow,
@@ -12,14 +14,24 @@ 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 ProjectSyncButton from '../shared/ProjectSyncButton'; import ProjectSyncButton from '../shared/ProjectSyncButton';
import { StatusIcon } from '@components/Sparkline'; import { StatusIcon } from '@components/Sparkline';
import { toTitleCase } from '@util/strings'; import { toTitleCase } from '@util/strings';
import { Project } from '@types'; import { Project } from '@types';
const PaddedIcon = styled(StatusIcon)`
margin: 6px 20px 0 0;
`;
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,
@@ -72,7 +84,7 @@ class ProjectListItem extends React.Component {
/> />
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="name" css="display: inline-flex">
{project.summary_fields.last_job && ( {project.summary_fields.last_job && (
<Tooltip <Tooltip
position="top" position="top"
@@ -84,17 +96,13 @@ class ProjectListItem extends React.Component {
<Link <Link
to={`/jobs/project/${project.summary_fields.last_job.id}`} to={`/jobs/project/${project.summary_fields.last_job.id}`}
> >
<StatusIcon <PaddedIcon
status={project.summary_fields.last_job.status} status={project.summary_fields.last_job.status}
/> />
</Link> </Link>
</Tooltip> </Tooltip>
)} )}
<Link <Link id={labelId} to={`${detailUrl}`}>
id={labelId}
to={`${detailUrl}`}
css={{ marginLeft: '10px' }}
>
<b>{project.name}</b> <b>{project.name}</b>
</Link> </Link>
</DataListCell>, </DataListCell>,
@@ -103,7 +111,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
@@ -113,34 +121,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

@@ -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`}
/>,
]
: []),
]} ]}
/> />
)} )}

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,16 +4,17 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
Button, Button,
DataListAction,
DataListCell,
DataListCheck, 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 { Team } from '@types'; import { Team } from '@types';
class TeamListItem extends React.Component { class TeamListItem extends React.Component {
@@ -27,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>
@@ -38,7 +40,7 @@ class TeamListItem extends React.Component {
/> />
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="name">
<Link id={labelId} to={`${detailUrl}`}> <Link id={labelId} to={`${detailUrl}`}>
<b>{team.name}</b> <b>{team.name}</b>
</Link> </Link>
@@ -46,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`}
> >
@@ -57,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

@@ -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] : []),
]} ]}
/> />
)} )}

View File

@@ -2,6 +2,8 @@ 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, DataListCheck,
DataListItem, DataListItem,
DataListItemRow, DataListItemRow,
@@ -16,12 +18,20 @@ import {
RocketIcon, RocketIcon,
} from '@patternfly/react-icons'; } from '@patternfly/react-icons';
import DataListCell from '@components/DataListCell';
import LaunchButton from '@components/LaunchButton'; import LaunchButton from '@components/LaunchButton';
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 =
@@ -31,16 +41,13 @@ 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={[
@@ -69,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

@@ -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,7 +7,6 @@ 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';
@@ -47,9 +46,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"

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

@@ -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

@@ -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`}
/>,
]
: []),
]} ]}
/> />
)} )}

View File

@@ -4,16 +4,17 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
Button, Button,
DataListAction,
DataListCell,
DataListCheck, 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 { User } from '@types'; import { User } from '@types';
class UserListItem extends React.Component { class UserListItem extends React.Component {
@@ -38,7 +39,7 @@ class UserListItem extends React.Component {
/> />
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key="divider"> <DataListCell key="username">
<Link to={`${detailUrl}`} id={labelId}> <Link to={`${detailUrl}`} id={labelId}>
<b>{user.username}</b> <b>{user.username}</b>
</Link> </Link>
@@ -46,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>
)} )}
@@ -54,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>
); );