diff --git a/__tests__/components/RoutedTabs.test.jsx b/__tests__/components/RoutedTabs.test.jsx
new file mode 100644
index 0000000000..9fcd896ef9
--- /dev/null
+++ b/__tests__/components/RoutedTabs.test.jsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import { mount, shallow } from 'enzyme';
+import { Router } from 'react-router-dom';
+import { createMemoryHistory } from 'history';
+import RoutedTabs, { _RoutedTabs } from '../../src/components/Tabs/RoutedTabs';
+
+let wrapper;
+let history;
+
+const tabs = [
+ { name: 'Details', link: '/organizations/19/details', id: 1 },
+ { name: 'Access', link: '/organizations/19/access', id: 2 },
+ { name: 'Teams', link: '/organizations/19/teams', id: 3 },
+ { name: 'Notification', link: '/organizations/19/notification', id: 4 }
+];
+
+describe('', () => {
+ beforeEach(() => {
+ history = createMemoryHistory({
+ initialEntries: ['/organizations/19/teams'],
+ });
+ });
+
+ test('RoutedTabs renders successfully', () => {
+ wrapper = shallow(
+ <_RoutedTabs
+ tabsArray={tabs}
+ history={history}
+ />
+ );
+ expect(wrapper.find('Tab')).toHaveLength(4);
+ });
+
+ test('Given a URL the correct tab is active', async () => {
+ wrapper = mount(
+
+
+
+ );
+
+ expect(history.location.pathname).toEqual('/organizations/19/teams');
+ expect(wrapper.find('Tabs').prop('activeKey')).toBe(3);
+ });
+
+ test('should update history when new tab selected', async () => {
+ wrapper = mount(
+
+
+
+ );
+
+ wrapper.find('Tabs').prop('onSelect')({}, 2);
+ wrapper.update();
+
+ expect(history.location.pathname).toEqual('/organizations/19/access');
+ expect(wrapper.find('Tabs').prop('activeKey')).toBe(2);
+ });
+});
diff --git a/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx b/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx
index ac147e2631..a3b59a9e0c 100644
--- a/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx
+++ b/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import { mount } from 'enzyme';
+
import { MemoryRouter } from 'react-router-dom';
import { I18nProvider } from '@lingui/react';
import Organization from '../../../../../src/pages/Organizations/screens/Organization/Organization';
diff --git a/package-lock.json b/package-lock.json
index d48da6c8e0..ec538cf3fe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2215,7 +2215,7 @@
},
"ansi-colors": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
+ "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
"integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
"requires": {
"ansi-wrap": "^0.1.0"
@@ -3234,12 +3234,12 @@
},
"babel-plugin-syntax-class-properties": {
"version": "6.13.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
+ "resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
"integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94="
},
"babel-plugin-syntax-flow": {
"version": "6.18.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz",
+ "resolved": "http://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz",
"integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0="
},
"babel-plugin-syntax-jsx": {
@@ -5414,7 +5414,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
@@ -6625,7 +6625,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
@@ -7012,7 +7012,8 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"aproba": {
"version": "1.2.0",
@@ -7033,12 +7034,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -7053,17 +7056,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"core-util-is": {
"version": "1.0.2",
@@ -7180,7 +7186,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"ini": {
"version": "1.3.5",
@@ -7192,6 +7199,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -7206,6 +7214,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -7213,12 +7222,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -7237,6 +7248,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -7317,7 +7329,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"object-assign": {
"version": "4.1.1",
@@ -7329,6 +7342,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"wrappy": "1"
}
@@ -7414,7 +7428,8 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -7450,6 +7465,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -7469,6 +7485,7 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -7512,12 +7529,14 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
}
}
},
@@ -10583,7 +10602,7 @@
},
"kind-of": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
+ "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
"integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ="
},
"kleur": {
diff --git a/src/app.scss b/src/app.scss
index 20df8aa24b..7f05497dc5 100644
--- a/src/app.scss
+++ b/src/app.scss
@@ -283,6 +283,14 @@
margin-bottom: 10px;
}
+.OrgsTab-closeButton {
+ color: black;
+ float: right;
+ position: relative;
+ top: -25px;
+ margin: 0 10px;
+ right: 10px;
+}
.awx-c-form-action-group {
float: right;
display: block;
diff --git a/src/components/Tabs/RoutedTabs.jsx b/src/components/Tabs/RoutedTabs.jsx
new file mode 100644
index 0000000000..4100e766b6
--- /dev/null
+++ b/src/components/Tabs/RoutedTabs.jsx
@@ -0,0 +1,56 @@
+import React from 'react';
+import { shape, string, number, arrayOf } from 'prop-types';
+import { Tab, Tabs } from '@patternfly/react-core';
+import { withRouter } from 'react-router-dom';
+
+function RoutedTabs (props) {
+ const { history, tabsArray } = props;
+
+ const getActiveTabId = () => {
+ const match = tabsArray.find(tab => tab.link === history.location.pathname);
+ if (match) {
+ return match.id;
+ }
+ return 0;
+ };
+
+ function handleTabSelect (event, eventKey) {
+ const match = tabsArray.find(tab => tab.id === eventKey);
+ if (match) {
+ history.push(match.link);
+ }
+ }
+
+ return (
+
+ {tabsArray.map(tab => (
+
+ ))}
+
+ );
+}
+RoutedTabs.propTypes = {
+ history: shape({
+ location: shape({
+ pathname: string.isRequired
+ }).isRequired,
+ }).isRequired,
+ tabsArray: arrayOf(shape({
+ id: number.isRequired,
+ link: string.isRequired,
+ name: string.isRequired,
+ })).isRequired,
+};
+
+export { RoutedTabs as _RoutedTabs };
+export default withRouter(RoutedTabs);
diff --git a/src/components/Tabs/Tab.jsx b/src/components/Tabs/Tab.jsx
deleted file mode 100644
index 6dd9409391..0000000000
--- a/src/components/Tabs/Tab.jsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { NavLink } from 'react-router-dom';
-import './tabs.scss';
-
-const Tab = ({ children, link, replace }) => (
-
-
- {children}
-
-
-);
-
-Tab.propTypes = {
- children: PropTypes.oneOfType([
- PropTypes.arrayOf(PropTypes.node),
- PropTypes.node
- ]).isRequired,
- link: PropTypes.string,
- replace: PropTypes.bool,
-};
-
-Tab.defaultProps = {
- link: null,
- replace: false,
-};
-
-export default Tab;
diff --git a/src/components/Tabs/Tabs.jsx b/src/components/Tabs/Tabs.jsx
deleted file mode 100644
index abea04f8b8..0000000000
--- a/src/components/Tabs/Tabs.jsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Link } from 'react-router-dom';
-import { Button, Tooltip } from '@patternfly/react-core';
-import { TimesIcon } from '@patternfly/react-icons';
-import './tabs.scss';
-
-const Tabs = ({ children, labelText, closeButton }) => (
-
-
- {closeButton
- && (
-
-
-
-
-
- )
- }
-
-);
-
-Tabs.propTypes = {
- children: PropTypes.oneOfType([
- PropTypes.arrayOf(PropTypes.node),
- PropTypes.node
- ]).isRequired,
- labelText: PropTypes.string,
- closeButton: PropTypes.shape({
- text: PropTypes.string,
- link: PropTypes.string,
- }),
-};
-
-Tabs.defaultProps = {
- labelText: null,
- closeButton: null,
-};
-
-export default Tabs;
diff --git a/src/components/Tabs/tabs.scss b/src/components/Tabs/tabs.scss
deleted file mode 100644
index 651be6acba..0000000000
--- a/src/components/Tabs/tabs.scss
+++ /dev/null
@@ -1,71 +0,0 @@
-.pf-c-card__header {
- --pf-c-card__header--PaddingBottom: 0;
- --pf-c-card__header--PaddingX: 0;
- --pf-c-card__header--PaddingRight: 0;
- --pf-c-card__header--PaddingLeft: 0;
- --pf-c-card__header--PaddingTop: 0;
-}
-
-.pf-c-tabs {
- --pf-global--link--Color: #484848;
- --pf-global--link--Color--hover: #484848;
- --pf-global--link--TextDecoration--hover: none;
-
- align-items: center;
- flex-direction: row;
- justify-content: space-between;
-
- &:before {
- border-bottom: 1px solid var(--pf-c-tabs__item--BorderColor);
- border-top: 1px solid var(--pf-c-tabs__item--BorderColor);
- bottom: 0;
- content: " ";
- left: 0;
- position: absolute;
- right: 0;
- top: 0;
- }
-
- .pf-c-tabs__button {
- --pf-c-tabs__button--PaddingLeft: 20px;
- --pf-c-tabs__button--PaddingRight: 20px;
- display: block;
- font-weight: 700;
-
- &:after {
- content: '';
- bottom: 0;
- left: 0;
- position: absolute;
- right: 0;
- top: 0;
- }
- }
-
- .pf-c-tabs__item:first-child .pf-c-tabs__button:before {
- border-left: 0;
- }
-
- .pf-c-tabs__item:not(.pf-m-current):hover
- .pf-c-tabs__button::after {
- border-top: none;
- }
-
- .pf-c-tabs__item:hover
- .pf-c-tabs__button:not(.pf-m-current)::after {
- border-bottom: 3px solid var(--pf-global--Color--dark-200);
- border-top: none;
- }
-
- .pf-c-tabs__button.pf-m-current {
- color: var(--pf-c-tabs__item--m-current--Color);
- }
-
- .pf-c-tabs__button.pf-m-current::after {
- content: '';
- border-bottom: 3px solid var(--pf-c-tabs__item--m-current--Color);
- border-top: none;
- margin-left: 1px;
- }
-}
-
diff --git a/src/pages/Organizations/screens/Organization/Organization.jsx b/src/pages/Organizations/screens/Organization/Organization.jsx
index 2ba53ece98..aba494809e 100644
--- a/src/pages/Organizations/screens/Organization/Organization.jsx
+++ b/src/pages/Organizations/screens/Organization/Organization.jsx
@@ -5,25 +5,25 @@ import {
Switch,
Route,
withRouter,
- Redirect
+ Redirect,
+ Link
} from 'react-router-dom';
import {
Card,
CardHeader,
- PageSection
+ PageSection,
} from '@patternfly/react-core';
-
+import {
+ TimesIcon
+} from '@patternfly/react-icons';
import { withNetwork } from '../../../../contexts/Network';
-
-import Tabs from '../../../../components/Tabs/Tabs';
-import Tab from '../../../../components/Tabs/Tab';
import NotifyAndRedirect from '../../../../components/NotifyAndRedirect';
-
import OrganizationAccess from './OrganizationAccess';
import OrganizationDetail from './OrganizationDetail';
import OrganizationEdit from './OrganizationEdit';
import OrganizationNotifications from './OrganizationNotifications';
import OrganizationTeams from './OrganizationTeams';
+import RoutedTabs from '../../../../components/Tabs/RoutedTabs';
class Organization extends Component {
constructor (props) {
@@ -79,35 +79,45 @@ class Organization extends Component {
loading
} = this.state;
- const tabElements = [
- { name: i18nMark('Details'), link: `${match.url}/details` },
- { name: i18nMark('Access'), link: `${match.url}/access` },
- { name: i18nMark('Teams'), link: `${match.url}/teams` },
- { name: i18nMark('Notifications'), link: `${match.url}/notifications` },
- ];
+ const tabsPaddingOverride = {
+ padding: '0'
+ };
let cardHeader = (
-
-
- {({ i18n }) => (
-
- {tabElements.map(tabElement => (
-
- {tabElement.name}
-
- ))}
-
- )}
-
-
- );
+ loading ? ''
+ : (
+
+
+ {({ i18n }) => (
+
+
+
+
+
+
+ )}
+
+
+ ));
+ if (!match) {
+ cardHeader = null;
+ }
if (location.pathname.endsWith('edit')) {
cardHeader = null;
@@ -185,5 +195,5 @@ class Organization extends Component {
);
}
}
-
export default withNetwork(withRouter(Organization));
+export { Organization as _Organization };