mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 18:40:01 -03:30
Refactor Teams and Team components
This commit is contained in:
parent
9cb7b0902a
commit
de75592f2a
@ -1,7 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Switch, Route, withRouter, Redirect, Link } from 'react-router-dom';
|
||||
import {
|
||||
Link,
|
||||
Redirect,
|
||||
Route,
|
||||
Switch,
|
||||
useLocation,
|
||||
useParams,
|
||||
} from 'react-router-dom';
|
||||
import { Card, PageSection } from '@patternfly/react-core';
|
||||
import CardCloseButton from '@components/CardCloseButton';
|
||||
import { TabbedCardHeader } from '@components/Card';
|
||||
@ -11,148 +18,110 @@ import TeamDetail from './TeamDetail';
|
||||
import TeamEdit from './TeamEdit';
|
||||
import { TeamsAPI } from '@api';
|
||||
|
||||
class Team extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
function Team({ i18n, setBreadcrumb }) {
|
||||
const [team, setTeam] = useState(null);
|
||||
const [contentError, setContentError] = useState(null);
|
||||
const [hasContentLoading, setHasContentLoading] = useState(true);
|
||||
const location = useLocation();
|
||||
const { id } = useParams();
|
||||
|
||||
this.state = {
|
||||
team: null,
|
||||
hasContentLoading: true,
|
||||
contentError: null,
|
||||
isInitialized: false,
|
||||
};
|
||||
this.loadTeam = this.loadTeam.bind(this);
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const { data } = await TeamsAPI.readDetail(id);
|
||||
setBreadcrumb(data);
|
||||
setTeam(data);
|
||||
} catch (error) {
|
||||
setContentError(error);
|
||||
} finally {
|
||||
setHasContentLoading(false);
|
||||
}
|
||||
})();
|
||||
}, [id, setBreadcrumb]);
|
||||
|
||||
const tabsArray = [
|
||||
{ name: i18n._(t`Details`), link: `/teams/${id}/details`, id: 0 },
|
||||
{ name: i18n._(t`Users`), link: `/teams/${id}/users`, id: 1 },
|
||||
{ name: i18n._(t`Access`), link: `/teams/${id}/access`, id: 2 },
|
||||
];
|
||||
|
||||
let cardHeader = (
|
||||
<TabbedCardHeader>
|
||||
<RoutedTabs tabsArray={tabsArray} />
|
||||
<CardCloseButton linkTo="/teams" />
|
||||
</TabbedCardHeader>
|
||||
);
|
||||
|
||||
if (location.pathname.endsWith('edit')) {
|
||||
cardHeader = null;
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
await this.loadTeam();
|
||||
this.setState({ isInitialized: true });
|
||||
}
|
||||
|
||||
async componentDidUpdate(prevProps) {
|
||||
const { location, match } = this.props;
|
||||
const url = `/teams/${match.params.id}/`;
|
||||
|
||||
if (
|
||||
prevProps.location.pathname.startsWith(url) &&
|
||||
prevProps.location !== location &&
|
||||
location.pathname === `${url}details`
|
||||
) {
|
||||
await this.loadTeam();
|
||||
}
|
||||
}
|
||||
|
||||
async loadTeam() {
|
||||
const { match, setBreadcrumb } = this.props;
|
||||
const id = parseInt(match.params.id, 10);
|
||||
|
||||
this.setState({ contentError: null, hasContentLoading: true });
|
||||
try {
|
||||
const { data } = await TeamsAPI.readDetail(id);
|
||||
setBreadcrumb(data);
|
||||
this.setState({ team: data });
|
||||
} catch (err) {
|
||||
this.setState({ contentError: err });
|
||||
} finally {
|
||||
this.setState({ hasContentLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { location, match, i18n } = this.props;
|
||||
|
||||
const { team, contentError, hasContentLoading, isInitialized } = this.state;
|
||||
|
||||
const tabsArray = [
|
||||
{ name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 },
|
||||
{ name: i18n._(t`Users`), link: `${match.url}/users`, id: 1 },
|
||||
{ name: i18n._(t`Access`), link: `${match.url}/access`, id: 2 },
|
||||
];
|
||||
|
||||
let cardHeader = (
|
||||
<TabbedCardHeader>
|
||||
<RoutedTabs tabsArray={tabsArray} />
|
||||
<CardCloseButton linkTo="/teams" />
|
||||
</TabbedCardHeader>
|
||||
);
|
||||
|
||||
if (!isInitialized) {
|
||||
cardHeader = null;
|
||||
}
|
||||
|
||||
if (location.pathname.endsWith('edit')) {
|
||||
cardHeader = null;
|
||||
}
|
||||
|
||||
if (!hasContentLoading && contentError) {
|
||||
return (
|
||||
<PageSection>
|
||||
<Card className="awx-c-card">
|
||||
<ContentError error={contentError}>
|
||||
{contentError.response.status === 404 && (
|
||||
<span>
|
||||
{i18n._(`Team not found.`)}{' '}
|
||||
<Link to="/teams">{i18n._(`View all Teams.`)}</Link>
|
||||
</span>
|
||||
)}
|
||||
</ContentError>
|
||||
</Card>
|
||||
</PageSection>
|
||||
);
|
||||
}
|
||||
|
||||
if (!hasContentLoading && contentError) {
|
||||
return (
|
||||
<PageSection>
|
||||
<Card className="awx-c-card">
|
||||
{cardHeader}
|
||||
<Switch>
|
||||
<Redirect from="/teams/:id" to="/teams/:id/details" exact />
|
||||
{team && (
|
||||
<Route
|
||||
path="/teams/:id/edit"
|
||||
render={() => <TeamEdit team={team} />}
|
||||
/>
|
||||
<ContentError error={contentError}>
|
||||
{contentError.response.status === 404 && (
|
||||
<span>
|
||||
{i18n._(`Team not found.`)}{' '}
|
||||
<Link to="/teams">{i18n._(`View all Teams.`)}</Link>
|
||||
</span>
|
||||
)}
|
||||
{team && (
|
||||
<Route
|
||||
path="/teams/:id/details"
|
||||
render={() => <TeamDetail team={team} />}
|
||||
/>
|
||||
)}
|
||||
{team && (
|
||||
<Route
|
||||
path="/teams/:id/users"
|
||||
render={() => <span>Coming soon :)</span>}
|
||||
/>
|
||||
)}
|
||||
{team && (
|
||||
<Route
|
||||
path="/teams/:id/access"
|
||||
render={() => <span>Coming soon :)</span>}
|
||||
/>
|
||||
)}
|
||||
<Route
|
||||
key="not-found"
|
||||
path="*"
|
||||
render={() =>
|
||||
!hasContentLoading && (
|
||||
<ContentError isNotFound>
|
||||
{match.params.id && (
|
||||
<Link to={`/teams/${match.params.id}/details`}>
|
||||
{i18n._(`View Team Details`)}
|
||||
</Link>
|
||||
)}
|
||||
</ContentError>
|
||||
)
|
||||
}
|
||||
/>
|
||||
,
|
||||
</Switch>
|
||||
</ContentError>
|
||||
</Card>
|
||||
</PageSection>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PageSection>
|
||||
<Card className="awx-c-card">
|
||||
{cardHeader}
|
||||
<Switch>
|
||||
<Redirect from="/teams/:id" to="/teams/:id/details" exact />
|
||||
{team && (
|
||||
<Route path="/teams/:id/details">
|
||||
<TeamDetail team={team} />
|
||||
</Route>
|
||||
)}
|
||||
{team && (
|
||||
<Route
|
||||
path="/teams/:id/edit"
|
||||
render={() => <TeamEdit team={team} />}
|
||||
/>
|
||||
)}
|
||||
{team && (
|
||||
<Route
|
||||
path="/teams/:id/users"
|
||||
render={() => <span>Coming soon :)</span>}
|
||||
/>
|
||||
)}
|
||||
{team && (
|
||||
<Route
|
||||
path="/teams/:id/access"
|
||||
render={() => <span>Coming soon :)</span>}
|
||||
/>
|
||||
)}
|
||||
<Route
|
||||
key="not-found"
|
||||
path="*"
|
||||
render={() =>
|
||||
!hasContentLoading && (
|
||||
<ContentError isNotFound>
|
||||
{id && (
|
||||
<Link to={`/teams/${id}/details`}>
|
||||
{i18n._(`View Team Details`)}
|
||||
</Link>
|
||||
)}
|
||||
</ContentError>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Switch>
|
||||
</Card>
|
||||
</PageSection>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n()(withRouter(Team));
|
||||
export default withI18n()(Team);
|
||||
export { Team as _Team };
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { TeamsAPI } from '@api';
|
||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||
@ -34,34 +35,46 @@ async function getTeams() {
|
||||
}
|
||||
|
||||
describe('<Team />', () => {
|
||||
test('initially renders succesfully', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
TeamsAPI.readDetail.mockResolvedValue({ data: mockTeam });
|
||||
TeamsAPI.read.mockImplementation(getTeams);
|
||||
mountWithContexts(<Team setBreadcrumb={() => {}} me={mockMe} />);
|
||||
});
|
||||
|
||||
test('initially renders succesfully', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<Team setBreadcrumb={() => {}} me={mockMe} />
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('Team').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should show content error when user attempts to navigate to erroneous route', async () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/teams/1/foobar'],
|
||||
});
|
||||
const wrapper = mountWithContexts(
|
||||
<Team setBreadcrumb={() => {}} me={mockMe} />,
|
||||
{
|
||||
context: {
|
||||
router: {
|
||||
history,
|
||||
route: {
|
||||
location: history.location,
|
||||
match: {
|
||||
params: { id: 1 },
|
||||
url: '/teams/1/foobar',
|
||||
path: '/teams/1/foobar',
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<Team setBreadcrumb={() => {}} me={mockMe} />,
|
||||
{
|
||||
context: {
|
||||
router: {
|
||||
history,
|
||||
route: {
|
||||
location: history.location,
|
||||
match: {
|
||||
params: { id: 1 },
|
||||
url: '/teams/1/foobar',
|
||||
path: '/teams/1/foobar',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
export { default as TeamList } from './TeamList';
|
||||
export { default } from './TeamList';
|
||||
export { default as TeamListItem } from './TeamListItem';
|
||||
|
||||
@ -1,79 +1,58 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { Route, withRouter, Switch } from 'react-router-dom';
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
|
||||
import { Config } from '@contexts/Config';
|
||||
import Breadcrumbs from '@components/Breadcrumbs/Breadcrumbs';
|
||||
|
||||
import TeamsList from './TeamList/TeamList';
|
||||
import TeamAdd from './TeamAdd/TeamAdd';
|
||||
import Breadcrumbs from '@components/Breadcrumbs';
|
||||
import TeamList from './TeamList';
|
||||
import TeamAdd from './TeamAdd';
|
||||
import Team from './Team';
|
||||
|
||||
class Teams extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
function Teams({ i18n }) {
|
||||
const [breadcrumbConfig, setBreadcrumbConfig] = useState({
|
||||
'/teams': i18n._(t`Teams`),
|
||||
'/teams/add': i18n._(t`Create New Team`),
|
||||
});
|
||||
|
||||
const { i18n } = props;
|
||||
const buildBreadcrumbConfig = useCallback(
|
||||
team => {
|
||||
if (!team) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state = {
|
||||
breadcrumbConfig: {
|
||||
setBreadcrumbConfig({
|
||||
'/teams': i18n._(t`Teams`),
|
||||
'/teams/add': i18n._(t`Create New Team`),
|
||||
},
|
||||
};
|
||||
}
|
||||
[`/teams/${team.id}`]: `${team.name}`,
|
||||
[`/teams/${team.id}/edit`]: i18n._(t`Edit Details`),
|
||||
[`/teams/${team.id}/details`]: i18n._(t`Details`),
|
||||
[`/teams/${team.id}/users`]: i18n._(t`Users`),
|
||||
[`/teams/${team.id}/access`]: i18n._(t`Access`),
|
||||
});
|
||||
},
|
||||
[i18n]
|
||||
);
|
||||
|
||||
setBreadcrumbConfig = team => {
|
||||
const { i18n } = this.props;
|
||||
|
||||
if (!team) {
|
||||
return;
|
||||
}
|
||||
|
||||
const breadcrumbConfig = {
|
||||
'/teams': i18n._(t`Teams`),
|
||||
'/teams/add': i18n._(t`Create New Team`),
|
||||
[`/teams/${team.id}`]: `${team.name}`,
|
||||
[`/teams/${team.id}/edit`]: i18n._(t`Edit Details`),
|
||||
[`/teams/${team.id}/details`]: i18n._(t`Details`),
|
||||
[`/teams/${team.id}/users`]: i18n._(t`Users`),
|
||||
[`/teams/${team.id}/access`]: i18n._(t`Access`),
|
||||
};
|
||||
|
||||
this.setState({ breadcrumbConfig });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { match, history, location } = this.props;
|
||||
const { breadcrumbConfig } = this.state;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Breadcrumbs breadcrumbConfig={breadcrumbConfig} />
|
||||
<Switch>
|
||||
<Route path={`${match.path}/add`} render={() => <TeamAdd />} />
|
||||
<Route
|
||||
path={`${match.path}/:id`}
|
||||
render={() => (
|
||||
<Config>
|
||||
{({ me }) => (
|
||||
<Team
|
||||
history={history}
|
||||
location={location}
|
||||
setBreadcrumb={this.setBreadcrumbConfig}
|
||||
me={me || {}}
|
||||
/>
|
||||
)}
|
||||
</Config>
|
||||
)}
|
||||
/>
|
||||
<Route path={`${match.path}`} render={() => <TeamsList />} />
|
||||
</Switch>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Breadcrumbs breadcrumbConfig={breadcrumbConfig} />
|
||||
<Switch>
|
||||
<Route path="/teams/add">
|
||||
<TeamAdd />
|
||||
</Route>
|
||||
<Route path="/teams/:id">
|
||||
<Team setBreadcrumb={buildBreadcrumbConfig} />
|
||||
</Route>
|
||||
<Route path="/teams">
|
||||
<Config>
|
||||
{({ me }) => <TeamList path="/teams" me={me || {}} />}
|
||||
</Config>
|
||||
</Route>
|
||||
</Switch>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { Teams as _Teams };
|
||||
export default withI18n()(withRouter(Teams));
|
||||
export default withI18n()(Teams);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user