Update breadcrumb and fetch new hosts when url changes

This commit is contained in:
Marliana Lara 2019-11-20 15:56:05 -05:00
parent fa144aa98f
commit faa0802d97
No known key found for this signature in database
GPG Key ID: 38C73B40DFA809EE
4 changed files with 165 additions and 201 deletions

View File

@ -1,4 +1,4 @@
import React, { Component } from 'react';
import React, { useEffect, useState } from 'react';
import { t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { Card, CardHeader, PageSection } from '@patternfly/react-core';
@ -16,186 +16,152 @@ import InventorySources from './InventorySources';
import { InventoriesAPI } from '@api';
import InventoryEdit from './InventoryEdit';
class Inventory extends Component {
constructor(props) {
super(props);
function Inventory({ history, i18n, location, match, setBreadcrumb }) {
const [contentError, setContentError] = useState(null);
const [hasContentLoading, setHasContentLoading] = useState(true);
const [inventory, setInventory] = useState(null);
this.state = {
contentError: null,
hasContentLoading: true,
inventory: null,
};
this.loadInventory = this.loadInventory.bind(this);
}
async componentDidMount() {
await this.loadInventory();
}
async componentDidUpdate(prevProps) {
const { location, match } = this.props;
const url = `/inventories/inventory/${match.params.id}/`;
if (
prevProps.location.pathname.startsWith(url) &&
prevProps.location !== location &&
location.pathname === `${url}details`
) {
await this.loadInventory();
}
}
async loadInventory() {
const { setBreadcrumb, match } = this.props;
const { id } = match.params;
this.setState({ contentError: null, hasContentLoading: true });
try {
const { data } = await InventoriesAPI.readDetail(id);
setBreadcrumb(data);
this.setState({ inventory: data });
} catch (err) {
this.setState({ contentError: err });
} finally {
this.setState({ hasContentLoading: false });
}
}
render() {
const { history, i18n, location, match } = this.props;
const { contentError, hasContentLoading, inventory } = this.state;
const tabsArray = [
{ name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 },
{ name: i18n._(t`Access`), link: `${match.url}/access`, id: 1 },
{ name: i18n._(t`Groups`), link: `${match.url}/groups`, id: 2 },
{ name: i18n._(t`Hosts`), link: `${match.url}/hosts`, id: 3 },
{ name: i18n._(t`Sources`), link: `${match.url}/sources`, id: 4 },
{
name: i18n._(t`Completed Jobs`),
link: `${match.url}/completed_jobs`,
id: 5,
},
];
let cardHeader = hasContentLoading ? null : (
<CardHeader style={{ padding: 0 }}>
<RoutedTabs history={history} tabsArray={tabsArray} />
<CardCloseButton linkTo="/inventories" />
</CardHeader>
);
if (
location.pathname.endsWith('edit') ||
location.pathname.endsWith('add')
) {
cardHeader = null;
useEffect(() => {
async function fetchData() {
try {
const { data } = await InventoriesAPI.readDetail(match.params.id);
setBreadcrumb(data);
setInventory(data);
} catch (error) {
setContentError(error);
} finally {
setHasContentLoading(false);
}
}
if (!hasContentLoading && contentError) {
return (
<PageSection>
<Card className="awx-c-card">
<ContentError error={contentError}>
{contentError.response.status === 404 && (
<span>
{i18n._(`Inventory not found.`)}{' '}
<Link to="/inventories">
{i18n._(`View all Inventories.`)}
</Link>
</span>
)}
</ContentError>
</Card>
</PageSection>
);
}
fetchData();
}, [match.params.id, setBreadcrumb]);
const tabsArray = [
{ name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 },
{ name: i18n._(t`Access`), link: `${match.url}/access`, id: 1 },
{ name: i18n._(t`Groups`), link: `${match.url}/groups`, id: 2 },
{ name: i18n._(t`Hosts`), link: `${match.url}/hosts`, id: 3 },
{ name: i18n._(t`Sources`), link: `${match.url}/sources`, id: 4 },
{
name: i18n._(t`Completed Jobs`),
link: `${match.url}/completed_jobs`,
id: 5,
},
];
let cardHeader = hasContentLoading ? null : (
<CardHeader style={{ padding: 0 }}>
<RoutedTabs history={history} tabsArray={tabsArray} />
<CardCloseButton linkTo="/inventories" />
</CardHeader>
);
if (location.pathname.endsWith('edit') || location.pathname.endsWith('add')) {
cardHeader = null;
}
if (!hasContentLoading && contentError) {
return (
<PageSection>
<Card className="awx-c-card">
{cardHeader}
<Switch>
<Redirect
from="/inventories/inventory/:id"
to="/inventories/inventory/:id/details"
exact
/>
{inventory && [
<Route
key="details"
path="/inventories/inventory/:id/details"
render={() => (
<InventoryDetail
match={match}
hasInventoryLoading={hasContentLoading}
inventory={inventory}
/>
)}
/>,
<Route
key="edit"
path="/inventories/inventory/:id/edit"
render={() => <InventoryEdit inventory={inventory} />}
/>,
<Route
key="host-add"
path="/inventories/inventory/:id/hosts/add"
render={() => <InventoryHostAdd />}
/>,
<Route
key="access"
path="/inventories/inventory/:id/access"
render={() => (
<ResourceAccessList
resource={inventory}
apiModel={InventoriesAPI}
/>
)}
/>,
<Route
key="groups"
path="/inventories/inventory/:id/groups"
render={() => <InventoryGroups inventory={inventory} />}
/>,
<Route
key="hosts"
path="/inventories/inventory/:id/hosts"
render={() => <InventoryHosts inventory={inventory} />}
/>,
<Route
key="sources"
path="/inventories/inventory/:id/sources"
render={() => <InventorySources inventory={inventory} />}
/>,
<Route
key="completed_jobs"
path="/inventories/inventory/:id/completed_jobs"
render={() => <InventoryCompletedJobs inventory={inventory} />}
/>,
<Route
key="not-found"
path="*"
render={() =>
!hasContentLoading && (
<ContentError isNotFound>
{match.params.id && (
<Link
to={`/inventories/inventory/${match.params.id}/details`}
>
{i18n._(`View Inventory Details`)}
</Link>
)}
</ContentError>
)
}
/>,
]}
</Switch>
<ContentError error={contentError}>
{contentError.response.status === 404 && (
<span>
{i18n._(`Inventory not found.`)}{' '}
<Link to="/inventories">{i18n._(`View all Inventories.`)}</Link>
</span>
)}
</ContentError>
</Card>
</PageSection>
);
}
return (
<PageSection>
<Card className="awx-c-card">
{cardHeader}
<Switch>
<Redirect
from="/inventories/inventory/:id"
to="/inventories/inventory/:id/details"
exact
/>
{inventory && [
<Route
key="details"
path="/inventories/inventory/:id/details"
render={() => (
<InventoryDetail
match={match}
hasInventoryLoading={hasContentLoading}
inventory={inventory}
/>
)}
/>,
<Route
key="edit"
path="/inventories/inventory/:id/edit"
render={() => <InventoryEdit inventory={inventory} />}
/>,
<Route
key="host-add"
path="/inventories/inventory/:id/hosts/add"
render={() => <InventoryHostAdd />}
/>,
<Route
key="access"
path="/inventories/inventory/:id/access"
render={() => (
<ResourceAccessList
resource={inventory}
apiModel={InventoriesAPI}
/>
)}
/>,
<Route
key="groups"
path="/inventories/inventory/:id/groups"
render={() => <InventoryGroups inventory={inventory} />}
/>,
<Route
key="hosts"
path="/inventories/inventory/:id/hosts"
render={() => <InventoryHosts />}
/>,
<Route
key="sources"
path="/inventories/inventory/:id/sources"
render={() => <InventorySources inventory={inventory} />}
/>,
<Route
key="completed_jobs"
path="/inventories/inventory/:id/completed_jobs"
render={() => <InventoryCompletedJobs inventory={inventory} />}
/>,
<Route
key="not-found"
path="*"
render={() =>
!hasContentLoading && (
<ContentError isNotFound>
{match.params.id && (
<Link
to={`/inventories/inventory/${match.params.id}/details`}
>
{i18n._(`View Inventory Details`)}
</Link>
)}
</ContentError>
)
}
/>,
]}
</Switch>
</Card>
</PageSection>
);
}
export { Inventory as _Inventory };

View File

@ -1,4 +1,5 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history';
import { InventoriesAPI } from '@api';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
@ -12,41 +13,38 @@ InventoriesAPI.readDetail.mockResolvedValue({
});
describe('<Inventory />', () => {
test('initially renders succesfully', async done => {
const wrapper = mountWithContexts(
<Inventory setBreadcrumb={() => {}} match={{ params: { id: 1 } }} />
);
await waitForElement(
wrapper,
'Inventory',
el => el.state('hasContentLoading') === true
);
await waitForElement(
wrapper,
'Inventory',
el => el.state('hasContentLoading') === false
);
let wrapper;
test('initially renders succesfully', async () => {
await act(async () => {
wrapper = mountWithContexts(
<Inventory setBreadcrumb={() => {}} match={{ params: { id: 1 } }} />
);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 6);
done();
});
test('should show content error when user attempts to navigate to erroneous route', async () => {
const history = createMemoryHistory({
initialEntries: ['/inventories/inventory/1/foobar'],
});
const wrapper = mountWithContexts(<Inventory setBreadcrumb={() => {}} />, {
context: {
router: {
history,
route: {
location: history.location,
match: {
params: { id: 1 },
url: '/inventories/inventory/1/foobar',
path: '/inventories/inventory/1/foobar',
await act(async () => {
wrapper = mountWithContexts(<Inventory setBreadcrumb={() => {}} />, {
context: {
router: {
history,
route: {
location: history.location,
match: {
params: { id: 1 },
url: '/inventories/inventory/1/foobar',
path: '/inventories/inventory/1/foobar',
},
},
},
},
},
});
});
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
});

View File

@ -20,7 +20,7 @@ const QS_CONFIG = getQSConfig('host', {
order_by: 'name',
});
function InventoryHosts({ i18n, location, match, inventory }) {
function InventoryHosts({ i18n, location, match }) {
const [actions, setActions] = useState(null);
const [contentError, setContentError] = useState(null);
const [deletionError, setDeletionError] = useState(null);
@ -47,7 +47,7 @@ function InventoryHosts({ i18n, location, match, inventory }) {
data: { actions: optionActions },
},
] = await Promise.all([
fetchHosts(inventory.id, location.search),
fetchHosts(match.params.id, location.search),
InventoriesAPI.readOptions(),
]);
@ -62,7 +62,7 @@ function InventoryHosts({ i18n, location, match, inventory }) {
}
fetchData();
}, [inventory, location]);
}, [match.params.id, location]);
const handleSelectAll = isSelected => {
setSelected(isSelected ? [...hosts] : []);
@ -88,7 +88,7 @@ function InventoryHosts({ i18n, location, match, inventory }) {
try {
const {
data: { count, results },
} = await fetchHosts(inventory.id, location.search);
} = await fetchHosts(match.params.id, location.search);
setHosts(results);
setHostCount(count);

View File

@ -81,7 +81,7 @@ describe('<InventoryHosts />', () => {
},
});
await act(async () => {
wrapper = mountWithContexts(<InventoryHosts inventory={mockInventory} />);
wrapper = mountWithContexts(<InventoryHosts />);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
});