-
-
- {searchColumnName}
-
- )}
- dropdownItems={searchDropdownItems}
- />
-
-
-
+
+
+
- { sortDropdownItems.length > 1 && (
-
-
- {sortedColumnName}
-
- )}
- dropdownItems={sortDropdownItems}
- />
-
- )}
-
+
- { (showExpandCollapse || showDelete || addUrl) && (
-
- )}
+ { (showExpandCollapse || showDelete || addUrl || add) && (
+
+ )}
{showExpandCollapse && (
-
-
-
-
-
-
-
- { (showDelete || addUrl) && (
+
+
+
+
+ { (showDelete || addUrl || add) && (
)}
-
+
)}
@@ -312,6 +132,9 @@ class DataListToolbar extends React.Component {
)}
+ {add && (
+ {add}
+ )}
@@ -329,10 +152,13 @@ DataListToolbar.propTypes = {
onSelectAll: PropTypes.func,
onSort: PropTypes.func,
showDelete: PropTypes.bool,
- showExpandCollapse: PropTypes.bool,
showSelectAll: PropTypes.bool,
sortOrder: PropTypes.string,
sortedColumnKey: PropTypes.string,
+ onCompact: PropTypes.func,
+ onExpand: PropTypes.func,
+ isCompact: PropTypes.bool,
+ add: PropTypes.node
};
DataListToolbar.defaultProps = {
@@ -341,11 +167,14 @@ DataListToolbar.defaultProps = {
onSelectAll: null,
onSort: null,
showDelete: false,
- showExpandCollapse: false,
showSelectAll: false,
sortOrder: 'ascending',
sortedColumnKey: 'name',
isAllSelected: false,
+ onCompact: null,
+ onExpand: null,
+ isCompact: false,
+ add: null
};
export default DataListToolbar;
diff --git a/src/components/ExpandCollapse/ExpandCollapse.jsx b/src/components/ExpandCollapse/ExpandCollapse.jsx
new file mode 100644
index 0000000000..dfd0402c3c
--- /dev/null
+++ b/src/components/ExpandCollapse/ExpandCollapse.jsx
@@ -0,0 +1,67 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { I18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import {
+ Button,
+ ToolbarItem
+} from '@patternfly/react-core';
+import {
+ BarsIcon,
+ EqualsIcon,
+} from '@patternfly/react-icons';
+
+const ToolbarActiveStyle = {
+ backgroundColor: '#007bba',
+ color: 'white',
+ padding: '0 5px',
+};
+
+class ExpandCollapse extends React.Component {
+ render () {
+ const {
+ onCompact,
+ onExpand,
+ isCompact
+ } = this.props;
+
+ return (
+
+ {({ i18n }) => (
+
+
+
+
+
+
+
+
+ )}
+
+ );
+ }
+}
+
+ExpandCollapse.propTypes = {
+ onCompact: PropTypes.func.isRequired,
+ onExpand: PropTypes.func.isRequired,
+ isCompact: PropTypes.bool.isRequired
+};
+
+ExpandCollapse.defaultProps = {};
+
+export default ExpandCollapse;
diff --git a/src/components/ExpandCollapse/index.js b/src/components/ExpandCollapse/index.js
new file mode 100644
index 0000000000..3bc9066e88
--- /dev/null
+++ b/src/components/ExpandCollapse/index.js
@@ -0,0 +1,3 @@
+import ExpandCollapse from './ExpandCollapse';
+
+export default ExpandCollapse;
diff --git a/src/components/Search/Search.jsx b/src/components/Search/Search.jsx
new file mode 100644
index 0000000000..f52c66410f
--- /dev/null
+++ b/src/components/Search/Search.jsx
@@ -0,0 +1,126 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { I18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import {
+ Button,
+ Dropdown,
+ DropdownPosition,
+ DropdownToggle,
+ DropdownItem,
+ TextInput
+} from '@patternfly/react-core';
+
+class Search extends React.Component {
+ constructor (props) {
+ super(props);
+
+ const { sortedColumnKey } = this.props;
+ this.state = {
+ isSearchDropdownOpen: false,
+ searchKey: sortedColumnKey,
+ searchValue: '',
+ };
+
+ this.handleSearchInputChange = this.handleSearchInputChange.bind(this);
+ this.handleDropdownToggle = this.handleDropdownToggle.bind(this);
+ this.handleDropdownSelect = this.handleDropdownSelect.bind(this);
+ this.handleSearch = this.handleSearch.bind(this);
+ }
+
+ handleDropdownToggle (isSearchDropdownOpen) {
+ this.setState({ isSearchDropdownOpen });
+ }
+
+ handleDropdownSelect ({ target }) {
+ const { columns } = this.props;
+ const { innerText } = target;
+
+ const [{ key: searchKey }] = columns.filter(({ name }) => name === innerText);
+ this.setState({ isSearchDropdownOpen: false, searchKey });
+ }
+
+ handleSearch () {
+ const { searchValue } = this.state;
+ const { onSearch } = this.props;
+
+ onSearch(searchValue);
+ }
+
+ handleSearchInputChange (searchValue) {
+ this.setState({ searchValue });
+ }
+
+ render () {
+ const { up } = DropdownPosition;
+ const {
+ columns
+ } = this.props;
+ const {
+ isSearchDropdownOpen,
+ searchKey,
+ searchValue,
+ } = this.state;
+
+ const [{ name: searchColumnName }] = columns.filter(({ key }) => key === searchKey);
+
+ const searchDropdownItems = columns
+ .filter(({ key }) => key !== searchKey)
+ .map(({ key, name }) => (
+
+ {name}
+
+ ));
+
+ return (
+
+ {({ i18n }) => (
+
+
+ {searchColumnName}
+
+ )}
+ dropdownItems={searchDropdownItems}
+ />
+
+
+
+ )}
+
+ );
+ }
+}
+
+Search.propTypes = {
+ columns: PropTypes.arrayOf(PropTypes.object).isRequired,
+ onSearch: PropTypes.func,
+ sortedColumnKey: PropTypes.string,
+};
+
+Search.defaultProps = {
+ onSearch: null,
+ sortedColumnKey: 'name'
+};
+
+export default Search;
diff --git a/src/components/Search/index.js b/src/components/Search/index.js
new file mode 100644
index 0000000000..517d0ee89b
--- /dev/null
+++ b/src/components/Search/index.js
@@ -0,0 +1,3 @@
+import Search from './Search';
+
+export default Search;
diff --git a/src/components/Sort/Sort.jsx b/src/components/Sort/Sort.jsx
new file mode 100644
index 0000000000..3f1e6f592e
--- /dev/null
+++ b/src/components/Sort/Sort.jsx
@@ -0,0 +1,130 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { I18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import {
+ Button,
+ Dropdown,
+ DropdownPosition,
+ DropdownToggle,
+ DropdownItem
+} from '@patternfly/react-core';
+import {
+ SortAlphaDownIcon,
+ SortAlphaUpIcon,
+ SortNumericDownIcon,
+ SortNumericUpIcon
+} from '@patternfly/react-icons';
+
+class Sort extends React.Component {
+ constructor (props) {
+ super(props);
+
+ this.state = {
+ isSortDropdownOpen: false,
+ };
+
+ this.handleDropdownToggle = this.handleDropdownToggle.bind(this);
+ this.handleDropdownSelect = this.handleDropdownSelect.bind(this);
+ this.handleSort = this.handleSort.bind(this);
+ }
+
+ handleDropdownToggle (isSortDropdownOpen) {
+ this.setState({ isSortDropdownOpen });
+ }
+
+ handleDropdownSelect ({ target }) {
+ const { columns, onSort, sortOrder } = this.props;
+ const { innerText } = target;
+
+ const [{ key: searchKey }] = columns.filter(({ name }) => name === innerText);
+
+ this.setState({ isSortDropdownOpen: false });
+ onSort(searchKey, sortOrder);
+ }
+
+ handleSort () {
+ const { onSort, sortedColumnKey, sortOrder } = this.props;
+ const newSortOrder = sortOrder === 'ascending' ? 'descending' : 'ascending';
+
+ onSort(sortedColumnKey, newSortOrder);
+ }
+
+ render () {
+ const { up } = DropdownPosition;
+ const {
+ columns,
+ sortedColumnKey,
+ sortOrder
+ } = this.props;
+ const {
+ isSortDropdownOpen
+ } = this.state;
+
+ const [{ name: sortedColumnName, isNumeric }] = columns
+ .filter(({ key }) => key === sortedColumnKey);
+
+ const sortDropdownItems = columns
+ .filter(({ key, isSortable }) => isSortable && key !== sortedColumnKey)
+ .map(({ key, name }) => (
+
+ {name}
+
+ ));
+
+ let SortIcon;
+ if (isNumeric) {
+ SortIcon = sortOrder === 'ascending' ? SortNumericUpIcon : SortNumericDownIcon;
+ } else {
+ SortIcon = sortOrder === 'ascending' ? SortAlphaUpIcon : SortAlphaDownIcon;
+ }
+
+ return (
+
+ {({ i18n }) => (
+
+ { sortDropdownItems.length > 1 && (
+
+ {sortedColumnName}
+
+ )}
+ dropdownItems={sortDropdownItems}
+ />
+ )}
+
+
+ )}
+
+ );
+ }
+}
+
+Sort.propTypes = {
+ columns: PropTypes.arrayOf(PropTypes.object).isRequired,
+ onSort: PropTypes.func,
+ sortOrder: PropTypes.string,
+ sortedColumnKey: PropTypes.string
+};
+
+Sort.defaultProps = {
+ onSort: null,
+ sortOrder: 'ascending',
+ sortedColumnKey: 'name'
+};
+
+export default Sort;
diff --git a/src/components/Sort/index.js b/src/components/Sort/index.js
new file mode 100644
index 0000000000..9b59741625
--- /dev/null
+++ b/src/components/Sort/index.js
@@ -0,0 +1,3 @@
+import Sort from './Sort';
+
+export default Sort;
diff --git a/src/pages/Organizations/components/OrganizationAccessList.jsx b/src/pages/Organizations/components/OrganizationAccessList.jsx
index 60616756c8..abc289ed96 100644
--- a/src/pages/Organizations/components/OrganizationAccessList.jsx
+++ b/src/pages/Organizations/components/OrganizationAccessList.jsx
@@ -305,125 +305,122 @@ class OrganizationAccessList extends React.Component {
showWarning
} = this.state;
return (
-
- {!error && results.length <= 0 && (
- Loading...
// TODO: replace with proper loading state
- )}
- {error && results.length <= 0 && (
+
+ {({ i18n }) => (
- {error.message}
- {error.response && (
- {error.response.data.detail}
+ {!error && results.length <= 0 && (
+ Loading...
// TODO: replace with proper loading state
)}
- // TODO: replace with proper error handling
- )}
- {results.length > 0 && (
-
- { }}
- onSort={this.onSort}
- onCompact={this.onCompact}
- onExpand={this.onExpand}
- isCompact={isCompact}
- showExpandCollapse
- />
- {showWarning && (
- }
- >
- {warningMsg}
-
-
-
-
-
+ {error && results.length <= 0 && (
+
+ {error.message}
+ {error.response && (
+ {error.response.data.detail}
+ )}
+ // TODO: replace with proper error handling
)}
-
-
-
- {({ i18n }) => (
-
-
-
+ ) : (
+ null
+ )}
+
+
+
+ {result.userRoles.length > 0 && (
+
+ {i18n._(t`User Roles`)}
+ {result.userRoles.map(role => (
+ this.handleWarning(role.name, role.id, result.username, result.id, 'users')}
+ >
+ {role.name}
+
+ ))}
+
+ )}
+ {result.teamRoles.length > 0 && (
+
+ {i18n._(t`Team Roles`)}
+ {result.teamRoles.map(role => (
+ this.handleWarning(role.name, role.id, role.team_name, role.team_id, 'teams')}
+ >
+ {role.name}
+
+ ))}
+
+ )}
+
+
+ ))}
+
+
+
+ )}
)}
-
+