diff --git a/awx/ui_next/dist/index.html b/awx/ui_next/dist/index.html index 10a4088e01..8afa7581c8 100644 --- a/awx/ui_next/dist/index.html +++ b/awx/ui_next/dist/index.html @@ -1,7 +1,7 @@ -
+
diff --git a/awx/ui_next/src/App.jsx b/awx/ui_next/src/App.jsx index 704515c113..fdc4bb4ec2 100644 --- a/awx/ui_next/src/App.jsx +++ b/awx/ui_next/src/App.jsx @@ -204,7 +204,7 @@ class App extends Component { /> diff --git a/awx/ui_next/src/app.scss b/awx/ui_next/src/app.scss deleted file mode 100644 index 2c02873917..0000000000 --- a/awx/ui_next/src/app.scss +++ /dev/null @@ -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); - } -} diff --git a/awx/ui_next/src/components/AlertModal/AlertModal.jsx b/awx/ui_next/src/components/AlertModal/AlertModal.jsx index 1b31f79653..f600d77745 100644 --- a/awx/ui_next/src/components/AlertModal/AlertModal.jsx +++ b/awx/ui_next/src/components/AlertModal/AlertModal.jsx @@ -1,42 +1,47 @@ import React from 'react'; - -import { Modal } from '@patternfly/react-core'; - +import { Modal, Title } from '@patternfly/react-core'; import { - ExclamationTriangleIcon, - ExclamationCircleIcon, - InfoCircleIcon, CheckCircleIcon, + ExclamationCircleIcon, + ExclamationTriangleIcon, + InfoCircleIcon, + TimesCircleIcon, } from '@patternfly/react-icons'; +import styled from 'styled-components'; -const getIcon = variant => { - let icon; - if (variant === 'warning') { - icon = ; - } else if (variant === 'danger') { - icon = ; +const Header = styled.div` + display: flex; + svg { + margin-right: 16px; } - if (variant === 'info') { - icon = ; - } - if (variant === 'success') { - icon = ; - } - return icon; -}; +`; + +export default ({ isOpen = null, title, variant, children, ...props }) => { + const variantIcons = { + danger: , + error: , + info: , + success: , + warning: , + }; + + const customHeader = ( +
+ {variant ? variantIcons[variant] : null} + {title} +
+ ); -export default ({ variant, children, ...props }) => { - const { isOpen = null } = props; - props.isOpen = Boolean(isOpen); return ( {children} - {getIcon(variant)} ); }; diff --git a/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx b/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx index 4c9389543f..da8b234e5c 100644 --- a/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx +++ b/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx @@ -5,7 +5,9 @@ import AlertModal from './AlertModal'; describe('AlertModal', () => { test('renders the expected content', () => { - const wrapper = mount(); + const wrapper = mount( + Are you sure? + ); expect(wrapper).toHaveLength(1); }); }); diff --git a/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx b/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx index 2616cf15cd..0213d7d113 100644 --- a/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx +++ b/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx @@ -6,19 +6,21 @@ import { DataListItemCells, DataListCell, DataListCheck, + Radio, } from '@patternfly/react-core'; -import DataListRadio from '@components/DataListRadio'; const CheckboxListItem = ({ + isDisabled = false, + isRadio = false, + isSelected = false, itemId, - name, label, - isSelected, - onSelect, + name, onDeselect, - isRadio, + onSelect, }) => { - const CheckboxRadio = isRadio ? DataListRadio : DataListCheck; + const CheckboxRadio = isRadio ? Radio : DataListCheck; + return ( (props.righthalf ? '16px' : '8px')}; - @media screen and (min-width: 768px) { - padding-bottom: 0; - justify-content: ${props => (props.lastcolumn ? 'flex-end' : 'inherit')}; - } -`; - -export default DataListCell; diff --git a/awx/ui_next/src/components/DataListCell/DataListCell.test.jsx b/awx/ui_next/src/components/DataListCell/DataListCell.test.jsx deleted file mode 100644 index 55ec0b3018..0000000000 --- a/awx/ui_next/src/components/DataListCell/DataListCell.test.jsx +++ /dev/null @@ -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(); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui_next/src/components/DataListCell/index.js b/awx/ui_next/src/components/DataListCell/index.js deleted file mode 100644 index d925b63c7d..0000000000 --- a/awx/ui_next/src/components/DataListCell/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './DataListCell'; diff --git a/awx/ui_next/src/components/DataListCheck/DataListCheck.jsx b/awx/ui_next/src/components/DataListCheck/DataListCheck.jsx deleted file mode 100644 index 30817989cf..0000000000 --- a/awx/ui_next/src/components/DataListCheck/DataListCheck.jsx +++ /dev/null @@ -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; - } - } -`; diff --git a/awx/ui_next/src/components/DataListCheck/DataListCheck.test.jsx b/awx/ui_next/src/components/DataListCheck/DataListCheck.test.jsx deleted file mode 100644 index f2414374fc..0000000000 --- a/awx/ui_next/src/components/DataListCheck/DataListCheck.test.jsx +++ /dev/null @@ -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(); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui_next/src/components/DataListCheck/index.js b/awx/ui_next/src/components/DataListCheck/index.js deleted file mode 100644 index cdb8b6137c..0000000000 --- a/awx/ui_next/src/components/DataListCheck/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './DataListCheck'; diff --git a/awx/ui_next/src/components/DataListRadio/DataListRadio.jsx b/awx/ui_next/src/components/DataListRadio/DataListRadio.jsx deleted file mode 100644 index bccb1a3efe..0000000000 --- a/awx/ui_next/src/components/DataListRadio/DataListRadio.jsx +++ /dev/null @@ -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 ( -
-
- onChange(event.currentTarget.checked, event)} - aria-invalid={!isValid} - disabled={isDisabled} - checked={isChecked || checked} - /> -
-
- ); -} -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; diff --git a/awx/ui_next/src/components/DataListRadio/DataListRadio.test.jsx b/awx/ui_next/src/components/DataListRadio/DataListRadio.test.jsx deleted file mode 100644 index b8fa2e4135..0000000000 --- a/awx/ui_next/src/components/DataListRadio/DataListRadio.test.jsx +++ /dev/null @@ -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(); - 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( - - ); - 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); - }); -}); diff --git a/awx/ui_next/src/components/DataListRadio/index.js b/awx/ui_next/src/components/DataListRadio/index.js deleted file mode 100644 index c8f5b6d345..0000000000 --- a/awx/ui_next/src/components/DataListRadio/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './DataListRadio'; diff --git a/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx b/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx index 3b03ac838e..ce1f53f053 100644 --- a/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx +++ b/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx @@ -7,10 +7,10 @@ import styled from 'styled-components'; import { SearchIcon } from '@patternfly/react-icons'; import { DataToolbar, - DataToolbarContent, - DataToolbarGroup, - DataToolbarToggleGroup, + DataToolbarContent as _DataToolbarContent, + DataToolbarGroup as _DataToolbarGroup, DataToolbarItem, + DataToolbarToggleGroup, } from '@patternfly/react-core/dist/umd/experimental'; import ExpandCollapse from '../ExpandCollapse'; import Search from '../Search'; @@ -18,27 +18,12 @@ import Sort from '../Sort'; import { SearchColumns, SortColumns, QSConfig } from '@types'; -const AdditionalControlsWrapper = styled.div` - display: flex; - flex-grow: 1; - justify-content: flex-end; - align-items: center; - - & > :not(:first-child) { - margin-left: 20px; - } +const DataToolbarContent = styled(_DataToolbarContent)` + --pf-c-data-toolbar__content--PaddingLeft: 24px; + --pf-c-data-toolbar__content--PaddingRight: 8px; `; - -const AdditionalControlsDataToolbarGroup = styled(DataToolbarGroup)` - 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; +const DataToolbarGroup = styled(_DataToolbarGroup)` + --pf-c-data-toolbar__group--spacer: 24px; `; class DataListToolbar extends React.Component { @@ -80,7 +65,6 @@ class DataListToolbar extends React.Component { id="select-all" /> - )} } breakpoint="lg"> @@ -110,13 +94,11 @@ class DataListToolbar extends React.Component { )} - - - - {additionalControls} - - - + + {additionalControls.map(control => ( + {control} + ))} + ); diff --git a/awx/ui_next/src/components/DeleteButton/DeleteButton.jsx b/awx/ui_next/src/components/DeleteButton/DeleteButton.jsx index f434ea40d5..184caf9ef9 100644 --- a/awx/ui_next/src/components/DeleteButton/DeleteButton.jsx +++ b/awx/ui_next/src/components/DeleteButton/DeleteButton.jsx @@ -3,7 +3,6 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Button } from '@patternfly/react-core'; import AlertModal from '@components/AlertModal'; -import { CardActionsRow } from '@components/Card'; function DeleteButton({ onConfirm, @@ -29,26 +28,28 @@ function DeleteButton({ title={modalTitle} variant="danger" onClose={() => setIsOpen(false)} - > - {i18n._(t`Are you sure you want to delete:`)} -
- {name} - - + actions={[ - + , + , + ]} + > + {i18n._(t`Are you sure you want to delete:`)} +
+ {name}
); diff --git a/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx b/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx index 1793eed8ba..86df83f41e 100644 --- a/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx +++ b/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx @@ -128,7 +128,7 @@ class LaunchButton extends React.Component { {launchError && ( diff --git a/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.jsx b/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.jsx index 782b11da36..cf05f48e89 100644 --- a/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.jsx +++ b/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.jsx @@ -8,7 +8,6 @@ import { CredentialsAPI, CredentialTypesAPI } from '@api'; import AnsibleSelect from '@components/AnsibleSelect'; import { FieldTooltip } from '@components/FormField'; import CredentialChip from '@components/CredentialChip'; -import VerticalSeperator from '@components/VerticalSeparator'; import { getQSConfig, parseQueryString } from '@util/qs'; import Lookup from './Lookup'; import OptionsList from './shared/OptionsList'; @@ -97,8 +96,9 @@ function MultiCredentialsLookup(props) { {credentialTypes && credentialTypes.length > 0 && ( -
{i18n._(t`Selected Category`)}
- +
+ {i18n._(t`Selected Category`)} +
(props.righthalf ? 'flex-start' : 'inherit')}; - padding-bottom: ${props => (props.righthalf ? '16px' : '8px')}; - - @media screen and (min-width: 768px) { - justify-content: ${props => (props.righthalf ? 'flex-end' : 'inherit')}; - padding-bottom: 0; - } +const DataListAction = styled(_DataListAction)` + align-items: center; + display: grid; + grid-gap: 16px; + grid-template-columns: repeat(3, max-content); `; function NotificationListItem(props) { @@ -51,7 +47,6 @@ function NotificationListItem(props) { to={{ pathname: detailUrl, }} - css="margin-right: 1.5em;" > {notification.name} @@ -61,51 +56,47 @@ function NotificationListItem(props) { {typeLabels[notification.notification_type]} , - - - toggleNotification( - notification.id, - startedTurnedOn, - 'started' - ) - } - aria-label={i18n._(t`Toggle notification start`)} - /> - - toggleNotification( - notification.id, - successTurnedOn, - 'success' - ) - } - aria-label={i18n._(t`Toggle notification success`)} - /> - - toggleNotification(notification.id, errorTurnedOn, 'error') - } - aria-label={i18n._(t`Toggle notification failure`)} - /> - , ]} /> + + + toggleNotification(notification.id, startedTurnedOn, 'started') + } + aria-label={i18n._(t`Toggle notification start`)} + /> + + toggleNotification(notification.id, successTurnedOn, 'success') + } + aria-label={i18n._(t`Toggle notification success`)} + /> + + toggleNotification(notification.id, errorTurnedOn, 'error') + } + aria-label={i18n._(t`Toggle notification failure`)} + /> + ); diff --git a/awx/ui_next/src/components/NotificationList/__snapshots__/NotificationListItem.test.jsx.snap b/awx/ui_next/src/components/NotificationList/__snapshots__/NotificationListItem.test.jsx.snap index c531e44c0c..2ec6011b1d 100644 --- a/awx/ui_next/src/components/NotificationList/__snapshots__/NotificationListItem.test.jsx.snap +++ b/awx/ui_next/src/components/NotificationList/__snapshots__/NotificationListItem.test.jsx.snap @@ -42,7 +42,7 @@ exports[` initially renders succe + initially renders succe Foo - , - + , + Slack - , - - - - - , + , ] } key=".0" @@ -99,453 +68,324 @@ exports[` initially renders succe
- - - -
- - - - - - - Foo - - - - - - -
-
-
-
- + + + +
+ + - - -
- Slack -
-
-
- - +
+ + + + + - - -
- - + - - + + + + + + + + - + - - + + + + + + + + - + - -
-
-
- - - + Failure + + + + + + + +
+
+
diff --git a/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.jsx b/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.jsx index c9d1b01247..0bad51f112 100644 --- a/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.jsx +++ b/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.jsx @@ -137,6 +137,7 @@ class ToolbarDeleteButton extends React.Component { render() { const { itemsToDelete, pluralizedItemName, i18n } = this.props; const { isModalOpen } = this.state; + const modalTitle = i18n._(t`Delete ${pluralizedItemName}?`); const isDisabled = itemsToDelete.length === 0 || itemsToDelete.some(cannotDelete); @@ -161,7 +162,7 @@ class ToolbarDeleteButton extends React.Component { {isModalOpen && ( , ]} > - {i18n._(t`Are you sure you want to delete:`)} -
+
{i18n._(t`This action will delete the following:`)}
{itemsToDelete.map(item => ( {item.name || item.username}
))} -
)}
diff --git a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.jsx b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.jsx index 1baefcf6a5..e321a14b2b 100644 --- a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.jsx +++ b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.jsx @@ -232,7 +232,7 @@ class ResourceAccessList extends React.Component { )} diff --git a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap index 50969673ec..9ea0627668 100644 --- a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap +++ b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap @@ -73,7 +73,7 @@ exports[` should render initially 1`] = ` aria-describedby="pf-modal-0" aria-label="Remove {0} Access" 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" > -

- - Remove {0} Access - -

+
+ +

+ Remove {0} Access +

+
+
should render initially 1`] = `

If you only want to remove access for this particular user, please remove them from the team. -
should render initially 1`] = `

If you only want to remove access for this particular user, please remove them from the team. -
@@ -423,41 +546,13 @@ exports[` should render initially 1`] = `

If you only want to remove access for this particular user, please remove them from the team. - - - - -
{label} - {selected.map(item => diff --git a/awx/ui_next/src/components/Sparkline/Sparkline.jsx b/awx/ui_next/src/components/Sparkline/Sparkline.jsx index d9346758c3..06ec23ae3a 100644 --- a/awx/ui_next/src/components/Sparkline/Sparkline.jsx +++ b/awx/ui_next/src/components/Sparkline/Sparkline.jsx @@ -2,7 +2,7 @@ import React, { Fragment } from 'react'; import { arrayOf, object } from 'prop-types'; import { withI18n } from '@lingui/react'; 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 styled from 'styled-components'; import { t } from '@lingui/macro'; @@ -13,6 +13,10 @@ import { JOB_TYPE_URL_SEGMENTS } from '@constants'; const Link = styled(props => <_Link {...props} />)` margin-right: 5px; `; + +const Wrapper = styled.div` + display: inline-flex; +`; /* eslint-enable react/jsx-pascal-case */ const Sparkline = ({ i18n, jobs }) => { @@ -32,13 +36,15 @@ const Sparkline = ({ i18n, jobs }) => { ); - return jobs.map(job => ( + const statusIcons = jobs.map(job => ( )); + + return {statusIcons}; }; Sparkline.propTypes = { diff --git a/awx/ui_next/src/components/Sparkline/index.js b/awx/ui_next/src/components/Sparkline/index.js index f7b30c0d98..7477abfdcc 100644 --- a/awx/ui_next/src/components/Sparkline/index.js +++ b/awx/ui_next/src/components/Sparkline/index.js @@ -1,2 +1 @@ -export { default as Sparkline } from './Sparkline'; -export { default as StatusIcon } from './StatusIcon'; +export { default } from './Sparkline'; diff --git a/awx/ui_next/src/components/Sparkline/StatusIcon.jsx b/awx/ui_next/src/components/StatusIcon/StatusIcon.jsx similarity index 96% rename from awx/ui_next/src/components/Sparkline/StatusIcon.jsx rename to awx/ui_next/src/components/StatusIcon/StatusIcon.jsx index d89d5592d4..e04a169121 100644 --- a/awx/ui_next/src/components/Sparkline/StatusIcon.jsx +++ b/awx/ui_next/src/components/StatusIcon/StatusIcon.jsx @@ -12,8 +12,12 @@ const Pulse = keyframes` `; const Wrapper = styled.div` - width: 14px; + align-items: center; + display: flex; + flex-flow: column nowrap; height: 14px; + margin: 5px 0; + width: 14px; `; const WhiteTop = styled.div` diff --git a/awx/ui_next/src/components/Sparkline/StatusIcon.test.jsx b/awx/ui_next/src/components/StatusIcon/StatusIcon.test.jsx similarity index 100% rename from awx/ui_next/src/components/Sparkline/StatusIcon.test.jsx rename to awx/ui_next/src/components/StatusIcon/StatusIcon.test.jsx diff --git a/awx/ui_next/src/components/StatusIcon/index.js b/awx/ui_next/src/components/StatusIcon/index.js new file mode 100644 index 0000000000..d026e41e3e --- /dev/null +++ b/awx/ui_next/src/components/StatusIcon/index.js @@ -0,0 +1 @@ +export { default } from './StatusIcon'; diff --git a/awx/ui_next/src/components/VerticalSeparator/VerticalSeparator.jsx b/awx/ui_next/src/components/VerticalSeparator/VerticalSeparator.jsx deleted file mode 100644 index ebe53e5ada..0000000000 --- a/awx/ui_next/src/components/VerticalSeparator/VerticalSeparator.jsx +++ /dev/null @@ -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 = () => ( -
- -
-); - -export default VerticalSeparator; diff --git a/awx/ui_next/src/components/VerticalSeparator/VerticalSeparator.test.jsx b/awx/ui_next/src/components/VerticalSeparator/VerticalSeparator.test.jsx deleted file mode 100644 index 3de7ae1e3e..0000000000 --- a/awx/ui_next/src/components/VerticalSeparator/VerticalSeparator.test.jsx +++ /dev/null @@ -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(); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui_next/src/components/VerticalSeparator/index.js b/awx/ui_next/src/components/VerticalSeparator/index.js deleted file mode 100644 index 73c51cd580..0000000000 --- a/awx/ui_next/src/components/VerticalSeparator/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './VerticalSeparator'; diff --git a/awx/ui_next/src/components/Workflow/WorkflowActionTooltipItem.test.jsx b/awx/ui_next/src/components/Workflow/WorkflowActionTooltipItem.test.jsx index ed6067a3c0..4f06a7f02f 100644 --- a/awx/ui_next/src/components/Workflow/WorkflowActionTooltipItem.test.jsx +++ b/awx/ui_next/src/components/Workflow/WorkflowActionTooltipItem.test.jsx @@ -4,7 +4,7 @@ import WorkflowActionTooltipItem from './WorkflowActionTooltipItem'; describe('WorkflowActionTooltipItem', () => { test('successfully mounts', () => { - const wrapper = mount(); + const wrapper = mount(); expect(wrapper).toHaveLength(1); }); }); diff --git a/awx/ui_next/src/index.jsx b/awx/ui_next/src/index.jsx index 06d5245d81..f2abdbc1ff 100644 --- a/awx/ui_next/src/index.jsx +++ b/awx/ui_next/src/index.jsx @@ -5,7 +5,6 @@ import { I18n } from '@lingui/react'; import { t } from '@lingui/macro'; import '@patternfly/react-core/dist/styles/base.css'; -import './app.scss'; import { isAuthenticated } from '@util/auth'; import Background from '@components/Background'; diff --git a/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx b/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx index f20b34ce10..290275fb15 100644 --- a/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx @@ -158,7 +158,7 @@ function CredentialDetail({ i18n, credential }) { {deletionError && ( setDeletionError(null)} > diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx index 87451cf0e7..40d7a65657 100644 --- a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx @@ -125,9 +125,9 @@ function CredentialList({ i18n }) { itemsToDelete={selected} pluralizedItemName={i18n._(t`Credentials`)} />, - canAdd && ( - - ), + ...(canAdd + ? [] + : []), ]} /> )} @@ -135,7 +135,7 @@ function CredentialList({ i18n }) { diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.test.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.test.jsx index 8ec83f2f49..5bab169d03 100644 --- a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.test.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.test.jsx @@ -54,44 +54,44 @@ describe('', () => { test('should check and uncheck the row item', async () => { expect( - wrapper.find('PFDataListCheck[id="select-credential-1"]').props().checked + wrapper.find('DataListCheck[id="select-credential-1"]').props().checked ).toBe(false); await act(async () => { wrapper - .find('PFDataListCheck[id="select-credential-1"]') + .find('DataListCheck[id="select-credential-1"]') .invoke('onChange')(true); }); wrapper.update(); expect( - wrapper.find('PFDataListCheck[id="select-credential-1"]').props().checked + wrapper.find('DataListCheck[id="select-credential-1"]').props().checked ).toBe(true); await act(async () => { wrapper - .find('PFDataListCheck[id="select-credential-1"]') + .find('DataListCheck[id="select-credential-1"]') .invoke('onChange')(false); }); wrapper.update(); expect( - wrapper.find('PFDataListCheck[id="select-credential-1"]').props().checked + wrapper.find('DataListCheck[id="select-credential-1"]').props().checked ).toBe(false); }); 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); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(true); }); wrapper.update(); - wrapper.find('PFDataListCheck').forEach(el => { + wrapper.find('DataListCheck').forEach(el => { expect(el.props().checked).toBe(true); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(false); }); wrapper.update(); - wrapper.find('PFDataListCheck').forEach(el => { + wrapper.find('DataListCheck').forEach(el => { expect(el.props().checked).toBe(false); }); }); @@ -102,7 +102,7 @@ describe('', () => { await act(async () => { wrapper - .find('PFDataListCheck[id="select-credential-3"]') + .find('DataListCheck[id="select-credential-3"]') .invoke('onChange')(); }); wrapper.update(); @@ -119,7 +119,7 @@ describe('', () => { ); await act(async () => { wrapper - .find('PFDataListCheck[id="select-credential-2"]') + .find('DataListCheck[id="select-credential-2"]') .invoke('onChange')(); }); wrapper.update(); diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx index 937dde8607..9b519bfcb1 100644 --- a/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx @@ -5,25 +5,18 @@ import { t } from '@lingui/macro'; import { Link } from 'react-router-dom'; import { Button, + DataListAction, + DataListCell, + DataListCheck, DataListItem, DataListItemRow, - DataListItemCells as _DataListItemCells, + DataListItemCells, Tooltip, } from '@patternfly/react-core'; 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'; -const DataListItemCells = styled(_DataListItemCells)` - ${DataListCell}:first-child { - flex-grow: 2; - } -`; - function CredentialListItem({ credential, detailUrl, @@ -50,7 +43,6 @@ function CredentialListItem({ - {credential.name} @@ -58,21 +50,25 @@ function CredentialListItem({ {credential.summary_fields.credential_type.name} , - - {canEdit && ( - - - - )} - , ]} /> + + {canEdit && ( + + + + )} + ); diff --git a/awx/ui_next/src/screens/Host/HostDetail/HostDetail.jsx b/awx/ui_next/src/screens/Host/HostDetail/HostDetail.jsx index f3c70b066f..c6c1814667 100644 --- a/awx/ui_next/src/screens/Host/HostDetail/HostDetail.jsx +++ b/awx/ui_next/src/screens/Host/HostDetail/HostDetail.jsx @@ -9,7 +9,7 @@ import AlertModal from '@components/AlertModal'; import ErrorDetail from '@components/ErrorDetail'; import { DetailList, Detail, UserDateDetail } from '@components/DetailList'; import { VariablesDetail } from '@components/CodeMirrorInput'; -import { Sparkline } from '@components/Sparkline'; +import Sparkline from '@components/Sparkline'; import DeleteButton from '@components/DeleteButton'; import { HostsAPI } from '@api'; @@ -69,7 +69,7 @@ function HostDetail({ host, i18n, onUpdateHost }) { if (toggleError && !toggleLoading) { return ( setToggleError(false)} @@ -83,7 +83,7 @@ function HostDetail({ host, i18n, onUpdateHost }) { return ( setDeletionError(false)} > @@ -107,7 +107,6 @@ function HostDetail({ host, i18n, onUpdateHost }) { } label={i18n._(t`Activity`)} /> diff --git a/awx/ui_next/src/screens/Host/HostList/HostList.jsx b/awx/ui_next/src/screens/Host/HostList/HostList.jsx index a84af76a4c..0edc224d30 100644 --- a/awx/ui_next/src/screens/Host/HostList/HostList.jsx +++ b/awx/ui_next/src/screens/Host/HostList/HostList.jsx @@ -224,9 +224,14 @@ class HostsList extends Component { itemsToDelete={selected} pluralizedItemName={i18n._(t`Hosts`)} />, - canAdd ? ( - - ) : null, + ...(canAdd + ? [ + , + ] + : []), ]} /> )} @@ -250,7 +255,7 @@ class HostsList extends Component { {toggleError && !toggleLoading && ( diff --git a/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx b/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx index cb81224fd3..2dda211169 100644 --- a/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx +++ b/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx @@ -4,6 +4,9 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Button, + DataListAction as _DataListAction, + DataListCell, + DataListCheck, DataListItem, DataListItemRow, DataListItemCells, @@ -13,11 +16,16 @@ import { import { Link } from 'react-router-dom'; import { PencilAltIcon } from '@patternfly/react-icons'; -import DataListCell from '@components/DataListCell'; -import DataListCheck from '@components/DataListCheck'; -import { Sparkline } from '@components/Sparkline'; -import VerticalSeparator from '@components/VerticalSeparator'; +import Sparkline from '@components/Sparkline'; 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 { static propTypes = { @@ -56,7 +64,6 @@ class HostListItem extends React.Component { - {host.name} @@ -67,9 +74,7 @@ class HostListItem extends React.Component { {host.summary_fields.inventory && ( - - {i18n._(t`Inventory`)} - + {i18n._(t`Inventory`)} )} , - - - onToggleHost(host)} - aria-label={i18n._(t`Toggle host`)} - /> - - , - - {host.summary_fields.user_capabilities.edit && ( - - - - )} - , ]} /> + + + onToggleHost(host)} + aria-label={i18n._(t`Toggle host`)} + /> + + {host.summary_fields.user_capabilities.edit && ( + + + + )} + ); diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.jsx index 36aac69ed9..eaee439dfe 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.jsx @@ -98,7 +98,7 @@ function InventoryGroupDetail({ i18n, inventoryGroup }) { )} {error && ( setError(false)} diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx index 94c4edb7f4..77f9ccab26 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx @@ -6,18 +6,17 @@ import { Group } from '@types'; import { Button, + DataListAction, + DataListCell, + DataListCheck, DataListItem, - DataListItemRow, DataListItemCells, + DataListItemRow, Tooltip, } from '@patternfly/react-core'; import { Link } from 'react-router-dom'; import { PencilAltIcon } from '@patternfly/react-icons'; -import DataListCell from '@components/DataListCell'; -import DataListCheck from '@components/DataListCheck'; -import VerticalSeparator from '@components/VerticalSeparator'; - function InventoryGroupItem({ i18n, group, @@ -40,23 +39,26 @@ function InventoryGroupItem({ /> - + {group.name} , - - {group.summary_fields.user_capabilities.edit && ( - - - - )} - , ]} /> + + {group.summary_fields.user_capabilities.edit && ( + + + + )} + ); diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx index a60c16c973..f511d83156 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx @@ -238,12 +238,14 @@ function InventoryGroupsList({ i18n, location, match }) {
, - canAdd && ( - - ), + ...(canAdd + ? [ + , + ] + : []), ]} /> )} @@ -259,7 +261,7 @@ function InventoryGroupsList({ i18n, location, match }) { {deletionError && ( setDeletionError(null)} > diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx index 3c9df3ec3b..b0f4521c30 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx @@ -98,46 +98,46 @@ describe('', () => { test('should check and uncheck the row item', async () => { expect( - wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked + wrapper.find('DataListCheck[id="select-group-1"]').props().checked ).toBe(false); await act(async () => { - wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')( + wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')( true ); }); wrapper.update(); expect( - wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked + wrapper.find('DataListCheck[id="select-group-1"]').props().checked ).toBe(true); await act(async () => { - wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')( + wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')( false ); }); wrapper.update(); expect( - wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked + wrapper.find('DataListCheck[id="select-group-1"]').props().checked ).toBe(false); }); 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); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(true); }); wrapper.update(); - wrapper.find('PFDataListCheck').forEach(el => { + wrapper.find('DataListCheck').forEach(el => { expect(el.props().checked).toBe(true); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(false); }); wrapper.update(); - wrapper.find('PFDataListCheck').forEach(el => { + wrapper.find('DataListCheck').forEach(el => { expect(el.props().checked).toBe(false); }); }); @@ -157,7 +157,7 @@ describe('', () => { Promise.reject(new Error()) ); await act(async () => { - wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')(); + wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')(); }); wrapper.update(); await act(async () => { @@ -191,7 +191,7 @@ describe('', () => { }) ); await act(async () => { - wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')(); + wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')(); }); wrapper.update(); await act(async () => { @@ -213,7 +213,7 @@ describe('', () => { .find('ModalBoxFooter Button[aria-label="Delete"]') .invoke('onClick')(); }); - await waitForElement(wrapper, { title: 'Error!', variant: 'danger' }); + await waitForElement(wrapper, { title: 'Error!', variant: 'error' }); await act(async () => { wrapper.find('ModalBoxCloseButton').invoke('onClose')(); }); diff --git a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx index 305fdfca92..cf7678f0e3 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx @@ -4,20 +4,27 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Button, + DataListAction as _DataListAction, + DataListCell, + DataListCheck, DataListItem, - DataListItemRow, DataListItemCells, + DataListItemRow, Switch, Tooltip, } from '@patternfly/react-core'; import { Link } from 'react-router-dom'; import { PencilAltIcon } from '@patternfly/react-icons'; - -import DataListCell from '@components/DataListCell'; -import DataListCheck from '@components/DataListCheck'; -import { Sparkline } from '@components/Sparkline'; -import VerticalSeparator from '@components/VerticalSeparator'; +import Sparkline from '@components/Sparkline'; 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) { const { @@ -50,7 +57,6 @@ function InventoryHostItem(props) { - {host.name} @@ -58,41 +64,42 @@ function InventoryHostItem(props) { , - - - toggleHost(host)} - aria-label={i18n._(t`Toggle host`)} - /> - - , - - {host.summary_fields.user_capabilities?.edit && ( - - - - )} - , ]} /> + + + toggleHost(host)} + aria-label={i18n._(t`Toggle host`)} + /> + + {host.summary_fields.user_capabilities?.edit && ( + + + + )} + ); diff --git a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx index df4d7783fd..d59890040c 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx @@ -165,12 +165,14 @@ function InventoryHostList({ i18n, location, match }) { itemsToDelete={selected} pluralizedItemName={i18n._(t`Hosts`)} />, - canAdd && ( - - ), + ...(canAdd + ? [ + , + ] + : []), ]} /> )} @@ -198,7 +200,7 @@ function InventoryHostList({ i18n, location, match }) { {toggleError && !toggleLoading && ( setToggleError(false)} @@ -211,7 +213,7 @@ function InventoryHostList({ i18n, location, match }) { {deletionError && ( setDeletionError(null)} > diff --git a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx index 0a60a71ada..6e8c5ffdb6 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx @@ -104,48 +104,48 @@ describe('', () => { test('should check and uncheck the row item', async () => { expect( - wrapper.find('PFDataListCheck[id="select-host-1"]').props().checked + wrapper.find('DataListCheck[id="select-host-1"]').props().checked ).toBe(false); await act(async () => { - wrapper.find('PFDataListCheck[id="select-host-1"]').invoke('onChange')( + wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')( true ); }); wrapper.update(); expect( - wrapper.find('PFDataListCheck[id="select-host-1"]').props().checked + wrapper.find('DataListCheck[id="select-host-1"]').props().checked ).toBe(true); await act(async () => { - wrapper.find('PFDataListCheck[id="select-host-1"]').invoke('onChange')( + wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')( false ); }); wrapper.update(); expect( - wrapper.find('PFDataListCheck[id="select-host-1"]').props().checked + wrapper.find('DataListCheck[id="select-host-1"]').props().checked ).toBe(false); }); 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); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(true); }); wrapper.update(); - wrapper.find('PFDataListCheck').forEach(el => { + wrapper.find('DataListCheck').forEach(el => { expect(el.props().checked).toBe(true); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(false); }); wrapper.update(); - wrapper.find('PFDataListCheck').forEach(el => { + wrapper.find('DataListCheck').forEach(el => { expect(el.props().checked).toBe(false); }); }); @@ -186,7 +186,7 @@ describe('', () => { test('delete button is disabled if user does not have delete capabilities on a selected host', async () => { await act(async () => { - wrapper.find('PFDataListCheck[id="select-host-3"]').invoke('onChange')(); + wrapper.find('DataListCheck[id="select-host-3"]').invoke('onChange')(); }); wrapper.update(); expect(wrapper.find('ToolbarDeleteButton button').props().disabled).toBe( @@ -197,7 +197,7 @@ describe('', () => { test('should call api delete hosts for each selected host', async () => { HostsAPI.destroy = jest.fn(); await act(async () => { - wrapper.find('PFDataListCheck[id="select-host-1"]').invoke('onChange')(); + wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')(); }); wrapper.update(); await act(async () => { @@ -220,7 +220,7 @@ describe('', () => { }) ); await act(async () => { - wrapper.find('PFDataListCheck[id="select-host-1"]').invoke('onChange')(); + wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')(); }); wrapper.update(); await act(async () => { @@ -242,7 +242,7 @@ describe('', () => { Promise.reject(new Error()) ); await act(async () => { - wrapper.find('PFDataListCheck[id="select-host-1"]').invoke('onChange')(); + wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')(); }); wrapper.update(); await act(async () => { diff --git a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx index 6ac8aef809..6884adf4f7 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx @@ -208,7 +208,7 @@ class InventoriesList extends Component { itemsToDelete={selected} pluralizedItemName="Inventories" />, - canAdd && addButton, + ...(canAdd ? [addButton] : []), ]} /> )} @@ -231,7 +231,7 @@ class InventoriesList extends Component { diff --git a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx index 168ac9e61b..ea5e445994 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx @@ -3,18 +3,18 @@ import { string, bool, func } from 'prop-types'; import { withI18n } from '@lingui/react'; import { Button, + DataListAction, + DataListCell, + DataListCheck, DataListItem, - DataListItemRow, DataListItemCells, + DataListItemRow, Tooltip, } from '@patternfly/react-core'; import { t } from '@lingui/macro'; import { Link } from 'react-router-dom'; 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'; class InventoryListItem extends React.Component { @@ -44,7 +44,6 @@ class InventoryListItem extends React.Component { - {inventory.name} @@ -54,25 +53,27 @@ class InventoryListItem extends React.Component { ? i18n._(t`Smart Inventory`) : i18n._(t`Inventory`)} , - - {inventory.summary_fields.user_capabilities.edit && ( - - - - )} - , ]} /> + + {inventory.summary_fields.user_capabilities.edit && ( + + + + )} + ); diff --git a/awx/ui_next/src/screens/Inventory/shared/InventoryGroupsDeleteModal.jsx b/awx/ui_next/src/screens/Inventory/shared/InventoryGroupsDeleteModal.jsx index ca86d722b5..5ce7f635c4 100644 --- a/awx/ui_next/src/screens/Inventory/shared/InventoryGroupsDeleteModal.jsx +++ b/awx/ui_next/src/screens/Inventory/shared/InventoryGroupsDeleteModal.jsx @@ -27,7 +27,7 @@ const InventoryGroupsDeleteModal = ({ isOpen={isModalOpen} variant="danger" 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} actions={[ @@ -60,7 +60,7 @@ const InventoryGroupsDeleteModal = ({ return {group.name}; })} -
+
setErrorMsg()} title={i18n._(t`Job Delete Error`)} > diff --git a/awx/ui_next/src/screens/Job/JobList/JobList.jsx b/awx/ui_next/src/screens/Job/JobList/JobList.jsx index 1779febeed..94bb99b73e 100644 --- a/awx/ui_next/src/screens/Job/JobList/JobList.jsx +++ b/awx/ui_next/src/screens/Job/JobList/JobList.jsx @@ -236,7 +236,7 @@ function JobList({ i18n }) { diff --git a/awx/ui_next/src/screens/Job/JobList/JobList.test.jsx b/awx/ui_next/src/screens/Job/JobList/JobList.test.jsx index 7379b4079a..75632f2f7b 100644 --- a/awx/ui_next/src/screens/Job/JobList/JobList.test.jsx +++ b/awx/ui_next/src/screens/Job/JobList/JobList.test.jsx @@ -23,6 +23,7 @@ const mockResults = [ summary_fields: { user_capabilities: { delete: true, + start: true, }, }, }, @@ -34,6 +35,7 @@ const mockResults = [ summary_fields: { user_capabilities: { delete: true, + start: true, }, }, }, @@ -45,6 +47,7 @@ const mockResults = [ summary_fields: { user_capabilities: { delete: true, + start: true, }, }, }, @@ -56,6 +59,7 @@ const mockResults = [ summary_fields: { user_capabilities: { delete: true, + start: true, }, }, }, @@ -67,6 +71,7 @@ const mockResults = [ summary_fields: { user_capabilities: { delete: true, + edit: true, }, }, }, @@ -78,6 +83,7 @@ const mockResults = [ summary_fields: { user_capabilities: { delete: true, + edit: true, }, }, }, diff --git a/awx/ui_next/src/screens/Job/JobList/JobListItem.jsx b/awx/ui_next/src/screens/Job/JobList/JobListItem.jsx index 5cbdec219b..30cdd1feaa 100644 --- a/awx/ui_next/src/screens/Job/JobList/JobListItem.jsx +++ b/awx/ui_next/src/screens/Job/JobList/JobListItem.jsx @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import { Link } from 'react-router-dom'; -import styled from 'styled-components'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Button, + DataListAction, DataListCell, DataListCheck, DataListItem, @@ -14,32 +14,31 @@ import { } from '@patternfly/react-core'; import { RocketIcon } from '@patternfly/react-icons'; import LaunchButton from '@components/LaunchButton'; -import { StatusIcon } from '@components/Sparkline'; +import StatusIcon from '@components/StatusIcon'; import { toTitleCase } from '@util/strings'; import { formatDateString } from '@util/dates'; import { JOB_TYPE_URL_SEGMENTS } from '@constants'; -const PaddedIcon = styled(StatusIcon)` - margin-right: 20px; -`; - class JobListItem extends Component { render() { const { i18n, job, isSelected, onSelect } = this.props; + const labelId = `check-action-${job.id}`; return ( - + - {job.status && } + + {job.status && } + , + {formatDateString(job.finished)} , - - {job.type !== 'system_job' && - job.summary_fields.user_capabilities.start && ( - - - {({ handleRelaunch }) => ( - - )} - - - )} - , ]} /> + {job.type !== 'system_job' && + job.summary_fields?.user_capabilities?.start && ( + + + + {({ handleRelaunch }) => ( + + )} + + + + )} ); diff --git a/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.jsx b/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.jsx index 989270325d..7ae3f50b62 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.jsx @@ -4,7 +4,7 @@ import CodeMirrorInput from '@components/CodeMirrorInput'; import ContentEmpty from '@components/ContentEmpty'; import PropTypes from 'prop-types'; import { DetailList, Detail } from '@components/DetailList'; -import { StatusIcon } from '@components/Sparkline'; +import StatusIcon from '@components/StatusIcon'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import styled from 'styled-components'; diff --git a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx index 76aed50c36..da850b6878 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx @@ -16,7 +16,7 @@ import { CardBody } from '@components/Card'; import ContentError from '@components/ContentError'; import ContentLoading from '@components/ContentLoading'; import ErrorDetail from '@components/ErrorDetail'; -import { StatusIcon } from '@components/Sparkline'; +import StatusIcon from '@components/StatusIcon'; import JobEvent from './JobEvent'; import JobEventSkeleton from './JobEventSkeleton'; diff --git a/awx/ui_next/src/screens/Job/JobOutput/shared/OutputToolbar.jsx b/awx/ui_next/src/screens/Job/JobOutput/shared/OutputToolbar.jsx index d5b79749ae..81e75938bc 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/shared/OutputToolbar.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/shared/OutputToolbar.jsx @@ -9,7 +9,6 @@ import { TrashAltIcon, } from '@patternfly/react-icons'; import { Badge as PFBadge, Button, Tooltip } from '@patternfly/react-core'; -import VerticalSeparator from '@components/VerticalSeparator'; import DeleteButton from '@components/DeleteButton'; import LaunchButton from '@components/LaunchButton'; @@ -123,8 +122,6 @@ const OutputToolbar = ({ i18n, job, onDelete }) => { - - {job.type !== 'system_job' && job.summary_fields.user_capabilities?.start && ( diff --git a/awx/ui_next/src/screens/Job/WorkflowOutput/WorkflowOutputNode.jsx b/awx/ui_next/src/screens/Job/WorkflowOutput/WorkflowOutputNode.jsx index 8b2f841a62..a0d8fd361a 100644 --- a/awx/ui_next/src/screens/Job/WorkflowOutput/WorkflowOutputNode.jsx +++ b/awx/ui_next/src/screens/Job/WorkflowOutput/WorkflowOutputNode.jsx @@ -5,7 +5,7 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import styled from 'styled-components'; import { func, shape } from 'prop-types'; -import { StatusIcon } from '@components/Sparkline'; +import StatusIcon from '@components/StatusIcon'; import { WorkflowNodeTypeLetter } from '@components/Workflow'; import { secondsToHHMMSS } from '@util/dates'; import { constants as wfConstants } from '@components/Workflow/WorkflowUtils'; diff --git a/awx/ui_next/src/screens/Job/WorkflowOutput/WorkflowOutputToolbar.jsx b/awx/ui_next/src/screens/Job/WorkflowOutput/WorkflowOutputToolbar.jsx index 26907ab3e4..a2127fd800 100644 --- a/awx/ui_next/src/screens/Job/WorkflowOutput/WorkflowOutputToolbar.jsx +++ b/awx/ui_next/src/screens/Job/WorkflowOutput/WorkflowOutputToolbar.jsx @@ -8,12 +8,11 @@ import { t } from '@lingui/macro'; import { shape } from 'prop-types'; import { Badge as PFBadge, Button, Tooltip } from '@patternfly/react-core'; import { CompassIcon, WrenchIcon } from '@patternfly/react-icons'; -import { StatusIcon } from '@components/Sparkline'; -import VerticalSeparator from '@components/VerticalSeparator'; +import StatusIcon from '@components/StatusIcon'; import styled from 'styled-components'; const Toolbar = styled.div` - align-items: center + align-items: center; border-bottom: 1px solid grey; display: flex; height: 56px; @@ -73,7 +72,6 @@ function WorkflowOutputToolbar({ i18n, job }) {
{i18n._(t`Total Nodes`)}
{totalNodes} - setDeletionError(null)} > diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx index 380011d545..1b116e3096 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx @@ -146,9 +146,9 @@ function OrganizationsList({ i18n }) { itemsToDelete={selected} pluralizedItemName="Organizations" />, - canAdd ? ( - - ) : null, + ...(canAdd + ? [] + : []), ]} /> )} @@ -169,7 +169,7 @@ function OrganizationsList({ i18n }) { diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.test.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.test.jsx index 5c7fb53966..8151033123 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.test.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.test.jsx @@ -23,6 +23,7 @@ const mockOrganizations = { }, user_capabilities: { delete: true, + edit: true, }, }, }, @@ -37,6 +38,7 @@ const mockOrganizations = { }, user_capabilities: { delete: true, + edit: true, }, }, }, @@ -51,6 +53,7 @@ const mockOrganizations = { }, user_capabilities: { delete: true, + edit: true, }, }, }, diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx index 261dee5700..f2b1a4f8ee 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx @@ -5,37 +5,29 @@ import { t } from '@lingui/macro'; import { Badge as PFBadge, Button, + DataListAction, + DataListCell, + DataListCheck, DataListItem, - DataListItemRow, DataListItemCells, + DataListItemRow, Tooltip, } from '@patternfly/react-core'; import { Link } from 'react-router-dom'; import styled from 'styled-components'; 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'; const Badge = styled(PFBadge)` - align-items: center; - display: flex; - justify-content: center; - margin-left: 10px; + margin-left: 8px; `; const ListGroup = styled.span` - display: flex; - margin-left: 40px; + margin-left: 24px; - @media screen and (min-width: 768px) { - margin-left: 20px; - - &:first-of-type { - margin-left: 0; - } + &:first-of-type { + margin-left: 0; } `; @@ -63,7 +55,6 @@ function OrganizationListItem({ - {organization.name} @@ -84,21 +75,25 @@ function OrganizationListItem({ , - - {organization.summary_fields.user_capabilities.edit && ( - - - - )} - , ]} /> + + {organization.summary_fields.user_capabilities.edit && ( + + + + )} +
); diff --git a/awx/ui_next/src/screens/Project/ProjectDetail/ProjectDetail.jsx b/awx/ui_next/src/screens/Project/ProjectDetail/ProjectDetail.jsx index 78f746c8a0..a0ef3c5041 100644 --- a/awx/ui_next/src/screens/Project/ProjectDetail/ProjectDetail.jsx +++ b/awx/ui_next/src/screens/Project/ProjectDetail/ProjectDetail.jsx @@ -177,7 +177,7 @@ function ProjectDetail({ project, i18n }) { {deletionError && ( setDeletionError(null)} > diff --git a/awx/ui_next/src/screens/Project/ProjectList/ProjectList.jsx b/awx/ui_next/src/screens/Project/ProjectList/ProjectList.jsx index 95d12e828a..4311f17a61 100644 --- a/awx/ui_next/src/screens/Project/ProjectList/ProjectList.jsx +++ b/awx/ui_next/src/screens/Project/ProjectList/ProjectList.jsx @@ -206,9 +206,14 @@ class ProjectsList extends Component { itemsToDelete={selected} pluralizedItemName={i18n._(t`Projects`)} />, - canAdd ? ( - - ) : null, + ...(canAdd + ? [ + , + ] + : []), ]} /> )} @@ -231,7 +236,7 @@ class ProjectsList extends Component { diff --git a/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx b/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx index 10626c5c70..b540be821a 100644 --- a/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx +++ b/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx @@ -3,6 +3,9 @@ import { string, bool, func } from 'prop-types'; import { withI18n } from '@lingui/react'; import { Button, + DataListAction as _DataListAction, + DataListCell, + DataListCheck, DataListItem, DataListItemRow, DataListItemCells, @@ -11,16 +14,20 @@ import { import { t } from '@lingui/macro'; import { Link } from 'react-router-dom'; import { PencilAltIcon, SyncIcon } from '@patternfly/react-icons'; +import styled from 'styled-components'; import ClipboardCopyButton from '@components/ClipboardCopyButton'; -import DataListCell from '@components/DataListCell'; -import DataListCheck from '@components/DataListCheck'; import ProjectSyncButton from '../shared/ProjectSyncButton'; -import { StatusIcon } from '@components/Sparkline'; -import VerticalSeparator from '@components/VerticalSeparator'; +import StatusIcon from '@components/StatusIcon'; import { toTitleCase } from '@util/strings'; 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 { static propTypes = { project: Project.isRequired, @@ -73,8 +80,7 @@ class ProjectListItem extends React.Component { /> - + {project.summary_fields.last_job && ( )} - + , + + {project.name} , @@ -105,7 +109,7 @@ class ProjectListItem extends React.Component { ? i18n._(t`Manual`) : toTitleCase(project.scm_type)} , - + {project.scm_revision.substring(0, 7)} {project.scm_revision ? ( ) : null} , - - {project.summary_fields.user_capabilities.start && ( - - - {handleSync => ( - - )} - - - )} - , - - {project.summary_fields.user_capabilities.edit && ( - - - - )} - , ]} /> + + {project.summary_fields.user_capabilities.start && ( + + + {handleSync => ( + + )} + + + )} + {project.summary_fields.user_capabilities.edit && ( + + + + )} + ); diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSyncButton.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSyncButton.jsx index bc3e40d022..a2700169b4 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectSyncButton.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectSyncButton.jsx @@ -54,7 +54,7 @@ class ProjectSyncButton extends React.Component { {syncError && ( diff --git a/awx/ui_next/src/screens/Team/TeamDetail/TeamDetail.jsx b/awx/ui_next/src/screens/Team/TeamDetail/TeamDetail.jsx index 90f994aa6b..9383ca148c 100644 --- a/awx/ui_next/src/screens/Team/TeamDetail/TeamDetail.jsx +++ b/awx/ui_next/src/screens/Team/TeamDetail/TeamDetail.jsx @@ -83,7 +83,7 @@ function TeamDetail({ team, i18n }) { {deletionError && ( setDeletionError(null)} > diff --git a/awx/ui_next/src/screens/Team/TeamList/TeamList.jsx b/awx/ui_next/src/screens/Team/TeamList/TeamList.jsx index 38afbc0213..e3912890e6 100644 --- a/awx/ui_next/src/screens/Team/TeamList/TeamList.jsx +++ b/awx/ui_next/src/screens/Team/TeamList/TeamList.jsx @@ -193,9 +193,14 @@ class TeamsList extends Component { itemsToDelete={selected} pluralizedItemName={i18n._(t`Teams`)} />, - canAdd ? ( - - ) : null, + ...(canAdd + ? [ + , + ] + : []), ]} /> )} @@ -218,7 +223,7 @@ class TeamsList extends Component { diff --git a/awx/ui_next/src/screens/Team/TeamList/TeamList.test.jsx b/awx/ui_next/src/screens/Team/TeamList/TeamList.test.jsx index 2eb816a5f6..063b958a70 100644 --- a/awx/ui_next/src/screens/Team/TeamList/TeamList.test.jsx +++ b/awx/ui_next/src/screens/Team/TeamList/TeamList.test.jsx @@ -17,6 +17,7 @@ const mockAPITeamsList = { summary_fields: { user_capabilities: { delete: true, + edit: true, }, }, }, @@ -27,6 +28,7 @@ const mockAPITeamsList = { summary_fields: { user_capabilities: { delete: true, + edit: true, }, }, }, @@ -37,6 +39,7 @@ const mockAPITeamsList = { summary_fields: { user_capabilities: { delete: true, + edit: true, }, }, }, diff --git a/awx/ui_next/src/screens/Team/TeamList/TeamListItem.jsx b/awx/ui_next/src/screens/Team/TeamList/TeamListItem.jsx index 6668547824..0b1e66e9d3 100644 --- a/awx/ui_next/src/screens/Team/TeamList/TeamListItem.jsx +++ b/awx/ui_next/src/screens/Team/TeamList/TeamListItem.jsx @@ -4,17 +4,17 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Button, + DataListAction, + DataListCell, + DataListCheck, DataListItem, - DataListItemRow, DataListItemCells, + DataListItemRow, Tooltip, } from '@patternfly/react-core'; import { Link } from 'react-router-dom'; 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'; class TeamListItem extends React.Component { @@ -28,6 +28,7 @@ class TeamListItem extends React.Component { render() { const { team, isSelected, onSelect, detailUrl, i18n } = this.props; const labelId = `check-action-${team.id}`; + return ( @@ -39,8 +40,7 @@ class TeamListItem extends React.Component { /> - + {team.name} @@ -48,9 +48,7 @@ class TeamListItem extends React.Component { {team.summary_fields.organization && ( - - {i18n._(t`Organization`)} - + {i18n._(t`Organization`)} @@ -59,21 +57,25 @@ class TeamListItem extends React.Component { )} , - - {team.summary_fields.user_capabilities.edit && ( - - - - )} - , ]} /> + + {team.summary_fields.user_capabilities.edit && ( + + + + )} + ); diff --git a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx index 2c63c6537e..999af636e2 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx @@ -338,7 +338,7 @@ function JobTemplateDetail({ i18n, template }) { {deletionError && ( setDeletionError(null)} > diff --git a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx index cc688886b0..04f19229c2 100644 --- a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx +++ b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx @@ -121,21 +121,25 @@ function TemplateList({ i18n }) { const canAddWFJT = wfjtActions && Object.prototype.hasOwnProperty.call(wfjtActions, 'POST'); const addButtonOptions = []; + if (canAddJT) { addButtonOptions.push({ label: i18n._(t`Template`), url: `/templates/job_template/add/`, }); } + if (canAddWFJT) { addButtonOptions.push({ label: i18n._(t`Workflow Template`), url: `/templates/workflow_job_template/add/`, }); } + const addButton = ( ); + return ( <> @@ -215,7 +219,7 @@ function TemplateList({ i18n }) { itemsToDelete={selected} pluralizedItemName="Templates" />, - (canAddJT || canAddWFJT) && addButton, + ...(canAddJT || canAddWFJT ? [addButton] : []), ]} /> )} @@ -234,7 +238,7 @@ function TemplateList({ i18n }) { diff --git a/awx/ui_next/src/screens/Template/TemplateList/TemplateListItem.jsx b/awx/ui_next/src/screens/Template/TemplateList/TemplateListItem.jsx index b3742dc574..b13eb92298 100644 --- a/awx/ui_next/src/screens/Template/TemplateList/TemplateListItem.jsx +++ b/awx/ui_next/src/screens/Template/TemplateList/TemplateListItem.jsx @@ -2,6 +2,9 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { Button, + DataListAction as _DataListAction, + DataListCell, + DataListCheck, DataListItem, DataListItemRow, DataListItemCells, @@ -15,14 +18,20 @@ import { RocketIcon, } from '@patternfly/react-icons'; -import DataListCell from '@components/DataListCell'; -import DataListCheck from '@components/DataListCheck'; 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 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 }) { + const labelId = `check-action-${template.id}`; const canLaunch = template.summary_fields.user_capabilities.start; const missingResourceIcon = @@ -32,21 +41,17 @@ function TemplateListItem({ i18n, template, isSelected, onSelect, detailUrl }) { !template.ask_inventory_on_launch)); return ( - + - {template.name} @@ -71,34 +76,41 @@ function TemplateListItem({ i18n, template, isSelected, onSelect, detailUrl }) { , - - {canLaunch && template.type === 'job_template' && ( - - - {({ handleLaunch }) => ( - - )} - - - )} - , - - {template.summary_fields.user_capabilities.edit && ( - - - - )} - , ]} /> + + {canLaunch && template.type === 'job_template' && ( + + + {({ handleLaunch }) => ( + + )} + + + )} + {template.summary_fields.user_capabilities.edit && ( + + + + )} + ); diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.jsx index e68dbb3e17..f97ed439fe 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.jsx @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { Link, useHistory } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; +import { WorkflowJobTemplatesAPI } from '@api'; import { Chip, ChipGroup, @@ -13,17 +14,16 @@ import { Label, } 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 ErrorDetail from '@components/ErrorDetail'; -import { DetailList, Detail, UserDateDetail } from '@components/DetailList'; +import { CardBody, CardActionsRow } from '@components/Card'; import { VariablesDetail } from '@components/CodeMirrorInput'; -import LaunchButton from '@components/LaunchButton'; +import ContentLoading from '@components/ContentLoading'; 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 { Sparkline } from '@components/Sparkline'; function WorkflowJobTemplateDetail({ template, i18n, webHookKey }) { const { @@ -104,7 +104,6 @@ function WorkflowJobTemplateDetail({ template, i18n, webHookKey }) { {summary_fields.recent_jobs?.length > 0 && ( } label={i18n._(t`Activity`)} /> @@ -223,7 +222,7 @@ function WorkflowJobTemplateDetail({ template, i18n, webHookKey }) { {deletionError && ( setDeletionError(null)} > diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.jsx index 5cd9840b57..3b5d96b381 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.jsx @@ -109,7 +109,7 @@ describe('NodeModal', () => { wrapper.find('button#next-node-modal').simulate('click'); }); wrapper.update(); - wrapper.find('DataListRadio').simulate('click'); + wrapper.find('Radio').simulate('click'); await act(async () => { wrapper.find('button#next-node-modal').simulate('click'); }); @@ -136,7 +136,7 @@ describe('NodeModal', () => { wrapper.find('AnsibleSelect').prop('onChange')(null, 'project_sync'); }); wrapper.update(); - wrapper.find('DataListRadio').simulate('click'); + wrapper.find('Radio').simulate('click'); await act(async () => { wrapper.find('button#next-node-modal').simulate('click'); }); @@ -166,7 +166,7 @@ describe('NodeModal', () => { ); }); wrapper.update(); - wrapper.find('DataListRadio').simulate('click'); + wrapper.find('Radio').simulate('click'); await act(async () => { wrapper.find('button#next-node-modal').simulate('click'); }); @@ -193,7 +193,7 @@ describe('NodeModal', () => { ); }); wrapper.update(); - wrapper.find('DataListRadio').simulate('click'); + wrapper.find('Radio').simulate('click'); await act(async () => { wrapper.find('button#next-node-modal').simulate('click'); }); @@ -396,7 +396,7 @@ describe('NodeModal', () => { ); }); wrapper.update(); - wrapper.find('DataListRadio').simulate('click'); + wrapper.find('Radio').simulate('click'); await act(async () => { wrapper.find('button#next-node-modal').simulate('click'); }); diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/NodeTypeStep.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/NodeTypeStep.jsx index a8a3f73fee..706207d046 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/NodeTypeStep.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/NodeTypeStep.jsx @@ -7,19 +7,11 @@ import { Formik, Field } from 'formik'; import { Form, FormGroup, TextInput } from '@patternfly/react-core'; import FormRow from '@components/FormRow'; import AnsibleSelect from '@components/AnsibleSelect'; -import VerticalSeperator from '@components/VerticalSeparator'; import InventorySourcesList from './InventorySourcesList'; import JobTemplatesList from './JobTemplatesList'; import ProjectsList from './ProjectsList'; 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)` width: 200px; :not(:first-of-type) { @@ -47,9 +39,8 @@ function NodeTypeStep({ }) { return ( <> -
- {i18n._(t`Node Type`)} - +
+ {i18n._(t`Node Type`)}
- {nodeType === 'job_template' && ( { wrapper.update(); expect(wrapper.find('AnsibleSelect').prop('value')).toBe('job_template'); expect(wrapper.find('JobTemplatesList').length).toBe(1); - wrapper.find('DataListRadio').simulate('click'); + wrapper.find('Radio').simulate('click'); expect(onUpdateNodeResource).toHaveBeenCalledWith({ id: 1, name: 'Test Job Template', @@ -119,7 +119,7 @@ describe('NodeTypeStep', () => { wrapper.update(); expect(wrapper.find('AnsibleSelect').prop('value')).toBe('project_sync'); expect(wrapper.find('ProjectsList').length).toBe(1); - wrapper.find('DataListRadio').simulate('click'); + wrapper.find('Radio').simulate('click'); expect(onUpdateNodeResource).toHaveBeenCalledWith({ id: 1, name: 'Test Project', @@ -146,7 +146,7 @@ describe('NodeTypeStep', () => { 'inventory_source_sync' ); expect(wrapper.find('InventorySourcesList').length).toBe(1); - wrapper.find('DataListRadio').simulate('click'); + wrapper.find('Radio').simulate('click'); expect(onUpdateNodeResource).toHaveBeenCalledWith({ id: 1, name: 'Test Inventory Source', @@ -173,7 +173,7 @@ describe('NodeTypeStep', () => { 'workflow_job_template' ); expect(wrapper.find('WorkflowJobTemplatesList').length).toBe(1); - wrapper.find('DataListRadio').simulate('click'); + wrapper.find('Radio').simulate('click'); expect(onUpdateNodeResource).toHaveBeenCalledWith({ id: 1, name: 'Test Workflow Job Template', diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.jsx index 0a8238455b..bf65330702 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.jsx @@ -234,7 +234,7 @@ function VisualizerGraph({ i18n, readOnly }) { {linkHelp && } )} - +
-
- {template.name} -
+ {template.name}
{i18n._(t`Total Nodes`)}
{totalNodes} - - - - - )} - , ]} /> + + {user.summary_fields.user_capabilities.edit && ( + + + + )} + ); diff --git a/awx/ui_next/src/screens/User/shared/UserForm.jsx b/awx/ui_next/src/screens/User/shared/UserForm.jsx index 9c1f2d7138..d74b31b583 100644 --- a/awx/ui_next/src/screens/User/shared/UserForm.jsx +++ b/awx/ui_next/src/screens/User/shared/UserForm.jsx @@ -80,11 +80,7 @@ function UserForm({ user, handleCancel, handleSubmit, submitError, i18n }) { onSubmit={handleValidateAndSubmit} > {formik => ( -
+