diff --git a/__tests__/components/PaginatedDataList/__snapshots__/DeleteToolbarButton.test.jsx.snap b/__tests__/components/PaginatedDataList/__snapshots__/DeleteToolbarButton.test.jsx.snap index f230c0abbe..037450f8d3 100644 --- a/__tests__/components/PaginatedDataList/__snapshots__/DeleteToolbarButton.test.jsx.snap +++ b/__tests__/components/PaginatedDataList/__snapshots__/DeleteToolbarButton.test.jsx.snap @@ -15,7 +15,7 @@ exports[` should render button 1`] = ` entryDelay={500} exitDelay={500} maxWidth="18.75rem" - position="left" + position="top" trigger="mouseenter focus" zIndex={9999} > @@ -49,7 +49,7 @@ exports[` should render button 1`] = ` maxWidth="18.75rem" onCreate={[Function]} performance={true} - placement="left" + placement="top" popperOptions={ Object { "modifiers": Object { @@ -66,96 +66,94 @@ exports[` should render button 1`] = ` trigger="mouseenter focus" zIndex={9999} > - - + svg{color:#d2d2d2;}}}&:hover{background-color:#d9534f;> svg{color:white;}}", - ], - }, - "displayName": "ToolbarDeleteButton__Button", - "foldedComponentIds": Array [], - "render": [Function], - "styledComponentId": "ToolbarDeleteButton__Button-sc-1e3r0eg-0", - "target": [Function], - "toString": [Function], - "warnTooManyClasses": [Function], - "withComponent": [Function], - } - } - forwardedRef={null} isDisabled={true} onClick={[Function]} variant="plain" > - - - - + + + + + + + + + diff --git a/__tests__/pages/Organizations/screens/Organization/OrganizationDetail.test.jsx b/__tests__/pages/Organizations/screens/Organization/OrganizationDetail.test.jsx index 3b7ddda352..b9739a8aa6 100644 --- a/__tests__/pages/Organizations/screens/Organization/OrganizationDetail.test.jsx +++ b/__tests__/pages/Organizations/screens/Organization/OrganizationDetail.test.jsx @@ -72,21 +72,20 @@ describe('', () => { const custom_virtualenvDetail = detailWrapper.findWhere(node => node.props().label === 'Ansible Environment'); const createdDetail = detailWrapper.findWhere(node => node.props().label === 'Created'); const modifiedDetail = detailWrapper.findWhere(node => node.props().label === 'Last Modified'); + expect(nameDetail.find('dt').text()).toBe('Name'); + expect(nameDetail.find('dd').text()).toBe('Foo'); - expect(nameDetail.find('h6').text()).toBe('Name'); - expect(nameDetail.find('p').text()).toBe('Foo'); + expect(descriptionDetail.find('dt').text()).toBe('Description'); + expect(descriptionDetail.find('dd').text()).toBe('Bar'); - expect(descriptionDetail.find('h6').text()).toBe('Description'); - expect(descriptionDetail.find('p').text()).toBe('Bar'); + expect(custom_virtualenvDetail.find('dt').text()).toBe('Ansible Environment'); + expect(custom_virtualenvDetail.find('dd').text()).toBe('Fizz'); - expect(custom_virtualenvDetail.find('h6').text()).toBe('Ansible Environment'); - expect(custom_virtualenvDetail.find('p').text()).toBe('Fizz'); + expect(createdDetail.find('dt').text()).toBe('Created'); + expect(createdDetail.find('dd').text()).toBe('Bat'); - expect(createdDetail.find('h6').text()).toBe('Created'); - expect(createdDetail.find('p').text()).toBe('Bat'); - - expect(modifiedDetail.find('h6').text()).toBe('Last Modified'); - expect(modifiedDetail.find('p').text()).toBe('Boo'); + expect(modifiedDetail.find('dt').text()).toBe('Last Modified'); + expect(modifiedDetail.find('dd').text()).toBe('Boo'); }); test('should show edit button for users with edit permission', () => { @@ -95,9 +94,8 @@ describe('', () => { organization={mockDetails} /> ).find('OrganizationDetail'); - - const editLink = wrapper.findWhere(node => node.props().to === '/organizations/undefined/edit'); - expect(editLink.length).toBe(1); + const editButton = wrapper.find('Button'); + expect((editButton).prop('to')).toBe('/organizations/undefined/edit'); }); test('should hide edit button for users without edit permission', () => { @@ -109,7 +107,8 @@ describe('', () => { /> ).find('OrganizationDetail'); - const editLink = wrapper.findWhere(node => node.props().to === '/organizations/undefined/edit'); + const editLink = wrapper + .findWhere(node => node.props().to === '/organizations/undefined/edit'); expect(editLink.length).toBe(0); }); }); diff --git a/__tests__/pages/Organizations/screens/Organization/__snapshots__/OrganizationNotifications.test.jsx.snap b/__tests__/pages/Organizations/screens/Organization/__snapshots__/OrganizationNotifications.test.jsx.snap index 912c791a0a..d9dfad9a1d 100644 --- a/__tests__/pages/Organizations/screens/Organization/__snapshots__/OrganizationNotifications.test.jsx.snap +++ b/__tests__/pages/Organizations/screens/Organization/__snapshots__/OrganizationNotifications.test.jsx.snap @@ -368,11 +368,11 @@ exports[` initially renders succesfully 1`] = ` "componentStyle": ComponentStyle { "componentId": "DataListToolbar__Toolbar-ajzso8-1", "isStatic": false, - "lastClassName": "dwCtVz", + "lastClassName": "exECbH", "rules": Array [ "flex-grow:1;margin-left:", [Function], - ";", + ";margin-right:20px;", ], }, "displayName": "DataListToolbar__Toolbar", @@ -389,11 +389,11 @@ exports[` initially renders succesfully 1`] = ` marginleft={0} >
@@ -1563,9 +1563,9 @@ exports[` initially renders succesfully 1`] = ` "componentStyle": ComponentStyle { "componentId": "DataListToolbar__AdditionalControlsWrapper-ajzso8-5", "isStatic": true, - "lastClassName": "cAcBQW", + "lastClassName": "bWuACV", "rules": Array [ - "display:flex;flex-grow:1;justify-content:flex-end;", + "display:flex;flex-grow:1;justify-content:flex-end;align-items:center;& >:not(:first-child){margin-left:20px;}", ], }, "displayName": "DataListToolbar__AdditionalControlsWrapper", @@ -1581,7 +1581,7 @@ exports[` initially renders succesfully 1`] = ` forwardedRef={null} >
@@ -1770,9 +1770,9 @@ exports[` initially renders succesfully 1`] = ` "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "componentId": "sc-htpNat", + "componentId": "sc-bxivhb", "isStatic": true, - "lastClassName": "dlcYLn", + "lastClassName": "lohjuH", "rules": Array [ "margin-right: 1.5em;", ], @@ -1780,7 +1780,7 @@ exports[` initially renders succesfully 1`] = ` "displayName": "Styled(Link)", "foldedComponentIds": Array [], "render": [Function], - "styledComponentId": "sc-htpNat", + "styledComponentId": "sc-bxivhb", "target": [Function], "toString": [Function], "warnTooManyClasses": [Function], @@ -1795,7 +1795,7 @@ exports[` initially renders succesfully 1`] = ` } > initially renders succesfully 1`] = ` } > initially renders succesfully 1`] = ` "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "componentId": "sc-bxivhb", + "componentId": "sc-ifAKCX", "isStatic": true, - "lastClassName": "iZoxgU", + "lastClassName": "jGZNNo", "rules": Array [ "text-transform: capitalize;", ], @@ -1835,7 +1835,7 @@ exports[` initially renders succesfully 1`] = ` "displayName": "Styled(Badge)", "foldedComponentIds": Array [], "render": [Function], - "styledComponentId": "sc-bxivhb", + "styledComponentId": "sc-ifAKCX", "target": [Function], "toString": [Function], "warnTooManyClasses": [Function], @@ -1846,11 +1846,11 @@ exports[` initially renders succesfully 1`] = ` isRead={true} > email @@ -2245,9 +2245,9 @@ exports[` initially renders succesfully 1`] = ` "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "componentId": "sc-htpNat", + "componentId": "sc-bxivhb", "isStatic": true, - "lastClassName": "dlcYLn", + "lastClassName": "lohjuH", "rules": Array [ "margin-right: 1.5em;", ], @@ -2255,7 +2255,7 @@ exports[` initially renders succesfully 1`] = ` "displayName": "Styled(Link)", "foldedComponentIds": Array [], "render": [Function], - "styledComponentId": "sc-htpNat", + "styledComponentId": "sc-bxivhb", "target": [Function], "toString": [Function], "warnTooManyClasses": [Function], @@ -2270,7 +2270,7 @@ exports[` initially renders succesfully 1`] = ` } > initially renders succesfully 1`] = ` } > initially renders succesfully 1`] = ` "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "componentId": "sc-bxivhb", + "componentId": "sc-ifAKCX", "isStatic": true, - "lastClassName": "iZoxgU", + "lastClassName": "jGZNNo", "rules": Array [ "text-transform: capitalize;", ], @@ -2310,7 +2310,7 @@ exports[` initially renders succesfully 1`] = ` "displayName": "Styled(Badge)", "foldedComponentIds": Array [], "render": [Function], - "styledComponentId": "sc-bxivhb", + "styledComponentId": "sc-ifAKCX", "target": [Function], "toString": [Function], "warnTooManyClasses": [Function], @@ -2321,11 +2321,11 @@ exports[` initially renders succesfully 1`] = ` isRead={true} > email diff --git a/src/components/DataListToolbar/DataListToolbar.jsx b/src/components/DataListToolbar/DataListToolbar.jsx index 599362d0ef..cf035931f1 100644 --- a/src/components/DataListToolbar/DataListToolbar.jsx +++ b/src/components/DataListToolbar/DataListToolbar.jsx @@ -34,6 +34,7 @@ const AWXToolbar = styled.div` const Toolbar = styled(PFToolbar)` flex-grow: 1; margin-left: ${props => (props.marginleft ? '0' : '20px')}; + margin-right: 20px; `; const ToolbarGroup = styled(PFToolbarGroup)` @@ -67,6 +68,11 @@ const AdditionalControlsWrapper = styled.div` display: flex; flex-grow: 1; justify-content: flex-end; + align-items: center; + + & > :not(:first-child) { + margin-left: 20px; + } `; class DataListToolbar extends React.Component { diff --git a/src/components/PaginatedDataList/PaginatedDataList.jsx b/src/components/PaginatedDataList/PaginatedDataList.jsx index 16e6d4e901..beb49253e0 100644 --- a/src/components/PaginatedDataList/PaginatedDataList.jsx +++ b/src/components/PaginatedDataList/PaginatedDataList.jsx @@ -6,17 +6,17 @@ import { DataListItemRow, DataListItemCells, DataListCell, - Text, TextContent, Title, EmptyState, EmptyStateIcon, - EmptyStateBody, + EmptyStateBody } from '@patternfly/react-core'; import { CubesIcon } from '@patternfly/react-icons'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { withRouter, Link } from 'react-router-dom'; +import styled from 'styled-components'; import Pagination from '../Pagination'; import DataListToolbar from '../DataListToolbar'; @@ -24,19 +24,26 @@ import { parseNamespacedQueryString, updateNamespacedQueryString, } from '../../util/qs'; -import { pluralize, getArticle, ucFirst } from '../../util/strings'; +import { pluralize, ucFirst } from '../../util/strings'; import { QSConfig } from '../../types'; -const detailWrapperStyle = { - display: 'grid', - gridTemplateColumns: 'minmax(70px, max-content) minmax(60px, max-content)', -}; +const EmptyStateControlsWrapper = styled.div` + display: flex; + margin-top: 20px; + margin-right: 20px; + margin-bottom: 20px; + justify-content: flex-end; -const detailLabelStyle = { - fontWeight: '700', - lineHeight: '24px', - marginRight: '20px', -}; + & > :not(:first-child) { + margin-left: 20px; + } +`; + +const ListItemGrid = styled(TextContent)` + display: grid; + grid-template-columns: minmax(70px,max-content) repeat(auto-fit, minmax(60px,max-content)); + grid-gap: 10px; +`; class PaginatedDataList extends React.Component { constructor (props) { @@ -95,6 +102,7 @@ class PaginatedDataList extends React.Component { render () { const { + emptyStateControls, items, itemCount, qsConfig, @@ -125,72 +133,78 @@ class PaginatedDataList extends React.Component { )} // TODO: replace with proper error handling )} - {items.length === 0 ? ( - - - - {i18n._(t`No ${ucFirst(itemNamePlural || pluralize(itemName))} Found`)} - - - {i18n._(t`Please add ${getArticle(itemName)} ${itemName} to populate this list`)} - - - ) : ( - - { }} - onSort={this.handleSort} - showSelectAll={showSelectAll} - isAllSelected={isAllSelected} - onSelectAll={onSelectAll} - additionalControls={additionalControls} - noLeftMargin={alignToolbarLeft} - /> - - {items.map(item => (renderItem ? renderItem(item) : ( - - - - - - - {item.name} - - - - - ]} - /> - - - )))} - - - - )} + {items.length === 0 + ? ( + + + {emptyStateControls} + +
+ + + + {i18n._(t`No ${ucFirst(itemNamePlural || pluralize(itemName))} Found `)} + + + {i18n._(t`Please add ${ucFirst(itemNamePlural || pluralize(itemName))} to populate this list `)} + + + + ) + : ( + + { }} + onSort={this.handleSort} + showSelectAll={showSelectAll} + isAllSelected={isAllSelected} + onSelectAll={onSelectAll} + additionalControls={additionalControls} + noLeftMargin={alignToolbarLeft} + + /> + + {items.map(item => (renderItem ? renderItem(item) : ( + + + + + + + {item.name} + + + + + ]} + /> + + + )))} + + + + )} ); } diff --git a/src/components/PaginatedDataList/ToolbarAddButton.jsx b/src/components/PaginatedDataList/ToolbarAddButton.jsx index ea2f2f183f..c4d9bfecad 100644 --- a/src/components/PaginatedDataList/ToolbarAddButton.jsx +++ b/src/components/PaginatedDataList/ToolbarAddButton.jsx @@ -1,22 +1,17 @@ import React from 'react'; import { string, func } from 'prop-types'; import { Link } from 'react-router-dom'; -import { Button as PFButton } from '@patternfly/react-core'; +import { Button as PFButton, Tooltip } from '@patternfly/react-core'; import { PlusIcon } from '@patternfly/react-icons'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import styled from 'styled-components'; const Button = styled(PFButton)` - &&& { /* higher specificity order */ + && { background-color: #5cb85c; - min-width: 0; - width: 30px; - height: 30px; - text-align: center; - padding: 0; - margin: 0; - margin-right: 20px; + padding: 5px 8px; + --pf-global--FontSize--md: 14px; } `; @@ -25,19 +20,22 @@ function ToolbarAddButton ({ linkTo, onClick, i18n }) { throw new Error('ToolbarAddButton requires either `linkTo` or `onClick` prop'); } if (linkTo) { - // TODO: This should only be a (no - + ); } - return ( +
+ + + +
{ isModalOpen && ( + loading ? '' : ( + +
-
- )); + + + ) + ); if (!match) { cardHeader = null; } diff --git a/src/pages/Organizations/screens/Organization/OrganizationDetail.jsx b/src/pages/Organizations/screens/Organization/OrganizationDetail.jsx index 1c6a69b6d5..14af5d1954 100644 --- a/src/pages/Organizations/screens/Organization/OrganizationDetail.jsx +++ b/src/pages/Organizations/screens/Organization/OrganizationDetail.jsx @@ -4,45 +4,60 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { - CardBody, + CardBody as PFCardBody, Button, - Text, - TextContent, - TextVariants, + TextList, + TextListItem, + TextListVariants, + TextListItemVariants, } from '@patternfly/react-core'; - +import styled from 'styled-components'; import { withNetwork } from '../../../../contexts/Network'; - import BasicChip from '../../../../components/BasicChip/BasicChip'; -const detailWrapperStyle = { - display: 'flex' -}; +const CardBody = styled(PFCardBody)` + padding-top: 20px; +`; -const detailLabelStyle = { - fontWeight: '700', - lineHeight: '24px', - marginRight: '20px', - minWidth: '150px', - textAlign: 'right' -}; +const DetailList = styled(TextList)` + display: grid; + grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); + grid-gap: 20px; -const detailValueStyle = { - lineHeight: '24px', - wordBreak: 'break-all' -}; + & > div { + display: grid; + grid-template-columns: 10em 1fr; + grid-gap: 20px; + } +`; + +const DetailName = styled(TextListItem)` + && { + grid-column: 1; + font-weight: var(--pf-global--FontWeight--bold); + text-align: right; + } +`; + +const DetailValue = styled(TextListItem)` + && { + grid-column: 2; + word-break: break-all; + } +`; + +const InstanceGroupsDetail = styled.div` + grid-column: 1 / -1; +`; const Detail = ({ label, value }) => { - let detail = null; - if (value) { - detail = ( - - { label } - { value } - - ); - } - return detail; + if (!value) return null; + return ( +
+ {label} + {value} +
+ ); }; class OrganizationDetail extends Component { @@ -129,7 +144,7 @@ class OrganizationDetail extends Component { return ( -
+ {(instanceGroups && instanceGroups.length > 0) && ( - - + + {i18n._(t`Instance Groups`)} - -
+ + {instanceGroupChips} {overflowChip} -
-
+ + )} -
+ {summary_fields.user_capabilities.edit && ( -
- - - +
+
)} {error ? 'error!' : ''} diff --git a/src/pages/Organizations/screens/OrganizationsList.jsx b/src/pages/Organizations/screens/OrganizationsList.jsx index 393a10d365..48a646215f 100644 --- a/src/pages/Organizations/screens/OrganizationsList.jsx +++ b/src/pages/Organizations/screens/OrganizationsList.jsx @@ -186,6 +186,10 @@ class OrganizationsList extends Component { onSelect={() => this.handleSelect(o)} /> )} + emptyStateControls={ + canAdd ? + : null + } /> )} { isLoading ?
loading...
: '' }