diff --git a/__tests__/components/DataListToolbar.test.jsx b/__tests__/components/DataListToolbar.test.jsx
index 22b91e6530..be5e028d37 100644
--- a/__tests__/components/DataListToolbar.test.jsx
+++ b/__tests__/components/DataListToolbar.test.jsx
@@ -4,8 +4,6 @@ import DataListToolbar from '../../src/components/DataListToolbar';
describe('', () => {
const columns = [{ name: 'Name', key: 'name', isSortable: true }];
- const noop = () => {};
-
let toolbar;
afterEach(() => {
diff --git a/__tests__/components/Tooltip.test.jsx b/__tests__/components/Tooltip.test.jsx
new file mode 100644
index 0000000000..7f09c5eaec
--- /dev/null
+++ b/__tests__/components/Tooltip.test.jsx
@@ -0,0 +1,55 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import Tooltip from '../../src/components/Tooltip';
+
+describe('', () => {
+ let elem;
+ let content;
+ let mouseOverHandler;
+ let mouseOutHandler;
+
+ test('initially renders without crashing', () => {
+ elem = mount();
+ expect(elem.length).toBe(1);
+ });
+
+ test('shows/hides with mouse over and leave', () => {
+ elem = mount();
+ mouseOverHandler = elem.find('.mouseOverHandler');
+ mouseOutHandler = elem.find('.mouseOutHandler');
+ expect(elem.state().isDisplayed).toBe(false);
+ elem.update();
+ content = elem.find('.pf-c-tooltip__content');
+ expect(content.length).toBe(0);
+ mouseOverHandler.props().onMouseOver();
+ expect(elem.state().isDisplayed).toBe(true);
+ elem.update();
+ content = elem.find('.pf-c-tooltip__content');
+ expect(content.length).toBe(1);
+ mouseOutHandler.props().onMouseLeave();
+ expect(elem.state().isDisplayed).toBe(false);
+ elem.update();
+ content = elem.find('.pf-c-tooltip__content');
+ expect(content.length).toBe(0);
+ });
+
+ test('shows/hides with focus and blur', () => {
+ elem = mount();
+ mouseOverHandler = elem.find('.mouseOverHandler');
+ mouseOutHandler = elem.find('.mouseOutHandler');
+ expect(elem.state().isDisplayed).toBe(false);
+ elem.update();
+ content = elem.find('.pf-c-tooltip__content');
+ expect(content.length).toBe(0);
+ mouseOverHandler.props().onFocus();
+ expect(elem.state().isDisplayed).toBe(true);
+ elem.update();
+ content = elem.find('.pf-c-tooltip__content');
+ expect(content.length).toBe(1);
+ mouseOutHandler.props().onBlur();
+ expect(elem.state().isDisplayed).toBe(false);
+ elem.update();
+ content = elem.find('.pf-c-tooltip__content');
+ expect(content.length).toBe(0);
+ });
+});
diff --git a/__tests__/pages/Organizations.jsx b/__tests__/pages/Organizations.jsx
deleted file mode 100644
index fdbc219cc8..0000000000
--- a/__tests__/pages/Organizations.jsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import React from 'react';
-import { HashRouter } from 'react-router-dom';
-
-import { mount } from 'enzyme';
-
-import api from '../../src/api';
-import { API_ORGANIZATIONS } from '../../src/endpoints';
-import Organizations from '../../src/pages/Organizations';
-
-describe('', () => {
- let pageWrapper;
-
- const results = [
- {
- id: 1,
- name: 'org 1',
- summary_fields: {
- related_field_counts: {
- users: 1,
- teams: 1,
- admins: 1
- }
- }
- },
- {
- id: 2,
- name: 'org 2',
- summary_fields: {
- related_field_counts: {
- users: 1,
- teams: 1,
- admins: 1
- }
- }
- },
- {
- id: 3,
- name: 'org 3',
- summary_fields: {
- related_field_counts: {
- users: 1,
- teams: 1,
- admins: 1
- }
- }
- },
- ];
- const count = results.length;
- const response = { data: { count, results } };
-
- beforeEach(() => {
- api.get = jest.fn().mockImplementation(() => Promise.resolve(response));
- pageWrapper = mount();
- });
-
- afterEach(() => {
- pageWrapper.unmount();
- });
-
- test('it renders expected content', () => {
- const pageSections = pageWrapper.find('PageSection');
- const title = pageWrapper.find('Title');
-
- expect(pageWrapper.length).toBe(1);
- expect(pageSections.length).toBe(2);
- expect(title.length).toBe(1);
- expect(title.props().size).toBe('2xl');
- pageSections.forEach(section => {
- expect(section.props().variant).toBeDefined();
- });
- expect(pageWrapper.find('ul').length).toBe(1);
- expect(pageWrapper.find('ul li').length).toBe(0);
- // will render all list items on update
- pageWrapper.update();
- expect(pageWrapper.find('ul li').length).toBe(count);
- });
-
- test('API Organization endpoint is valid', () => {
- expect(API_ORGANIZATIONS).toBeDefined();
- });
-
- test('it displays a tooltip on delete hover', () => {
- const tooltip = '.pf-c-tooltip__content';
- const deleteButton = 'button[aria-label="Delete"]';
-
- expect(pageWrapper.find(tooltip).length).toBe(0);
- pageWrapper.find(deleteButton).simulate('mouseover');
- expect(pageWrapper.find(tooltip).length).toBe(1);
- });
-});
diff --git a/__tests__/pages/Organizations/components/OrganizationBreadcrumb.test.jsx b/__tests__/pages/Organizations/components/OrganizationBreadcrumb.test.jsx
new file mode 100644
index 0000000000..bbb222555f
--- /dev/null
+++ b/__tests__/pages/Organizations/components/OrganizationBreadcrumb.test.jsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import { MemoryRouter } from 'react-router-dom';
+import OrganizationBreadcrumb from '../../../../src/pages/Organizations/components/OrganizationBreadcrumb';
+
+describe('', () => {
+ test('initially renders succesfully', () => {
+ mount(
+
+
+
+ );
+ });
+});
diff --git a/__tests__/pages/Organizations/components/OrganizationDetail.test.jsx b/__tests__/pages/Organizations/components/OrganizationDetail.test.jsx
new file mode 100644
index 0000000000..a160ad8773
--- /dev/null
+++ b/__tests__/pages/Organizations/components/OrganizationDetail.test.jsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import { MemoryRouter } from 'react-router-dom';
+import OrganizationDetail from '../../../../src/pages/Organizations/components/OrganizationDetail';
+
+describe('', () => {
+ test('initially renders succesfully', () => {
+ mount(
+
+
+
+ );
+ });
+});
diff --git a/__tests__/pages/Organizations/components/OrganizationEdit.test.jsx b/__tests__/pages/Organizations/components/OrganizationEdit.test.jsx
new file mode 100644
index 0000000000..6a99eb2d84
--- /dev/null
+++ b/__tests__/pages/Organizations/components/OrganizationEdit.test.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import { MemoryRouter } from 'react-router-dom';
+import OrganizationEdit from '../../../../src/pages/Organizations/components/OrganizationEdit';
+
+describe('', () => {
+ test('initially renders succesfully', () => {
+ mount(
+
+
+
+ );
+ });
+});
diff --git a/__tests__/pages/Organizations/components/OrganizationListItem.test.jsx b/__tests__/pages/Organizations/components/OrganizationListItem.test.jsx
new file mode 100644
index 0000000000..aab249b197
--- /dev/null
+++ b/__tests__/pages/Organizations/components/OrganizationListItem.test.jsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import { MemoryRouter } from 'react-router-dom';
+import OrganizationListItem from '../../../../src/pages/Organizations/components/OrganizationListItem';
+
+describe('', () => {
+ test('initially renders succesfully', () => {
+ mount(
+
+
+
+ );
+ });
+});
diff --git a/__tests__/pages/Organizations/index.test.jsx b/__tests__/pages/Organizations/index.test.jsx
new file mode 100644
index 0000000000..c9fc5359ab
--- /dev/null
+++ b/__tests__/pages/Organizations/index.test.jsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { MemoryRouter } from 'react-router-dom';
+import { mount } from 'enzyme';
+import Organizations from '../../../src/pages/Organizations/index';
+
+describe('', () => {
+ test('initially renders succesfully', () => {
+ mount(
+
+
+
+ );
+ });
+});
diff --git a/__tests__/pages/Organizations/utils.test.jsx b/__tests__/pages/Organizations/utils.test.jsx
new file mode 100644
index 0000000000..4b92cae0c1
--- /dev/null
+++ b/__tests__/pages/Organizations/utils.test.jsx
@@ -0,0 +1,13 @@
+import getTabName from '../../../src/pages/Organizations/utils';
+
+describe('getTabName', () => {
+ test('returns tab name', () => {
+ expect(getTabName('details')).toBe('Details');
+ expect(getTabName('users')).toBe('Users');
+ expect(getTabName('teams')).toBe('Teams');
+ expect(getTabName('admins')).toBe('Admins');
+ expect(getTabName('notifications')).toBe('Notifications');
+ expect(getTabName('unknown')).toBe('');
+ expect(getTabName()).toBe('');
+ });
+});
diff --git a/__tests__/pages/Organizations/views/Organization.add.test.jsx b/__tests__/pages/Organizations/views/Organization.add.test.jsx
new file mode 100644
index 0000000000..c8822cfc38
--- /dev/null
+++ b/__tests__/pages/Organizations/views/Organization.add.test.jsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import OrganizationAdd from '../../../../src/pages/Organizations/views/Organization.add';
+
+describe('', () => {
+ test('initially renders succesfully', () => {
+ mount(
+
+ );
+ });
+});
diff --git a/__tests__/pages/Organizations/views/Organization.view.test.jsx b/__tests__/pages/Organizations/views/Organization.view.test.jsx
new file mode 100644
index 0000000000..c5dff0e2ef
--- /dev/null
+++ b/__tests__/pages/Organizations/views/Organization.view.test.jsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import { MemoryRouter } from 'react-router-dom';
+import OrganizationView from '../../../../src/pages/Organizations/views/Organization.view';
+
+describe('', () => {
+ test('initially renders succesfully', () => {
+ mount(
+
+
+
+ );
+ });
+});
diff --git a/__tests__/pages/Organizations/views/Organizations.list.test.jsx b/__tests__/pages/Organizations/views/Organizations.list.test.jsx
new file mode 100644
index 0000000000..e26717d554
--- /dev/null
+++ b/__tests__/pages/Organizations/views/Organizations.list.test.jsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import { MemoryRouter } from 'react-router-dom';
+import OrganizationsList from '../../../../src/pages/Organizations/views/Organizations.list';
+
+describe('', () => {
+ test('initially renders succesfully', () => {
+ mount(
+
+
+
+ );
+ });
+});
diff --git a/package.json b/package.json
index a60cb4f397..19dad2c599 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"main": "index.jsx",
"scripts": {
"start": "webpack-dev-server --config ./webpack.config.js --mode development",
- "test": "jest --watchAll --coverage",
+ "test": "jest --watch --coverage",
"lint": "./node_modules/eslint/bin/eslint.js src/**/*.js src/**/*.jsx"
},
"keywords": [],
diff --git a/src/components/DataListToolbar/DataListToolbar.jsx b/src/components/DataListToolbar/DataListToolbar.jsx
index a3fbb80d92..ba7ca9edc1 100644
--- a/src/components/DataListToolbar/DataListToolbar.jsx
+++ b/src/components/DataListToolbar/DataListToolbar.jsx
@@ -6,8 +6,6 @@ import {
DropdownPosition,
DropdownToggle,
DropdownItem,
- FormGroup,
- KebabToggle,
Level,
LevelItem,
TextInput,
@@ -24,11 +22,14 @@ import {
SortNumericUpIcon,
TrashAltIcon,
} from '@patternfly/react-icons';
+import {
+ Link
+} from 'react-router-dom';
import Tooltip from '../Tooltip';
class DataListToolbar extends React.Component {
- constructor(props) {
+ constructor (props) {
super(props);
const { sortedColumnKey } = this.props;
@@ -72,15 +73,7 @@ class DataListToolbar extends React.Component {
this.setState({ isSearchDropdownOpen: false, searchKey: key });
};
- onActionToggle = isActionDropdownOpen => {
- this.setState({ isActionDropdownOpen });
- };
-
- onActionSelect = ({ target }) => {
- this.setState({ isActionDropdownOpen: false });
- };
-
- render() {
+ render () {
const { up } = DropdownPosition;
const {
columns,
@@ -90,9 +83,10 @@ class DataListToolbar extends React.Component {
onSort,
sortedColumnKey,
sortOrder,
+ addUrl
} = this.props;
const {
- isActionDropdownOpen,
+ // isActionDropdownOpen,
isSearchDropdownOpen,
isSortDropdownOpen,
searchKey,
@@ -107,19 +101,29 @@ class DataListToolbar extends React.Component {
.filter(({ key }) => key === sortedColumnKey);
const sortedColumnName = sortedColumn.name;
const isSortNumeric = sortedColumn.isNumeric;
+ const displayedSortIcon = () => {
+ let icon;
+ if (sortOrder === 'ascending') {
+ icon = isSortNumeric ? () : ();
+ } else {
+ icon = isSortNumeric ? () : ();
+ }
+ return icon;
+ };
return (
-
+
+ id="select-all"
+ />
@@ -132,10 +136,12 @@ class DataListToolbar extends React.Component {
isOpen={isSearchDropdownOpen}
toggle={(
+ onToggle={this.onSearchDropdownToggle}
+ >
{ searchColumnName }
- )}>
+ )}
+ >
{columns.filter(({ key }) => key !== searchKey).map(({ key, name }) => (
{ name }
@@ -146,12 +152,14 @@ class DataListToolbar extends React.Component {
type="search"
aria-label="search text input"
value={searchValue}
- onChange={this.handleSearchInputChange}/>
+ onChange={this.handleSearchInputChange}
+ />
@@ -165,31 +173,28 @@ class DataListToolbar extends React.Component {
isOpen={isSortDropdownOpen}
toggle={(
+ onToggle={this.onSortDropdownToggle}
+ >
{ sortedColumnName }
- )}>
+ )}
+ >
{columns
.filter(({ key, isSortable }) => isSortable && key !== sortedColumnKey)
.map(({ key, name }) => (
-
- { name }
-
- ))}
+
+ { name }
+
+ ))}
@@ -210,12 +215,16 @@ class DataListToolbar extends React.Component {
-
+ {addUrl && (
+
+
+
+ )}
@@ -223,4 +232,4 @@ class DataListToolbar extends React.Component {
}
}
-export default DataListToolbar;
\ No newline at end of file
+export default DataListToolbar;
diff --git a/src/components/Tooltip/Tooltip.jsx b/src/components/Tooltip/Tooltip.jsx
index f4a3617dff..107ee0674e 100644
--- a/src/components/Tooltip/Tooltip.jsx
+++ b/src/components/Tooltip/Tooltip.jsx
@@ -3,36 +3,36 @@ import React from 'react';
class Tooltip extends React.Component {
transforms = {
top: {
- bottom: "100%",
- left: "50%",
- transform: "translate(-50%, -25%)"
+ bottom: '100%',
+ left: '50%',
+ transform: 'translate(-50%, -25%)'
},
bottom: {
- top: "100%",
- left: "50%",
- transform: "translate(-50%, 25%)"
+ top: '100%',
+ left: '50%',
+ transform: 'translate(-50%, 25%)'
},
left: {
- top: "50%",
- right: "100%",
- transform: "translate(-25%, -50%)"
+ top: '50%',
+ right: '100%',
+ transform: 'translate(-25%, -50%)'
},
right: {
- bottom: "100%",
- left: "50%",
- transform: "translate(25%, 50%)"
+ bottom: '100%',
+ left: '50%',
+ transform: 'translate(25%, 50%)'
},
};
- constructor(props) {
- super(props)
+ constructor (props) {
+ super(props);
this.state = {
isDisplayed: false
};
}
- render() {
+ render () {
const {
children,
message,
@@ -44,24 +44,33 @@ class Tooltip extends React.Component {
return (
this.setState({ isDisplayed: false })}>
- { isDisplayed &&
-
-
-
- { message }
+ style={{ position: 'relative' }}
+ className="mouseOutHandler"
+ onMouseLeave={() => this.setState({ isDisplayed: false })}
+ onBlur={() => this.setState({ isDisplayed: false })}
+ >
+ { isDisplayed
+ && (
+
-
+ )
}
this.setState({ isDisplayed: true })}>
+ className="mouseOverHandler"
+ onMouseOver={() => this.setState({ isDisplayed: true })}
+ onFocus={() => this.setState({ isDisplayed: true })}
+ >
{ children }
- )
+ );
}
}
diff --git a/src/pages/Organizations/components/OrganizationBreadcrumb.jsx b/src/pages/Organizations/components/OrganizationBreadcrumb.jsx
new file mode 100644
index 0000000000..bbac1b3d5c
--- /dev/null
+++ b/src/pages/Organizations/components/OrganizationBreadcrumb.jsx
@@ -0,0 +1,78 @@
+import React, { Fragment } from 'react';
+import {
+ PageSection,
+ PageSectionVariants,
+ Title,
+} from '@patternfly/react-core';
+import {
+ Link
+} from 'react-router-dom';
+
+import getTabName from '../utils';
+
+const OrganizationBreadcrumb = ({ parentObj, organization, currentTab, location }) => {
+ const { light } = PageSectionVariants;
+ let breadcrumb = '';
+ if (parentObj !== 'loading') {
+ const generateCrumb = (noLastLink = false) => (
+
+ {parentObj
+ .map(({ url, name }, index) => {
+ let elem;
+ if (noLastLink && parentObj.length - 1 === index) {
+ elem = ({name});
+ } else {
+ elem = (
+
+ {name}
+
+ );
+ }
+ return elem;
+ })
+ .reduce((prev, curr) => [prev, ' > ', curr])}
+
+ );
+
+ if (currentTab && currentTab !== 'details') {
+ breadcrumb = (
+
+ {generateCrumb()}
+ {' > '}
+ {getTabName(currentTab)}
+
+ );
+ } else if (location.pathname.indexOf('edit') > -1) {
+ breadcrumb = (
+
+ {generateCrumb()}
+ {' > edit'}
+
+ );
+ } else if (location.pathname.indexOf('add') > -1) {
+ breadcrumb = (
+
+ {generateCrumb()}
+ {' > add'}
+
+ );
+ } else {
+ breadcrumb = (
+
+ {generateCrumb(true)}
+
+ );
+ }
+ }
+
+ return (
+
+ {breadcrumb}
+
+ );
+};
+
+export default OrganizationBreadcrumb;
diff --git a/src/pages/Organizations/components/OrganizationDetail.jsx b/src/pages/Organizations/components/OrganizationDetail.jsx
new file mode 100644
index 0000000000..52800fc7c2
--- /dev/null
+++ b/src/pages/Organizations/components/OrganizationDetail.jsx
@@ -0,0 +1,140 @@
+import React, { Fragment } from 'react';
+import {
+ Card,
+ CardHeader,
+ CardBody,
+ PageSection,
+ PageSectionVariants,
+ ToolbarGroup,
+ ToolbarItem,
+ ToolbarSection,
+} from '@patternfly/react-core';
+import {
+ Switch,
+ Link,
+ Route
+} from 'react-router-dom';
+
+import getTabName from '../utils';
+
+import '../tabs.scss';
+
+const DetailTab = ({ location, match, tab, currentTab, children, breadcrumb }) => {
+ const tabClasses = () => {
+ let classes = 'at-c-tabs__tab';
+ if (tab === currentTab) {
+ classes += ' at-m-selected';
+ }
+
+ return classes;
+ };
+
+ const updateTab = () => {
+ const params = new URLSearchParams(location.search);
+ if (params.get('tab') !== undefined) {
+ params.set('tab', tab);
+ } else {
+ params.append('tab', tab);
+ }
+
+ return `?${params.toString()}`;
+ };
+
+ return (
+
+
+ {children}
+
+
+ );
+};
+
+const OrganizationDetail = ({
+ location,
+ match,
+ parentBreadcrumbObj,
+ organization,
+ params,
+ currentTab
+}) => {
+ // TODO: set objectName by param or through grabbing org detail get from api
+ const { medium } = PageSectionVariants;
+
+ const deleteResourceView = () => (
+
+ {`deleting ${currentTab} association with orgs `}
+
+ {`confirm removal of ${currentTab}/cancel and go back to ${currentTab} view.`}
+
+
+ );
+
+ const addResourceView = () => (
+
+ {`adding ${currentTab} `}
+
+ {`save/cancel and go back to ${currentTab} view`}
+
+
+ );
+
+ const resourceView = () => (
+
+ {`${currentTab} detail view `}
+
+ {`add ${currentTab}`}
+
+ {' '}
+
+ {`delete ${currentTab}`}
+
+
+ );
+
+ const detailTabs = (tabs) => (
+
+
+ {tabs.map(tab => (
+
+ {getTabName(tab)}
+
+ ))}
+
+
+ );
+
+ return (
+
+
+
+ {detailTabs(['details', 'users', 'teams', 'admins', 'notifications'])}
+
+
+ {(currentTab && currentTab !== 'details') ? (
+
+ deleteResourceView()} />
+ addResourceView()} />
+ resourceView()} />
+
+ ) : (
+
+ {'detail view '}
+
+ {'edit'}
+
+
+ )}
+
+
+
+ );
+};
+
+export default OrganizationDetail;
diff --git a/src/pages/Organizations/components/OrganizationEdit.jsx b/src/pages/Organizations/components/OrganizationEdit.jsx
new file mode 100644
index 0000000000..04d49ff6d5
--- /dev/null
+++ b/src/pages/Organizations/components/OrganizationEdit.jsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import {
+ Card,
+ CardBody,
+ PageSection,
+ PageSectionVariants
+} from '@patternfly/react-core';
+import {
+ Link
+} from 'react-router-dom';
+
+const OrganizationEdit = ({ match, parentBreadcrumbObj, organization }) => {
+ const { medium } = PageSectionVariants;
+
+ return (
+
+
+
+ {'edit view '}
+
+ {'save/cancel and go back to view'}
+
+
+
+
+ );
+};
+
+export default OrganizationEdit;
diff --git a/src/components/OrganizationListItem.jsx b/src/pages/Organizations/components/OrganizationListItem.jsx
similarity index 59%
rename from src/components/OrganizationListItem.jsx
rename to src/pages/Organizations/components/OrganizationListItem.jsx
index f64beb3984..d199b47e96 100644
--- a/src/components/OrganizationListItem.jsx
+++ b/src/pages/Organizations/components/OrganizationListItem.jsx
@@ -3,8 +3,21 @@ import {
Badge,
Checkbox,
} from '@patternfly/react-core';
+import {
+ Link
+} from 'react-router-dom';
-export default ({ itemId, name, userCount, teamCount, adminCount, isSelected, onSelect }) => (
+export default ({
+ itemId,
+ name,
+ userCount,
+ teamCount,
+ adminCount,
+ isSelected,
+ onSelect,
+ detailUrl,
+ parentBreadcrumb
+}) => (
-
Users
+
+ Users
+
{' '}
{userCount}
{' '}
-
Teams
+
+ Teams
+
{' '}
{teamCount}
{' '}
-
Admins
+
+ Admins
+
{' '}
{adminCount}
diff --git a/src/pages/Organizations/index.jsx b/src/pages/Organizations/index.jsx
new file mode 100644
index 0000000000..09299873df
--- /dev/null
+++ b/src/pages/Organizations/index.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { Route, Switch } from 'react-router-dom';
+
+import OrganizationAdd from './views/Organization.add';
+import OrganizationView from './views/Organization.view';
+import OrganizationsList from './views/Organizations.list';
+
+const Organizations = ({ match }) => (
+
+
+
+
+
+);
+
+export default Organizations;
diff --git a/src/pages/Organizations/tabs.scss b/src/pages/Organizations/tabs.scss
new file mode 100644
index 0000000000..2afa2f5ee7
--- /dev/null
+++ b/src/pages/Organizations/tabs.scss
@@ -0,0 +1,18 @@
+.at-c-tabs {
+ padding: 0 5px !important;
+ margin: 0 -10px !important;
+
+ .at-c-tabs__tab {
+ margin: 0 5px;
+ }
+
+ .at-c-tabs__tab.at-m-selected {
+ text-decoration: underline;
+ }
+}
+
+.at-c-orgPane {
+ a {
+ display: block;
+ }
+}
\ No newline at end of file
diff --git a/src/pages/Organizations/utils.jsx b/src/pages/Organizations/utils.jsx
new file mode 100644
index 0000000000..4db8f0627d
--- /dev/null
+++ b/src/pages/Organizations/utils.jsx
@@ -0,0 +1,17 @@
+const getTabName = (tab) => {
+ let tabName = '';
+ if (tab === 'details') {
+ tabName = 'Details';
+ } else if (tab === 'users') {
+ tabName = 'Users';
+ } else if (tab === 'teams') {
+ tabName = 'Teams';
+ } else if (tab === 'admins') {
+ tabName = 'Admins';
+ } else if (tab === 'notifications') {
+ tabName = 'Notifications';
+ }
+ return tabName;
+};
+
+export default getTabName;
diff --git a/src/pages/Organizations/views/Organization.add.jsx b/src/pages/Organizations/views/Organization.add.jsx
new file mode 100644
index 0000000000..831cc0c243
--- /dev/null
+++ b/src/pages/Organizations/views/Organization.add.jsx
@@ -0,0 +1,21 @@
+import React, { Fragment } from 'react';
+import {
+ PageSection,
+ PageSectionVariants,
+ Title,
+} from '@patternfly/react-core';
+
+const { light, medium } = PageSectionVariants;
+
+const OrganizationView = () => (
+
+
+ Organization Add
+
+
+ This is the add view
+
+
+);
+
+export default OrganizationView;
diff --git a/src/pages/Organizations/views/Organization.view.jsx b/src/pages/Organizations/views/Organization.view.jsx
new file mode 100644
index 0000000000..f714eda0a5
--- /dev/null
+++ b/src/pages/Organizations/views/Organization.view.jsx
@@ -0,0 +1,120 @@
+import React, { Component, Fragment } from 'react';
+import {
+ Switch,
+ Route
+} from 'react-router-dom';
+
+import OrganizationBreadcrumb from '../components/OrganizationBreadcrumb';
+import OrganizationDetail from '../components/OrganizationDetail';
+import OrganizationEdit from '../components/OrganizationEdit';
+
+import api from '../../../api';
+import { API_ORGANIZATIONS } from '../../../endpoints';
+
+class OrganizationView extends Component {
+ constructor (props) {
+ super(props);
+
+ let { breadcrumb: parentBreadcrumbObj, organization } = props.location.state || {};
+ if (!parentBreadcrumbObj) {
+ parentBreadcrumbObj = 'loading';
+ }
+ if (!organization) {
+ organization = 'loading';
+ }
+ this.state = {
+ parentBreadcrumbObj,
+ organization,
+ error: false,
+ loading: false,
+ mounted: false
+ };
+ }
+
+ componentDidMount () {
+ this.setState({ mounted: true }, () => {
+ const { organization } = this.state;
+ if (organization === 'loading') {
+ this.fetchOrganization();
+ }
+ });
+ }
+
+ componentWillUnmount () {
+ this.setState({ mounted: false });
+ }
+
+ async fetchOrganization () {
+ const { mounted } = this.state;
+ if (mounted) {
+ this.setState({ error: false, loading: true });
+
+ const { match } = this.props;
+ const { parentBreadcrumbObj, organization } = this.state;
+ try {
+ const { data } = await api.get(`${API_ORGANIZATIONS}${match.params.id}/`);
+ if (organization === 'loading') {
+ this.setState({ organization: data });
+ }
+ const { name } = data;
+ if (parentBreadcrumbObj === 'loading') {
+ this.setState({ parentBreadcrumbObj: [{ name: 'Organizations', url: '/organizations' }, { name, url: match.url }] });
+ }
+ } catch (err) {
+ this.setState({ error: true });
+ } finally {
+ this.setState({ loading: false });
+ }
+ }
+ }
+
+ render () {
+ const { location, match } = this.props;
+ const { parentBreadcrumbObj, organization, error, loading } = this.state;
+ const params = new URLSearchParams(location.search);
+ const currentTab = params.get('tab') || 'details';
+
+ return (
+
+
+
+ (
+
+ )}
+ />
+ (
+
+ )}
+ />
+
+ {error ? 'error!' : ''}
+ {loading ? 'loading...' : ''}
+
+ );
+ }
+}
+
+export default OrganizationView;
diff --git a/src/pages/Organizations.jsx b/src/pages/Organizations/views/Organizations.list.jsx
similarity index 91%
rename from src/pages/Organizations.jsx
rename to src/pages/Organizations/views/Organizations.list.jsx
index 050031afeb..741eeb30f0 100644
--- a/src/pages/Organizations.jsx
+++ b/src/pages/Organizations/views/Organizations.list.jsx
@@ -11,17 +11,17 @@ import {
Title,
} from '@patternfly/react-core';
-import DataListToolbar from '../components/DataListToolbar';
+import DataListToolbar from '../../../components/DataListToolbar';
import OrganizationListItem from '../components/OrganizationListItem';
-import Pagination from '../components/Pagination';
+import Pagination from '../../../components/Pagination';
-import api from '../api';
-import { API_ORGANIZATIONS } from '../endpoints';
+import api from '../../../api';
+import { API_ORGANIZATIONS } from '../../../endpoints';
import {
encodeQueryString,
parseQueryString,
-} from '../qs';
+} from '../../../qs';
class Organizations extends Component {
columns = [
@@ -58,7 +58,6 @@ class Organizations extends Component {
componentDidMount () {
const queryParams = this.getQueryParams();
-
this.fetchOrganizations(queryParams);
}
@@ -122,7 +121,6 @@ class Organizations extends Component {
updateUrl (queryParams) {
const { history, location } = this.props;
-
const pathname = '/organizations';
const search = `?${encodeQueryString(queryParams)}`;
@@ -185,6 +183,8 @@ class Organizations extends Component {
results,
selected,
} = this.state;
+ const { match } = this.props;
+ const parentBreadcrumb = { name: 'Organizations', url: match.url };
return (
@@ -193,6 +193,7 @@ class Organizations extends Component {