Skip to content

Commit

Permalink
Fixes #32045 - Add start-end date pickers to job wizard
Browse files Browse the repository at this point in the history
  • Loading branch information
MariaAga committed Jul 6, 2021
1 parent 7cd9992 commit aa86599
Show file tree
Hide file tree
Showing 13 changed files with 437 additions and 77 deletions.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@
},
"devDependencies": {
"@babel/core": "^7.7.0",
"@theforeman/builder": "^4.14.0",
"@theforeman/eslint-plugin-foreman": "^4.14.0",
"@theforeman/stories": "^4.14.0",
"@theforeman/test": "^4.14.0",
"@theforeman/vendor-dev": "^4.14.0",
"@theforeman/builder": "^8.7.0",
"@theforeman/eslint-plugin-foreman": "^8.7.0",
"@theforeman/stories": "^8.7.0",
"@theforeman/test": "^8.7.0",
"@theforeman/vendor-dev": "^8.7.0",
"babel-eslint": "^10.0.0",
"eslint": "^6.8.0",
"prettier": "^1.19.1",
"@patternfly/react-catalog-view-extension": "^4.8.126",
"redux-mock-store": "^1.2.2"
},
"peerDependencies": {
"@theforeman/vendor": "^8.3.0"
"@theforeman/vendor": "^8.7.0"
}
}
24 changes: 20 additions & 4 deletions webpack/JobWizard/JobWizard.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { translate as __ } from 'foremanReact/common/I18n';
import history from 'foremanReact/history';
import CategoryAndTemplate from './steps/CategoryAndTemplate/';
import { AdvancedFields } from './steps/AdvancedFields/AdvancedFields';
import { JOB_TEMPLATE } from './JobWizardConstants';
import { selectTemplateError } from './JobWizardSelectors';
import { JOB_TEMPLATE, repeatTypes } from './JobWizardConstants';
import { selectTemplateError, selectJobTemplate } from './JobWizardSelectors';
import Schedule from './steps/Schedule/';
import './JobWizard.scss';

