Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Assignment and course grade fields validations #366

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20,352 changes: 9,144 additions & 11,208 deletions package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ exports[`AssignmentFilter component render without selected assignment snapshot
className="grade-filter-action"
>
<Button
disabled={true}
disabled={false}
name="assignmentGradeMinMax"
type="submit"
variant="outline-secondary"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const useAssignmentGradeFilterData = ({ updateQueryParams }) => {
const fetchGrades = thunkActions.grades.useFetchGrades();
const setFilter = actions.app.useSetLocalFilter();
const updateAssignmentLimits = actions.filters.useUpdateAssignmentLimits();
const isDisabled = !selectors.app.useAreAssignmentGradeFiltersValid() || !selectedAssignment;

const handleSubmit = () => {
updateAssignmentLimits(localAssignmentLimits);
Expand All @@ -27,6 +28,7 @@ const useAssignmentGradeFilterData = ({ updateQueryParams }) => {
assignmentGradeMin,
assignmentGradeMax,
selectedAssignment,
isDisabled,
handleSetMax,
handleSetMin,
handleSubmit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import useAssignmentGradeFilterData from './hooks';

jest.mock('data/redux/hooks', () => ({
selectors: {
app: { useAssignmentGradeLimits: jest.fn() },
app: {
useAreAssignmentGradeFiltersValid: jest.fn(),
useAssignmentGradeLimits: jest.fn(),
},
filters: { useSelectedAssignmentLabel: jest.fn() },
},
actions: {
Expand All @@ -20,6 +23,7 @@ let out;

const assignmentGradeLimits = { assignmentGradeMax: 200, assignmentGradeMin: 3 };
const selectedAssignmentLabel = 'test-assignment-label';
const useAreAssignmentGradeFiltersValid = false;
selectors.app.useAssignmentGradeLimits.mockReturnValue(assignmentGradeLimits);
selectors.filters.useSelectedAssignmentLabel.mockReturnValue(selectedAssignmentLabel);

Expand All @@ -40,6 +44,7 @@ describe('useAssignmentFilterData hook', () => {
});
describe('behavior', () => {
it('initializes redux hooks', () => {
expect(selectors.app.useAreAssignmentGradeFiltersValid).toHaveBeenCalledWith();
expect(selectors.app.useAssignmentGradeLimits).toHaveBeenCalledWith();
expect(selectors.filters.useSelectedAssignmentLabel).toHaveBeenCalledWith();
expect(actions.app.useSetLocalFilter).toHaveBeenCalledWith();
Expand Down Expand Up @@ -77,5 +82,8 @@ describe('useAssignmentFilterData hook', () => {
expect(out.assignmentGradeMax).toEqual(assignmentGradeLimits.assignmentGradeMax);
expect(out.assignmentGradeMin).toEqual(assignmentGradeLimits.assignmentGradeMin);
});
it('passes isDisabled from hook', () => {
expect(out.isDisabled).toEqual(!useAreAssignmentGradeFiltersValid || !selectedAssignmentLabel);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const AssignmentGradeFilter = ({ updateQueryParams }) => {
assignmentGradeMin,
assignmentGradeMax,
selectedAssignment,
isDisabled,
handleSetMax,
handleSetMin,
handleSubmit,
Expand All @@ -39,7 +40,7 @@ export const AssignmentGradeFilter = ({ updateQueryParams }) => {
type="submit"
variant="outline-secondary"
name="assignmentGradeMinMax"
disabled={!selectedAssignment}
disabled={isDisabled}
onClick={handleSubmit}
>
{formatMessage(messages.apply)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const hookData = {
selectedAssignment: 'test-assignment',
assignmentGradeMax: 300,
assignmentGradeMin: 23,
isDisabled: false,
};
useAssignmentGradeFilterData.mockReturnValue(hookData);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,12 @@ exports[`StatusAlerts component render snapshot 1`] = `
>
hooks.grade-filter-text
</Alert>
<Alert
dismissible={false}
show="hooks.show-grade-filter"
variant="danger"
>
hooks.grade-filter-text
</Alert>
</Fragment>
`;
30 changes: 23 additions & 7 deletions src/components/GradesView/StatusAlerts/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,27 @@ import messages from './messages';
export const useStatusAlertsData = () => {
const { formatMessage } = useIntl();

const limitValidity = selectors.app.useCourseGradeFilterValidity();
const courseLimitValidity = selectors.app.useCourseGradeFilterValidity();
const assignmentLimitValidity = selectors.app.useAssignmentGradeFilterValidity();

const showSuccessBanner = selectors.grades.useShowSuccess();
const handleCloseSuccessBanner = actions.grades.useCloseBanner();

const isCourseGradeFilterAlertOpen = !limitValidity.isMinValid || !limitValidity.isMaxValid;
const isCourseGradeFilterAlertOpen = !courseLimitValidity.isMinValid
|| !courseLimitValidity.isMaxValid || !courseLimitValidity.isMinLessMaxValid;
const isAssignmentGradeFilterAlertOpen = !assignmentLimitValidity.isMinValid
|| !assignmentLimitValidity.isMaxValid || !assignmentLimitValidity.isMinLessMaxValid;

const courseValidityMessages = {
min: courseLimitValidity.isMinValid ? '' : formatMessage(messages.minGradeInvalid),
max: courseLimitValidity.isMaxValid ? '' : formatMessage(messages.maxGradeInvalid),
minLessMax: courseLimitValidity.isMinLessMaxValid ? '' : formatMessage(messages.minLessMaxGradeInvalid),
};

const validityMessages = {
min: limitValidity.isMinValid ? '' : formatMessage(messages.minGradeInvalid),
max: limitValidity.isMaxValid ? '' : formatMessage(messages.maxGradeInvalid),
const assignmentValidityMessages = {
min: assignmentLimitValidity.isMinValid ? '' : formatMessage(messages.minAssignmentInvalid),
max: assignmentLimitValidity.isMaxValid ? '' : formatMessage(messages.maxAssignmentInvalid),
minLessMax: assignmentLimitValidity.isMinLessMaxValid ? '' : formatMessage(messages.minLessMaxAssignmentInvalid),
};

return {
Expand All @@ -23,9 +35,13 @@ export const useStatusAlertsData = () => {
show: showSuccessBanner,
text: formatMessage(messages.editSuccessAlert),
},
gradeFilter: {
courseGradeFilter: {
show: isCourseGradeFilterAlertOpen,
text: `${validityMessages.min}${validityMessages.max}`,
text: `${courseValidityMessages.min} ${courseValidityMessages.max} ${courseValidityMessages.minLessMax}`,
},
assignmentGradeFilter: {
show: isAssignmentGradeFilterAlertOpen,
text: `${assignmentValidityMessages.min} ${assignmentValidityMessages.max} ${assignmentValidityMessages.minLessMax}`,
},
};
};
Expand Down
78 changes: 67 additions & 11 deletions src/components/GradesView/StatusAlerts/hooks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,21 @@ jest.mock('data/redux/hooks', () => ({
grades: { useCloseBanner: jest.fn() },
},
selectors: {
app: { useCourseGradeFilterValidity: jest.fn() },
app: {
useCourseGradeFilterValidity: jest.fn(),
useAssignmentGradeFilterValidity: jest.fn(),
},
grades: { useShowSuccess: jest.fn() },
},
}));

const validity = {
isMinValid: true,
isMaxValid: true,
isMinLessMaxValid: true,
};
selectors.app.useCourseGradeFilterValidity.mockReturnValue(validity);
selectors.app.useAssignmentGradeFilterValidity.mockReturnValue(validity);
const showSuccess = 'test-show-success';
selectors.grades.useShowSuccess.mockReturnValue(showSuccess);
const closeBanner = jest.fn().mockName('hooks.closeBanner');
Expand All @@ -39,6 +44,7 @@ describe('useStatusAlertsData', () => {
it('initializes redux hooks', () => {
expect(actions.grades.useCloseBanner).toHaveBeenCalled();
expect(selectors.app.useCourseGradeFilterValidity).toHaveBeenCalled();
expect(selectors.app.useAssignmentGradeFilterValidity).toHaveBeenCalled();
expect(selectors.grades.useShowSuccess).toHaveBeenCalled();
});
});
Expand All @@ -53,55 +59,105 @@ describe('useStatusAlertsData', () => {
});
});
describe('gradeFilter', () => {
describe('both filters are valid', () => {
describe('all filters are valid', () => {
test('do not show', () => {
expect(out.gradeFilter.show).toEqual(false);
expect(out.courseGradeFilter.show).toEqual(false);
expect(out.assignmentGradeFilter.show).toEqual(false);
});
});
describe('min filter is invalid', () => {
beforeEach(() => {
selectors.app.useCourseGradeFilterValidity.mockReturnValue({
isMinValid: false,
isMaxValid: true,
isMinLessMaxValid: true,
});
selectors.app.useAssignmentGradeFilterValidity.mockReturnValue({
isMinValid: false,
isMaxValid: true,
isMinLessMaxValid: true,
});
out = useStatusAlertsData();
});
test('show grade filter banner', () => {
expect(out.gradeFilter.show).toEqual(true);
expect(out.courseGradeFilter.show).toEqual(true);
expect(out.assignmentGradeFilter.show).toEqual(true);
});
test('filter message', () => {
expect(out.gradeFilter.text).toEqual(formatMessage(messages.minGradeInvalid));
expect(out.courseGradeFilter.text).toEqual(`${formatMessage(messages.minGradeInvalid)} `);
expect(out.assignmentGradeFilter.text).toEqual(`${formatMessage(messages.minAssignmentInvalid)} `);
});
});
describe('max filter is invalid', () => {
beforeEach(() => {
selectors.app.useCourseGradeFilterValidity.mockReturnValue({
isMinValid: true,
isMaxValid: false,
isMinLessMaxValid: true,
});
selectors.app.useAssignmentGradeFilterValidity.mockReturnValue({
isMinValid: true,
isMaxValid: false,
isMinLessMaxValid: true,
});
out = useStatusAlertsData();
});
test('show grade filter banner', () => {
expect(out.gradeFilter.show).toEqual(true);
expect(out.courseGradeFilter.show).toEqual(true);
expect(out.assignmentGradeFilter.show).toEqual(true);
});
test('filter message', () => {
expect(out.gradeFilter.text).toEqual(formatMessage(messages.maxGradeInvalid));
expect(out.courseGradeFilter.text).toEqual(` ${formatMessage(messages.maxGradeInvalid)} `);
expect(out.assignmentGradeFilter.text).toEqual(` ${formatMessage(messages.maxAssignmentInvalid)} `);
});
});
describe('both filters are invalid', () => {
describe('minLessMax filter is invalid', () => {
beforeEach(() => {
selectors.app.useCourseGradeFilterValidity.mockReturnValue({
isMinValid: true,
isMaxValid: true,
isMinLessMaxValid: false,
});
selectors.app.useAssignmentGradeFilterValidity.mockReturnValue({
isMinValid: true,
isMaxValid: true,
isMinLessMaxValid: false,
});
out = useStatusAlertsData();
});
test('show grade filter banner', () => {
expect(out.courseGradeFilter.show).toEqual(true);
expect(out.assignmentGradeFilter.show).toEqual(true);
});
test('filter message', () => {
expect(out.courseGradeFilter.text).toEqual(` ${formatMessage(messages.minLessMaxGradeInvalid)}`);
expect(out.assignmentGradeFilter.text).toEqual(` ${formatMessage(messages.minLessMaxAssignmentInvalid)}`);
});
});
describe('all filters are invalid', () => {
beforeEach(() => {
selectors.app.useCourseGradeFilterValidity.mockReturnValue({
isMinValid: false,
isMaxValid: false,
isMinLessMaxValid: false,
});
selectors.app.useAssignmentGradeFilterValidity.mockReturnValue({
isMinValid: false,
isMaxValid: false,
isMinLessMaxValid: false,
});
out = useStatusAlertsData();
});
test('show grade filter banner', () => {
expect(out.gradeFilter.show).toEqual(true);
expect(out.courseGradeFilter.show).toEqual(true);
expect(out.assignmentGradeFilter.show).toEqual(true);
});
test('filter message', () => {
expect(out.gradeFilter.text).toEqual(
`${formatMessage(messages.minGradeInvalid)}${formatMessage(messages.maxGradeInvalid)}`,
expect(out.courseGradeFilter.text).toEqual(
`${formatMessage(messages.minGradeInvalid)} ${formatMessage(messages.maxGradeInvalid)} ${formatMessage(messages.minLessMaxGradeInvalid)}`,
);
expect(out.assignmentGradeFilter.text).toEqual(
`${formatMessage(messages.minAssignmentInvalid)} ${formatMessage(messages.maxAssignmentInvalid)} ${formatMessage(messages.minLessMaxAssignmentInvalid)}`,
);
});
});
Expand Down
14 changes: 11 additions & 3 deletions src/components/GradesView/StatusAlerts/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import useStatusAlertsData from './hooks';
export const StatusAlerts = () => {
const {
successBanner,
gradeFilter,
courseGradeFilter,
assignmentGradeFilter,
} = useStatusAlertsData();

return (
Expand All @@ -22,9 +23,16 @@ export const StatusAlerts = () => {
<Alert
variant="danger"
dismissible={false}
show={gradeFilter.show}
show={assignmentGradeFilter.show}
>
{gradeFilter.text}
{assignmentGradeFilter.text}
</Alert>
<Alert
variant="danger"
dismissible={false}
show={courseGradeFilter.show}
>
{courseGradeFilter.text}
</Alert>
</>
);
Expand Down
20 changes: 17 additions & 3 deletions src/components/GradesView/StatusAlerts/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ const hookProps = {
show: 'hooks.show-success-banner',
text: 'hooks.success-banner-text',
},
gradeFilter: {
courseGradeFilter: {
show: 'hooks.show-grade-filter',
text: 'hooks.grade-filter-text',
},
assignmentGradeFilter: {
show: 'hooks.show-grade-filter',
text: 'hooks.grade-filter-text',
},
Expand Down Expand Up @@ -46,8 +50,18 @@ describe('StatusAlerts component', () => {
test('grade filter banner', () => {
const alert = el.instance.findByType(Alert)[1];
const { props } = alert;
expect(props.show).toEqual(hookProps.gradeFilter.show);
expect(alert.children[0].el).toEqual(hookProps.gradeFilter.text);
expect(props.show)
.toEqual(hookProps.gradeFilter.show);
expect(alert.children[0].el)
.toEqual(hookProps.gradeFilter.text);
});
test('course and assignment filter banner', () => {
const alert = el.find(Alert).at(1);
const props = alert.props();
expect(props.show).toEqual(hookProps.courseGradeFilter.show);
expect(alert.text()).toEqual(hookProps.courseGradeFilter.text);
expect(props.show).toEqual(hookProps.assignmentGradeFilter.show);
expect(alert.text()).toEqual(hookProps.assignmentGradeFilter.text);
});
});
});
20 changes: 20 additions & 0 deletions src/components/GradesView/StatusAlerts/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ const messages = defineMessages({
defaultMessage: 'Minimum course grade must be between 0 and 100',
description: 'An alert text for selecting a minimum course grade less than 0',
},
minLessMaxGradeInvalid: {
id: 'gradebook.GradesView.minLessMaxGradeInvalid',
defaultMessage: 'Minimum course grade must be less than maximum course grade',
description: 'An alert text for selecting a minimum course grade must be less than maximum course grade',
},
maxAssignmentInvalid: {
id: 'gradebook.GradesView.maxAssignmentGradeInvalid',
defaultMessage: 'Maximum assignment grade must be between 0 and 100',
description: 'An alert text for selecting a maximum assignment grade greater than 100',
},
minAssignmentInvalid: {
id: 'gradebook.GradesView.minAssignmentGradeInvalid',
defaultMessage: 'Minimum assignment grade must be between 0 and 100',
description: 'An alert text for selecting a minimum assignment grade less than 0',
},
minLessMaxAssignmentInvalid: {
id: 'gradebook.GradesView.minLessMaxAssignmentInvalid',
defaultMessage: 'Minimum assignment grade must be less than maximum assignment grade',
description: 'An alert text for selecting a minimum assignment grade must be less than maximum assignment grade',
},
});

export default messages;
2 changes: 2 additions & 0 deletions src/data/redux/hooks/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ export const app = StrictDict({
useActiveView: selectorHook(selectors.app.activeView),
useAssignmentGradeLimits: selectorHook(selectors.app.assignmentGradeLimits),
useAreCourseGradeFiltersValid: selectorHook(selectors.app.areCourseGradeFiltersValid),
useAreAssignmentGradeFiltersValid: selectorHook(selectors.app.areAssignmentGradeFiltersValid),
useCourseGradeLimits: selectorHook(selectors.app.courseGradeLimits),
useCourseGradeFilterValidity: selectorHook(selectors.app.courseGradeFilterValidity),
useAssignmentGradeFilterValidity: selectorHook(selectors.app.assignmentGradeFilterValidity),
useCourseId: selectorHook(selectors.app.courseId),
useModalData: selectorHook(selectors.app.modalData),
useSearchValue: selectorHook(selectors.app.searchValue),
Expand Down
Loading
Loading