Makes entire hierarchy of schedule components generic

This commit is contained in:
mabashian 2020-03-05 17:46:23 -05:00
parent 01fe89e43c
commit 90e047821d
29 changed files with 110 additions and 109 deletions

View File

@ -20,19 +20,21 @@ import { TabbedCardHeader } from '@components/Card';
import { ScheduleDetail } from '@components/Schedule';
import { SchedulesAPI } from '@api';
function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) {
const [jobTemplateSchedule, setJobTemplateSchedule] = useState(null);
function Schedule({ i18n, setBreadcrumb, unifiedJobTemplate }) {
const [schedule, setSchedule] = useState(null);
const [contentLoading, setContentLoading] = useState(true);
const [contentError, setContentError] = useState(null);
const { id: jobTemplateId, scheduleId } = useParams();
const { scheduleId } = useParams();
const location = useLocation();
const { pathname } = location;
const pathRoot = pathname.substr(0, pathname.indexOf('schedules'));
useEffect(() => {
const loadData = async () => {
try {
const { data } = await SchedulesAPI.readDetail(scheduleId);
setJobTemplateSchedule(data);
setBreadcrumb(jobTemplate, data);
setSchedule(data);
setBreadcrumb(unifiedJobTemplate, data);
} catch (err) {
setContentError(err);
} finally {
@ -41,7 +43,7 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) {
};
loadData();
}, [location.pathname, scheduleId, jobTemplate, setBreadcrumb]);
}, [location.pathname, scheduleId, unifiedJobTemplate, setBreadcrumb]);
const tabsArray = [
{
@ -51,14 +53,12 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) {
{i18n._(t`Back to Schedules`)}
</>
),
link: `/templates/job_template/${jobTemplate.id}/schedules`,
link: `${pathRoot}schedules`,
id: 99,
},
{
name: i18n._(t`Details`),
link: `/templates/job_template/${
jobTemplate.id
}/schedules/${jobTemplateSchedule && jobTemplateSchedule.id}/details`,
link: `${pathRoot}schedules/${schedule && schedule.id}/details`,
id: 0,
},
];
@ -68,15 +68,13 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) {
}
if (
jobTemplateSchedule.summary_fields.unified_job_template.id !==
parseInt(jobTemplateId, 10)
schedule.summary_fields.unified_job_template.id !==
parseInt(unifiedJobTemplate.id, 10)
) {
return (
<ContentError>
{jobTemplateSchedule && (
<Link to={`/templates/job_template/${jobTemplate.id}/schedules`}>
{i18n._(t`View Template Schedules`)}
</Link>
{schedule && (
<Link to={`${pathRoot}schedules`}>{i18n._(t`View Schedules`)}</Link>
)}
</ContentError>
);
@ -95,9 +93,7 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) {
<TabbedCardHeader>
<RoutedTabs tabsArray={tabsArray} />
<CardActions>
<CardCloseButton
linkTo={`/templates/job_template/${jobTemplate.id}/schedules`}
/>
<CardCloseButton linkTo={`${pathRoot}schedules`} />
</CardActions>
</TabbedCardHeader>
);
@ -107,16 +103,16 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) {
{cardHeader}
<Switch>
<Redirect
from="/templates/job_template/:id/schedules/:scheduleId"
to="/templates/job_template/:id/schedules/:scheduleId/details"
from={`${pathRoot}schedules/:scheduleId`}
to={`${pathRoot}schedules/:scheduleId/details`}
exact
/>
{jobTemplateSchedule && [
{schedule && [
<Route
key="details"
path="/templates/job_template/:id/schedules/:scheduleId/details"
path={`${pathRoot}schedules/:scheduleId/details`}
render={() => {
return <ScheduleDetail schedule={jobTemplateSchedule} />;
return <ScheduleDetail schedule={schedule} />;
}}
/>,
]}
@ -126,11 +122,9 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) {
render={() => {
return (
<ContentError>
{jobTemplate && (
<Link
to={`/templates/job_template/${jobTemplate.id}/details`}
>
{i18n._(t`View Template Details`)}
{unifiedJobTemplate && (
<Link to={`${pathRoot}details`}>
{i18n._(t`View Details`)}
</Link>
)}
</ContentError>
@ -142,5 +136,5 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) {
);
}
export { JobTemplateSchedule as _JobTemplateSchedule };
export default withI18n()(JobTemplateSchedule);
export { Schedule as _Schedule };
export default withI18n()(Schedule);

View File

@ -4,8 +4,7 @@ import { Route } from 'react-router-dom';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history';
import JobTemplateSchedule from './JobTemplateSchedule';
import Schedule from './Schedule';
jest.mock('@api/models/Schedules');
@ -60,10 +59,10 @@ SchedulesAPI.readCredentials.mockResolvedValue({
},
});
describe('<JobTemplateSchedule />', () => {
describe('<Schedule />', () => {
let wrapper;
let history;
const jobTemplate = { id: 1, name: 'Mock JT' };
const unifiedJobTemplate = { id: 1, name: 'Mock JT' };
beforeAll(async () => {
history = createMemoryHistory({
initialEntries: ['/templates/job_template/1/schedules/1/details'],
@ -73,9 +72,9 @@ describe('<JobTemplateSchedule />', () => {
<Route
path="/templates/job_template/:id/schedules"
component={() => (
<JobTemplateSchedule
<Schedule
setBreadcrumb={() => {}}
jobTemplate={jobTemplate}
unifiedJobTemplate={unifiedJobTemplate}
/>
)}
/>,

View File

@ -0,0 +1 @@
export { default } from './ScheduleDetail';

View File

@ -7,13 +7,13 @@ import { SchedulesAPI } from '@api';
import AlertModal from '@components/AlertModal';
import ErrorDetail from '@components/ErrorDetail';
import DataListToolbar from '@components/DataListToolbar';
import { ScheduleListItem } from '@components/Schedule';
import PaginatedDataList, {
ToolbarAddButton,
ToolbarDeleteButton,
} from '@components/PaginatedDataList';
import useRequest, { useDeleteItems } from '@util/useRequest';
import { getQSConfig, parseQueryString } from '@util/qs';
import ScheduleListItem from './ScheduleListItem';
const QS_CONFIG = getQSConfig('schedule', {
page: 1,

View File

@ -2,8 +2,8 @@ import React from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import { SchedulesAPI } from '@api';
import { ScheduleList } from '@components/Schedule';
import mockSchedules from './data.schedules.json';
import ScheduleList from './ScheduleList';
import mockSchedules from '../data.schedules.json';
jest.mock('@api/models/Schedules');

View File

@ -1,6 +1,6 @@
import React from 'react';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import { ScheduleListItem } from '@components/Schedule';
import ScheduleListItem from './ScheduleListItem';
const mockSchedule = {
rrule:

View File

@ -0,0 +1 @@
export { default } from './ScheduleList';

View File

@ -0,0 +1 @@
export { default } from './ScheduleOccurrences';

View File

@ -0,0 +1 @@
export { default } from './ScheduleToggle';

View File

@ -0,0 +1,33 @@
import React from 'react';
import { withI18n } from '@lingui/react';
import { Switch, Route, useRouteMatch } from 'react-router-dom';
import { Schedule, ScheduleList } from '@components/Schedule';
function Schedules({ setBreadcrumb, unifiedJobTemplate, loadSchedules }) {
const match = useRouteMatch();
return (
<Switch>
<Route
key="details"
path={`${match.path}/:scheduleId`}
render={() => (
<Schedule
unifiedJobTemplate={unifiedJobTemplate}
setBreadcrumb={setBreadcrumb}
/>
)}
/>
<Route
key="list"
path={`${match.path}`}
render={() => {
return <ScheduleList loadSchedules={loadSchedules} />;
}}
/>
</Switch>
);
}
export { Schedules as _Schedules };
export default withI18n()(Schedules);

View File

@ -2,9 +2,9 @@ import React from 'react';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history';
import JobTemplateSchedules from './JobTemplateSchedules';
import Schedules from './Schedules';
describe('<JobTemplateSchedules />', () => {
describe('<Schedules />', () => {
test('initially renders successfully', async () => {
let wrapper;
const history = createMemoryHistory({
@ -14,10 +14,7 @@ describe('<JobTemplateSchedules />', () => {
await act(async () => {
wrapper = mountWithContexts(
<JobTemplateSchedules
setBreadcrumb={() => {}}
jobTemplate={jobTemplate}
/>,
<Schedules setBreadcrumb={() => {}} jobTemplate={jobTemplate} />,
{
context: {

View File

@ -1,5 +1,6 @@
export { default as Schedule } from './Schedule';
export { default as Schedules } from './Schedules';
export { default as ScheduleList } from './ScheduleList';
export { default as ScheduleListItem } from './ScheduleListItem';
export { default as ScheduleOccurrences } from './ScheduleOccurrences';
export { default as ScheduleToggle } from './ScheduleToggle';
export { default as ScheduleDetail } from './ScheduleDetail';

View File

@ -9,7 +9,7 @@ import RoutedTabs from '@components/RoutedTabs';
import ContentError from '@components/ContentError';
import NotificationList from '@components/NotificationList';
import { ResourceAccessList } from '@components/ResourceAccessList';
import { ScheduleList } from '@components/Schedule';
import { Schedules } from '@components/Schedule';
import ProjectDetail from './ProjectDetail';
import ProjectEdit from './ProjectEdit';
import ProjectJobTemplatesList from './ProjectJobTemplatesList';
@ -116,7 +116,7 @@ class Project extends Component {
}
render() {
const { location, match, me, i18n } = this.props;
const { location, match, me, i18n, setBreadcrumb } = this.props;
const {
project,
@ -175,7 +175,10 @@ class Project extends Component {
cardHeader = null;
}
if (location.pathname.endsWith('edit')) {
if (
location.pathname.endsWith('edit') ||
location.pathname.includes('schedules/')
) {
cardHeader = null;
}
@ -247,7 +250,9 @@ class Project extends Component {
<Route
path="/projects/:id/schedules"
render={() => (
<ScheduleList
<Schedules
setBreadcrumb={setBreadcrumb}
unifiedJobTemplate={project}
loadSchedules={this.loadSchedules}
loadScheduleOptions={this.loadScheduleOptions}
/>

View File

@ -8,7 +8,7 @@ import { ScheduleList } from '@components/Schedule';
import { SchedulesAPI } from '@api';
import { PageSection, Card } from '@patternfly/react-core';
function Schedules({ i18n }) {
function AllSchedules({ i18n }) {
const loadScheduleOptions = () => {
return SchedulesAPI.readOptions();
};
@ -41,4 +41,4 @@ function Schedules({ i18n }) {
);
}
export default withI18n()(Schedules);
export default withI18n()(AllSchedules);

View File

@ -1,9 +1,9 @@
import React from 'react';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import { createMemoryHistory } from 'history';
import Schedules from './Schedules';
import AllSchedules from './AllSchedules';
describe('<Schedules />', () => {
describe('<AllSchedules />', () => {
let wrapper;
afterEach(() => {
@ -11,7 +11,7 @@ describe('<Schedules />', () => {
});
test('initially renders succesfully', () => {
wrapper = mountWithContexts(<Schedules />);
wrapper = mountWithContexts(<AllSchedules />);
});
test('should display schedule list breadcrumb heading', () => {
@ -19,7 +19,7 @@ describe('<Schedules />', () => {
initialEntries: ['/schedules'],
});
wrapper = mountWithContexts(<Schedules />, {
wrapper = mountWithContexts(<AllSchedules />, {
context: {
router: {
history,

View File

@ -1 +1 @@
export { default } from './Schedules';
export { default } from './AllSchedules';

View File

@ -1 +0,0 @@
export { default } from './JobTemplateSchedule';

View File

@ -1,26 +0,0 @@
import React from 'react';
import { withI18n } from '@lingui/react';
import { Switch, Route, withRouter } from 'react-router-dom';
import JobTemplateSchedule from '../JobTemplateSchedule/JobTemplateSchedule';
function JobTemplateSchedules({ setBreadcrumb, jobTemplate }) {
return (
<Switch>
<Route
key="details"
path="/templates/job_template/:id/schedules/:scheduleId/"
render={() => (
<JobTemplateSchedule
jobTemplate={jobTemplate}
setBreadcrumb={setBreadcrumb}
/>
)}
/>
</Switch>
);
}
export { JobTemplateSchedules as _JobTemplateSchedules };
export default withI18n()(withRouter(JobTemplateSchedules));

View File

@ -1 +0,0 @@
export { default } from './JobTemplateSchedules';

View File

@ -10,11 +10,10 @@ import ContentError from '@components/ContentError';
import JobList from '@components/JobList';
import NotificationList from '@components/NotificationList';
import RoutedTabs from '@components/RoutedTabs';
import { ScheduleList } from '@components/Schedule';
import { Schedules } from '@components/Schedule';
import { ResourceAccessList } from '@components/ResourceAccessList';
import JobTemplateDetail from './JobTemplateDetail';
import JobTemplateEdit from './JobTemplateEdit';
import JobTemplateSchedules from './JobTemplateSchedules';
import { JobTemplatesAPI, OrganizationsAPI } from '@api';
import SurveyList from './shared/SurveyList';
@ -221,12 +220,14 @@ class Template extends Component {
)}
{template && (
<Route
key="groups"
key="schedules"
path="/templates/:templateType/:id/schedules"
render={() => (
<JobTemplateSchedules
<Schedules
setBreadcrumb={setBreadcrumb}
jobTemplate={template}
unifiedJobTemplate={template}
loadSchedules={this.loadSchedules}
loadScheduleOptions={this.loadScheduleOptions}
/>
)}
/>
@ -248,17 +249,6 @@ class Template extends Component {
<JobList defaultParams={{ job__job_template: template.id }} />
</Route>
)}
{template && (
<Route
path="/templates/:templateType/:id/schedules"
render={() => (
<ScheduleList
loadSchedules={this.loadSchedules}
loadScheduleOptions={this.loadScheduleOptions}
/>
)}
/>
)}
{template && (
<Route
path="/templates/:templateType/:id/survey"

View File

@ -58,10 +58,11 @@ describe('<Template />', () => {
const wrapper = mountWithContexts(
<Template setBreadcrumb={() => {}} me={mockMe} />
);
const tabs = await waitForElement(
wrapper,
'.pf-c-tabs__item',
el => el.length === 7
el => el.length === 6
);
expect(tabs.at(2).text()).toEqual('Notifications');
done();

View File

@ -10,7 +10,7 @@ import ContentError from '@components/ContentError';
import FullPage from '@components/FullPage';
import JobList from '@components/JobList';
import RoutedTabs from '@components/RoutedTabs';
import { ScheduleList } from '@components/Schedule';
import { Schedules } from '@components/Schedule';
import ContentLoading from '@components/ContentLoading';
import { WorkflowJobTemplatesAPI, CredentialsAPI } from '@api';
import WorkflowJobTemplateDetail from './WorkflowJobTemplateDetail';
@ -88,7 +88,7 @@ class WorkflowJobTemplate extends Component {
}
render() {
const { i18n, location, match } = this.props;
const { i18n, location, match, setBreadcrumb } = this.props;
const {
contentError,
hasContentLoading,
@ -152,7 +152,10 @@ class WorkflowJobTemplate extends Component {
return (
<PageSection>
<Card>
{location.pathname.endsWith('edit') ? null : cardHeader}
{location.pathname.endsWith('edit') ||
location.pathname.includes('schedules/')
? null
: cardHeader}
<Switch>
<Redirect
from="/templates/workflow_job_template/:id"
@ -207,9 +210,11 @@ class WorkflowJobTemplate extends Component {
)}
{template?.id && (
<Route
path="/templates/:templateType/:id/schedules"
path="/templates/workflow_job_template/:id/schedules"
render={() => (
<ScheduleList
<Schedules
setBreadcrumb={setBreadcrumb}
unifiedJobTemplate={template}
loadSchedules={this.loadSchedules}
loadScheduleOptions={this.loadScheduleOptions}
/>