Expand All @@ -17,6 +17,15 @@ export const JobWizard = () => {
const [advancedValues, setAdvancedValues] = useState({});
const dispatch = useDispatch();

const [scheduleValue, setScheduleValue] = useState({
repeatType: repeatTypes.noRepeat,
repeatAmount: '',
starts: '',
ends: '',
isFuture: false,
isNeverEnds: false,
});

const setDefaults = useCallback(response => {
const responseJob = response.data;
setAdvancedValues({
Expand All @@ -37,7 +46,9 @@ export const JobWizard = () => {
}, [jobTemplateID, setDefaults, dispatch]);

const templateError = !!useSelector(selectTemplateError);
const isTemplate = !templateError && !!jobTemplateID;
const isTemplateLoaded =
Object.keys(useSelector(selectJobTemplate)).length > 1;
const isTemplate = !templateError && !!jobTemplateID && isTemplateLoaded;
const steps = [
{
name: __('Category and Template'),
Expand Down Expand Up @@ -73,7 +84,12 @@ export const JobWizard = () => {
},
{
name: __('Schedule'),
component: <Schedule />,
component: (
<Schedule
scheduleValue={scheduleValue}
setScheduleValue={setScheduleValue}
/>
),
canJumpTo: isTemplate,
},
{
Expand Down
14 changes: 14 additions & 0 deletions webpack/JobWizard/JobWizard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,18 @@
text-align: start;
}
}

.pf-c-date-picker {
vertical-align: top;
}

.time-picker {
width: 150px;
}

input[type='radio'],
input[type='checkbox'] {
// overwriting bootstrap/_forms.scss margin: 4px 0 0;
margin: 0;
}
}
6 changes: 6 additions & 0 deletions webpack/JobWizard/__tests__/JobWizard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import JobWizardPage from '../index';

jest.spyOn(patternfly, 'Wizard');
patternfly.Wizard.mockImplementation(props => <div>{props.navAriaLabel}</div>);
const mockDate = new Date(1466424490000);
const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate);

afterAll(() => {
spy.mockRestore();
});

const fixtures = {
'renders ': {},
Expand Down
2 changes: 1 addition & 1 deletion webpack/JobWizard/__tests__/fixtures.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const jobTemplate = {
export const jobTemplate = {
id: 178,
name: 'Run Command - Ansible Default',
template:
Expand Down
3 changes: 1 addition & 2 deletions webpack/JobWizard/__tests__/integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ api.get.mockImplementation(({ handleSuccess, ...action }) => {
return { type: 'get', ...action };
});

selectors.selectJobTemplate.mockImplementation(() => null);
selectors.selectJobTemplate.mockImplementation(() => ({}));
selectors.selectJobCategories.mockImplementation(() => jobCategories);
selectors.selectJobCategoriesStatus.mockImplementation(() => null);
selectors.selectJobTemplates.mockImplementation(() => jobTemplates);
Expand Down Expand Up @@ -108,7 +108,6 @@ describe('Job wizard fill', () => {

it('have all steps', async () => {
selectors.selectJobCategoriesStatus.mockImplementation(() => null);
selectors.selectJobTemplate.mockRestore();
selectors.selectJobTemplates.mockRestore();
selectors.selectJobCategories.mockRestore();
api.get.mockImplementation(({ handleSuccess, ...action }) => {
Expand Down
12 changes: 10 additions & 2 deletions webpack/JobWizard/steps/CategoryAndTemplate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ const ConnectedCategoryAndTemplate = ({

const jobCategoriesStatus = useSelector(selectJobCategoriesStatus);
useEffect(() => {
if (!jobCategoriesStatus) {
let mounted = true;
if (!jobCategoriesStatus && mounted) {
// Don't reload categories if they are already loaded
dispatch(
get({
Expand All @@ -41,11 +42,15 @@ const ConnectedCategoryAndTemplate = ({
})
);
}
return () => {
mounted = false;
};
}, [jobCategoriesStatus, dispatch, setCategory]);

const jobCategories = useSelector(selectJobCategories);
useEffect(() => {
if (category) {
let mounted = true;
if (category && mounted) {
const templatesUrlObject = new URI(templatesUrl);
dispatch(
get({
Expand All @@ -61,6 +66,9 @@ const ConnectedCategoryAndTemplate = ({
})
);
}
return () => {
mounted = false;
};
}, [category, dispatch, setJobTemplate]);

const jobTemplates = useSelector(selectJobTemplates);
Expand Down
45 changes: 24 additions & 21 deletions webpack/JobWizard/steps/Schedule/ScheduleType.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import React, { useState } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { FormGroup, Radio } from '@patternfly/react-core';
import { translate as __ } from 'foremanReact/common/I18n';

export const ScheduleType = () => {
const [isFuture, setIsFuture] = useState(false);
return (
<FormGroup label={__('Schedule type')} fieldId="schedule-type">
<Radio
isChecked={!isFuture}
name="schedule-type"
onChange={() => setIsFuture(false)}
id="schedule-type-now"
label={__('Execute now')}
/>
<Radio
isChecked={isFuture}
name="schedule-type"
onChange={() => setIsFuture(true)}
id="schedule-type-future"
label={__('Schedule for future execution')}
/>
</FormGroup>
);
export const ScheduleType = ({ isFuture, setIsFuture }) => (
<FormGroup label={__('Schedule type')} fieldId="schedule-type">
<Radio
isChecked={!isFuture}
name="schedule-type"
onChange={() => setIsFuture(false)}
id="schedule-type-now"
label={__('Execute now')}
/>
<Radio
isChecked={isFuture}
name="schedule-type"
onChange={() => setIsFuture(true)}
id="schedule-type-future"
label={__('Schedule for future execution')}
/>
</FormGroup>
);

ScheduleType.propTypes = {
isFuture: PropTypes.bool.isRequired,
setIsFuture: PropTypes.func.isRequired,
};
57 changes: 36 additions & 21 deletions webpack/JobWizard/steps/Schedule/StartEndDates.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,48 @@
import React, { useState } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { FormGroup, TextInput, Checkbox } from '@patternfly/react-core';
import { FormGroup, Checkbox } from '@patternfly/react-core';
import { translate as __ } from 'foremanReact/common/I18n';
import { DateTimePicker } from '../form/DateTimePicker';

// TODO: change to datepicker
export const StartEndDates = ({ starts, setStarts, ends, setEnds }) => {
const [isNeverEnds, setIsNeverEnds] = useState(false);
export const StartEndDates = ({
starts,
setStarts,
ends,
setEnds,
isNeverEnds,
setIsNeverEnds,
}) => {
const toggleIsNeverEnds = (checked, event) => {
const value = event?.target?.checked;
setIsNeverEnds(value);
setEnds('');
};
const validateEndDate = () => {
if (isNeverEnds) return 'success';
if (!starts || !ends) return 'success';
if (new Date(starts).getTime() <= new Date(ends).getTime())
return 'success';
return 'error';
};
return (
<>
<FormGroup label={__('Starts')} fieldId="start-date">
<TextInput
id="start-date"
value={starts}
type="text"
onChange={newValue => setStarts(newValue)}
placeholder="mm/dd/yy, hh:mm UTC"
/>
<FormGroup
className="start-date"
label={__('Starts')}
fieldId="start-date"
>
<DateTimePicker dateTime={starts} setDateTime={setStarts} />
</FormGroup>
<FormGroup label={__('Ends')} fieldId="end-date">
<TextInput
<FormGroup
className="end-date"
label={__('Ends')}
fieldId="end-date"
helperTextInvalid={__('End time needs to be after start time')}
validated={validateEndDate()}
>
<DateTimePicker
dateTime={ends}
setDateTime={setEnds}
isDisabled={isNeverEnds}
id="end-date"
value={ends}
type="text"
onChange={newValue => setEnds(newValue)}
placeholder="mm/dd/yy, hh:mm UTC"
/>
<Checkbox
label={__('Never ends')}
Expand All @@ -48,4 +61,6 @@ StartEndDates.propTypes = {
setStarts: PropTypes.func.isRequired,
ends: PropTypes.string.isRequired,
setEnds: PropTypes.func.isRequired,
isNeverEnds: PropTypes.bool.isRequired,
setIsNeverEnds: PropTypes.func.isRequired,
};

0 comments on commit aa86599

Please sign in to comment.