Merge pull request #6271 from AlexSCorey/6260-UsersOrgList

Adds User Organization List

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
softwarefactory-project-zuul[bot] 2020-03-24 22:38:56 +00:00 committed by GitHub
commit 62e93d5c57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 232 additions and 13 deletions

View File

@ -7,7 +7,9 @@ class Users extends Base {
}
associateRole(userId, roleId) {
return this.http.post(`${this.baseUrl}${userId}/roles/`, { id: roleId });
return this.http.post(`${this.baseUrl}${userId}/roles/`, {
id: roleId,
});
}
disassociateRole(userId, roleId) {
@ -16,6 +18,12 @@ class Users extends Base {
disassociate: true,
});
}
readOrganizations(userId, params) {
return this.http.get(`${this.baseUrl}${userId}/organizations/`, {
params,
});
}
}
export default Users;

View File

@ -130,10 +130,9 @@ class User extends Component {
render={() => <UserDetail user={user} />}
/>
)}
<Route
path="/users/:id/organizations"
render={() => <UserOrganizations id={Number(match.params.id)} />}
/>
<Route path="/users/:id/organizations">
<UserOrganizations id={Number(match.params.id)} />
</Route>
<Route
path="/users/:id/teams"
render={() => <UserTeams id={Number(match.params.id)} />}

View File

@ -0,0 +1,42 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import UserOrganizationListItem from './UserOrganizationListItem';
describe('<UserOrganizationListItem />', () => {
test('mounts correctly', () => {
let wrapper;
act(() => {
wrapper = mountWithContexts(
<UserOrganizationListItem
organization={{ name: 'foo', id: 1, description: 'Bar' }}
/>
);
});
expect(wrapper.find('UserOrganizationListItem').length).toBe(1);
});
test('render correct information', () => {
let wrapper;
act(() => {
wrapper = mountWithContexts(
<UserOrganizationListItem
organization={{ name: 'foo', id: 1, description: 'Bar' }}
/>
);
});
expect(
wrapper
.find('DataListCell')
.at(0)
.text()
).toBe('foo');
expect(
wrapper
.find('DataListCell')
.at(1)
.text()
).toBe('Bar');
expect(wrapper.find('Link').prop('to')).toBe('/organizations/1/details');
});
});

View File

@ -1,10 +1,7 @@
import React, { Component } from 'react';
import { CardBody } from '@components/Card';
import React from 'react';
import UserOrganizationsList from './UserOrganizationsList';
class UserAdd extends Component {
render() {
return <CardBody>Coming soon :)</CardBody>;
}
function UserOrganizations() {
return <UserOrganizationsList />;
}
export default UserAdd;
export default UserOrganizations;

View File

@ -0,0 +1,36 @@
import React from 'react';
import { Route } from 'react-router-dom';
import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import UserOrganizations from './UserOrganizations';
describe('<UserOrganizations />', () => {
test('userOrganizations mounts successfully', () => {
const history = createMemoryHistory({
initialEntries: ['/users/1/organizations'],
});
let wrapper;
act(() => {
wrapper = mountWithContexts(
<Route
path="/users/:id/organizations"
component={() => <UserOrganizations />}
/>,
{
context: {
router: {
history,
route: {
location: history.location,
match: { params: { id: 1 } },
},
},
},
}
);
});
waitForElement(wrapper, 'UserOrganizationList');
});
});

View File

@ -0,0 +1,71 @@
import React, { useCallback, useEffect } from 'react';
import { withI18n } from '@lingui/react';
import { useLocation, useParams } from 'react-router-dom';
import { t } from '@lingui/macro';
import PaginatedDataList from '@components/PaginatedDataList';
import useRequest from '@util/useRequest';
import { UsersAPI } from '@api';
import { getQSConfig, parseQueryString } from '@util/qs';
import UserOrganizationListItem from './UserOrganizationListItem';
const QS_CONFIG = getQSConfig('organizations', {
page: 1,
page_size: 20,
order_by: 'name',
type: 'organization',
});
function UserOrganizationsList({ i18n }) {
const location = useLocation();
const { id: userId } = useParams();
const {
result: { organizations, count },
error: contentError,
isLoading,
request: fetchOrgs,
} = useRequest(
useCallback(async () => {
const params = parseQueryString(QS_CONFIG, location.search);
const {
data: { results, count: orgCount },
} = await UsersAPI.readOrganizations(userId, params);
return {
organizations: results,
count: orgCount,
};
}, [userId, location.search]),
{
organizations: [],
count: 0,
}
);
useEffect(() => {
fetchOrgs();
}, [fetchOrgs]);
return (
<PaginatedDataList
items={organizations}
contentError={contentError}
hasContentLoading={isLoading}
itemCount={count}
pluralizedItemName={i18n._(t`Organizations`)}
qsConfig={QS_CONFIG}
renderItem={organization => (
<UserOrganizationListItem
key={organization.id}
value={organization.name}
organization={organization}
detailUrl={`/organizations/${organization.id}/details`}
onSelect={() => {}}
isSelected={false}
/>
)}
/>
);
}
export default withI18n()(UserOrganizationsList);

View File

@ -0,0 +1,66 @@
import React from 'react';
import { Route } from 'react-router-dom';
import { act } from 'react-dom/test-utils';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import { createMemoryHistory } from 'history';
import UserOrganizationsList from './UserOrganizationsList';
import { UsersAPI } from '@api';
jest.mock('@api/models/Users');
describe('<UserOrganizationlist />', () => {
let history;
let wrapper;
beforeEach(async () => {
history = createMemoryHistory({
initialEntries: ['/users/1/organizations'],
});
UsersAPI.readOrganizations.mockResolvedValue({
data: {
results: [
{
name: 'Foo',
id: 1,
description: 'Bar',
url: '/api/v2/organizations/1/',
},
],
count: 1,
},
});
await act(async () => {
wrapper = mountWithContexts(
<Route
path="/users/:id/organizations"
component={() => <UserOrganizationsList />}
/>,
{
context: {
router: {
history,
route: {
location: history.location,
match: { params: { id: 1 } },
},
},
},
}
);
});
});
afterEach(() => {
jest.clearAllMocks();
});
test('successfully mounts', async () => {
await waitForElement(wrapper, 'UserOrganizationListItem');
});
test('calls api to get organizations', () => {
expect(UsersAPI.readOrganizations).toBeCalledWith('1', {
order_by: 'name',
page: 1,
page_size: 20,
type: 'organization',
});
});
});