add cred access tab and update credentials routing

This commit is contained in:
John Mitchell
2020-02-18 14:40:09 -05:00
parent 0aba4c36af
commit 8e0ad2ef6e
3 changed files with 81 additions and 38 deletions

View File

@@ -6,12 +6,14 @@ import {
Switch, Switch,
useParams, useParams,
useLocation, useLocation,
useRouteMatch,
Route, Route,
Redirect, Redirect,
Link, Link,
} from 'react-router-dom'; } from 'react-router-dom';
import { TabbedCardHeader } from '@components/Card'; import { TabbedCardHeader } from '@components/Card';
import CardCloseButton from '@components/CardCloseButton'; import CardCloseButton from '@components/CardCloseButton';
import { ResourceAccessList } from '@components/ResourceAccessList';
import ContentError from '@components/ContentError'; import ContentError from '@components/ContentError';
import RoutedTabs from '@components/RoutedTabs'; import RoutedTabs from '@components/RoutedTabs';
import CredentialDetail from './CredentialDetail'; import CredentialDetail from './CredentialDetail';
@@ -21,7 +23,10 @@ function Credential({ i18n, setBreadcrumb }) {
const [credential, setCredential] = useState(null); const [credential, setCredential] = useState(null);
const [contentError, setContentError] = useState(null); const [contentError, setContentError] = useState(null);
const [hasContentLoading, setHasContentLoading] = useState(true); const [hasContentLoading, setHasContentLoading] = useState(true);
const location = useLocation(); const { pathname } = useLocation();
const match = useRouteMatch({
path: '/credentials/:id',
});
const { id } = useParams(); const { id } = useParams();
useEffect(() => { useEffect(() => {
@@ -37,18 +42,20 @@ function Credential({ i18n, setBreadcrumb }) {
} }
} }
fetchData(); fetchData();
}, [id, setBreadcrumb]); }, [id, pathname, setBreadcrumb]);
const tabsArray = [ const tabsArray = [
{ name: i18n._(t`Details`), link: `/credentials/${id}/details`, id: 0 }, { name: i18n._(t`Details`), link: `/credentials/${id}/details`, id: 0 },
{ name: i18n._(t`Access`), link: `/credentials/${id}/access`, id: 1 },
{
name: i18n._(t`Notifications`),
link: `/credentials/${id}/notifications`,
id: 2,
},
]; ];
if (credential && credential.organization) {
tabsArray.push({
name: i18n._(t`Access`),
link: `/credentials/${id}/access`,
id: 1,
});
}
let cardHeader = hasContentLoading ? null : ( let cardHeader = hasContentLoading ? null : (
<TabbedCardHeader> <TabbedCardHeader>
<RoutedTabs tabsArray={tabsArray} /> <RoutedTabs tabsArray={tabsArray} />
@@ -56,7 +63,7 @@ function Credential({ i18n, setBreadcrumb }) {
</TabbedCardHeader> </TabbedCardHeader>
); );
if (location.pathname.endsWith('edit') || location.pathname.endsWith('add')) { if (pathname.endsWith('edit') || pathname.endsWith('add')) {
cardHeader = null; cardHeader = null;
} }
@@ -87,11 +94,40 @@ function Credential({ i18n, setBreadcrumb }) {
to="/credentials/:id/details" to="/credentials/:id/details"
exact exact
/> />
{credential && ( {credential && [
<Route path="/credentials/:id/details"> <Route
<CredentialDetail credential={credential} /> key="details"
</Route> path="/credentials/:id/details"
render={() => <CredentialDetail credential={credential} />}
/>,
credential.organization && (
<Route
key="access"
path="/credentials/:id/access"
render={() => (
<ResourceAccessList
resource={credential}
apiModel={CredentialsAPI}
/>
)}
/>
),
<Route
key="not-found"
path="*"
render={() =>
!hasContentLoading && (
<ContentError isNotFound>
{match.params.id && (
<Link to={`/credentials/${match.params.id}/details`}>
{i18n._(`View Credential Details`)}
</Link>
)} )}
</ContentError>
)
}
/>,
]}
<Route <Route
key="not-found" key="not-found"
path="*" path="*"

View File

@@ -3,31 +3,50 @@ import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history'; import { createMemoryHistory } from 'history';
import { CredentialsAPI } from '@api'; import { CredentialsAPI } from '@api';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import { mockCredentials } from './shared'; import mockCredential from './shared/data.credential.json';
import mockOrgCredential from './shared/data.orgCredential.json';
import Credential from './Credential'; import Credential from './Credential';
jest.mock('@api'); jest.mock('@api');
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useRouteMatch: () => ({
url: '/credentials/2',
params: { id: 2 },
}),
}));
CredentialsAPI.readDetail.mockResolvedValue({ CredentialsAPI.readDetail.mockResolvedValueOnce({
data: mockCredentials.results[0], data: mockCredential,
}); });
describe('<Credential />', () => { describe('<Credential />', () => {
let wrapper; let wrapper;
beforeEach(async () => { test('initially renders user-based credential succesfully', async () => {
await act(async () => { await act(async () => {
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />); wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />);
}); });
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 1);
}); });
test('initially renders succesfully', async () => { test('initially renders org-based credential succesfully', async () => {
expect(wrapper.find('Credential').length).toBe(1); CredentialsAPI.readDetail.mockResolvedValueOnce({
data: mockOrgCredential,
});
await act(async () => {
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
// org-based credential detail needs access tab
await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 2);
}); });
test('should show content error when user attempts to navigate to erroneous route', async () => { test('should show content error when user attempts to navigate to erroneous route', async () => {
const history = createMemoryHistory({ const history = createMemoryHistory({
initialEntries: ['/credentials/1/foobar'], initialEntries: ['/credentials/2/foobar'],
}); });
await act(async () => { await act(async () => {
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />, { wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />, {
@@ -38,8 +57,8 @@ describe('<Credential />', () => {
location: history.location, location: history.location,
match: { match: {
params: { id: 1 }, params: { id: 1 },
url: '/credentials/1/foobar', url: '/credentials/2/foobar',
path: '/credentials/1/foobar', path: '/credentials/2/foobar',
}, },
}, },
}, },
@@ -47,19 +66,5 @@ describe('<Credential />', () => {
}); });
}); });
await waitForElement(wrapper, 'ContentError', el => el.length === 1); await waitForElement(wrapper, 'ContentError', el => el.length === 1);
expect(wrapper.find('ContentError Title').text()).toEqual('Not Found');
});
test('should show content error if api throws an error', async () => {
CredentialsAPI.readDetail.mockImplementationOnce(() =>
Promise.reject(new Error())
);
await act(async () => {
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />);
});
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
expect(wrapper.find('ContentError Title').text()).toEqual(
'Something went wrong...'
);
}); });
}); });

