Merge pull request #7444 from AlexSCorey/7412-OrganizationEditButton

Adds edit icon to the OrganizationTeams list 

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
softwarefactory-project-zuul[bot] 2020-07-07 17:32:34 +00:00 committed by GitHub
commit 23cab61408
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 162 additions and 17 deletions

View File

@ -6,6 +6,7 @@ import { t } from '@lingui/macro';
import { OrganizationsAPI } from '../../../api';
import PaginatedDataList from '../../../components/PaginatedDataList';
import { getQSConfig, parseQueryString } from '../../../util/qs';
import OrganizationTeamListItem from './OrganizationTeamListItem';
const QS_CONFIG = getQSConfig('team', {
page: 1,
@ -13,7 +14,7 @@ const QS_CONFIG = getQSConfig('team', {
order_by: 'name',
});
function OrganizationTeams({ id, i18n }) {
function OrganizationTeamList({ id, i18n }) {
const location = useLocation();
const [contentError, setContentError] = useState(null);
const [hasContentLoading, setHasContentLoading] = useState(false);
@ -70,13 +71,21 @@ function OrganizationTeams({ id, i18n }) {
key: 'name',
},
]}
renderItem={item => (
<OrganizationTeamListItem
key={item.id}
value={item.name}
team={item}
detailUrl={`/teams/${item.id}`}
/>
)}
/>
);
}
OrganizationTeams.propTypes = {
OrganizationTeamList.propTypes = {
id: PropTypes.number.isRequired,
};
export { OrganizationTeams as _OrganizationTeams };
export default withI18n()(OrganizationTeams);
export { OrganizationTeamList as _OrganizationTeamList };
export default withI18n()(OrganizationTeamList);

View File

@ -8,7 +8,7 @@ import {
} from '../../../../testUtils/enzymeHelpers';
import { sleep } from '../../../../testUtils/testUtils';
import OrganizationTeams from './OrganizationTeams';
import OrganizationTeamList from './OrganizationTeamList';
jest.mock('../../../api');
@ -16,16 +16,41 @@ const listData = {
data: {
count: 7,
results: [
{ id: 1, name: 'one', url: '/org/team/1' },
{ id: 2, name: 'two', url: '/org/team/2' },
{ id: 3, name: 'three', url: '/org/team/3' },
{ id: 4, name: 'four', url: '/org/team/4' },
{ id: 5, name: 'five', url: '/org/team/5' },
{
id: 1,
name: 'one',
url: '/org/team/1',
summary_fields: { user_capabilities: { edit: true, delete: true } },
},
{
id: 2,
name: 'two',
url: '/org/team/2',
summary_fields: { user_capabilities: { edit: true, delete: true } },
},
{
id: 3,
name: 'three',
url: '/org/team/3',
summary_fields: { user_capabilities: { edit: true, delete: true } },
},
{
id: 4,
name: 'four',
url: '/org/team/4',
summary_fields: { user_capabilities: { edit: true, delete: true } },
},
{
id: 5,
name: 'five',
url: '/org/team/5',
summary_fields: { user_capabilities: { edit: true, delete: true } },
},
],
},
};
describe('<OrganizationTeams />', () => {
describe('<OrganizationTeamList />', () => {
beforeEach(() => {
OrganizationsAPI.readTeams.mockResolvedValue(listData);
});
@ -37,7 +62,7 @@ describe('<OrganizationTeams />', () => {
test('renders succesfully', async () => {
await act(async () => {
mountWithContexts(
<OrganizationTeams
<OrganizationTeamList
id={1}
searchString=""
location={{ search: '', pathname: '/organizations/1/teams' }}
@ -48,8 +73,8 @@ describe('<OrganizationTeams />', () => {
test('should load teams on mount', async () => {
await act(async () => {
mountWithContexts(<OrganizationTeams id={1} searchString="" />).find(
'OrganizationTeams'
mountWithContexts(<OrganizationTeamList id={1} searchString="" />).find(
'OrganizationTeamList'
);
});
expect(OrganizationsAPI.readTeams).toHaveBeenCalledWith(1, {
@ -62,7 +87,9 @@ describe('<OrganizationTeams />', () => {
test('should pass fetched teams to PaginatedDatalist', async () => {
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<OrganizationTeams id={1} searchString="" />);
wrapper = mountWithContexts(
<OrganizationTeamList id={1} searchString="" />
);
});
await sleep(0);
wrapper.update();
@ -90,7 +117,7 @@ describe('<OrganizationTeams />', () => {
);
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<OrganizationTeams id={1} />);
wrapper = mountWithContexts(<OrganizationTeamList id={1} />);
});
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
});

View File

@ -0,0 +1,64 @@
import React from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import {
Button,
DataListAction,
DataListItem,
DataListItemRow,
DataListItemCells,
Tooltip,
} from '@patternfly/react-core';
import { t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { PencilAltIcon } from '@patternfly/react-icons';
import DataListCell from '../../../components/DataListCell';
function OrganizationTeamListItem({ i18n, team, detailUrl }) {
const labelId = `check-action-${team.id}`;
return (
<DataListItem aria-labelledby={labelId} id={`${team.id}`}>
<DataListItemRow>
<DataListItemCells
dataListCells={[
<DataListCell key="divider">
<span>
<Link to={`${detailUrl}/details`}>
<b aria-label={i18n._(t`team name`)}>{team.name}</b>
</Link>
</span>
</DataListCell>,
]}
/>
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
{team.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Team`)} position="top">
<Button
aria-label={i18n._(t`Edit Team`)}
css="grid-column: 2"
variant="plain"
component={Link}
to={`${detailUrl}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow>
</DataListItem>
);
}
OrganizationTeamListItem.propTypes = {
team: PropTypes.shape({ id: PropTypes.number, name: PropTypes.string })
.isRequired,
detailUrl: PropTypes.string.isRequired,
};
export default withI18n()(OrganizationTeamListItem);

View File

@ -0,0 +1,45 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
import OrganizationTeamListItem from './OrganizationTeamListItem';
const team = {
id: 1,
name: 'one',
url: '/org/team/1',
summary_fields: { user_capabilities: { edit: true, delete: true } },
};
describe('<OrganizationTeamListItem />', () => {
let wrapper;
test('should mount properly', async () => {
await act(async () => {
wrapper = mountWithContexts(
<OrganizationTeamListItem team={team} detailUrl="/teams/1" />
);
});
expect(wrapper.find('OrganizationTeamListItem').length).toBe(1);
});
test('should render proper data', async () => {
await act(async () => {
wrapper = mountWithContexts(
<OrganizationTeamListItem team={team} detailUrl="/teams/1" />
);
});
expect(wrapper.find(`b[aria-label="team name"]`).text()).toBe('one');
expect(wrapper.find('PencilAltIcon').length).toBe(1);
});
test('should not render edit button', async () => {
team.summary_fields.user_capabilities.edit = false;
await act(async () => {
wrapper = mountWithContexts(
<OrganizationTeamListItem team={team} detailUrl="/teams/1" />
);
});
expect(wrapper.find('PencilAltIcon').length).toBe(0);
});
});

View File

@ -1 +1 @@
export { default } from './OrganizationTeams';
export { default } from './OrganizationTeamList';