View File

@@ -2,7 +2,7 @@ import React, { useState, useCallback } from 'react';
import { Route, Switch } from 'react-router-dom'; import { Route, Switch } from 'react-router-dom';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Config } from '@contexts/Config';
import Breadcrumbs from '@components/Breadcrumbs'; import Breadcrumbs from '@components/Breadcrumbs';
import Credential from './Credential'; import Credential from './Credential';
import CredentialAdd from './CredentialAdd'; import CredentialAdd from './CredentialAdd';
@@ -24,7 +24,9 @@ function Credentials({ i18n }) {
'/credentials': i18n._(t`Credentials`), '/credentials': i18n._(t`Credentials`),
'/credentials/add': i18n._(t`Create New Credential`), '/credentials/add': i18n._(t`Create New Credential`),
[`/credentials/${credential.id}`]: `${credential.name}`, [`/credentials/${credential.id}`]: `${credential.name}`,
[`/credentials/${credential.id}/edit`]: i18n._(t`Edit Details`),
[`/credentials/${credential.id}/details`]: i18n._(t`Details`), [`/credentials/${credential.id}/details`]: i18n._(t`Details`),
[`/credentials/${credential.id}/access`]: i18n._(t`Access`),
}); });
}, },
[i18n] [i18n]
@@ -35,7 +37,7 @@ function Credentials({ i18n }) {
<Breadcrumbs breadcrumbConfig={breadcrumbConfig} /> <Breadcrumbs breadcrumbConfig={breadcrumbConfig} />
<Switch> <Switch>
<Route path="/credentials/add"> <Route path="/credentials/add">
<CredentialAdd /> <Config>{({ me }) => <CredentialAdd me={me || {}} />}</Config>
</Route> </Route>
<Route path="/credentials/:id"> <Route path="/credentials/:id">
<Credential setBreadcrumb={buildBreadcrumbConfig} /> <Credential setBreadcrumb={buildBreadcrumbConfig} />