Fix page buttons (#188)

* add prev/next grades selectors

* fix PageButtons props
This commit is contained in:
Ben Warzeski
2021-06-01 12:23:33 -04:00
committed by GitHub
parent 2ee522352e
commit 40059ec41e
6 changed files with 206 additions and 146 deletions

View File

@@ -208,7 +208,7 @@ export default class Gradebook extends React.Component {
<BulkManagementControls courseId={this.props.courseId} />
</div>
<GradebookTable setGradebookState={this.safeSetState} />
{PageButtons(this.props)}
<PageButtons {...this.props} />
<p>* available for learners in the Master&apos;s track only</p>
<EditModal
assignmentName={this.state.assignmentName}

View File

@@ -1,36 +1,112 @@
import renderer from 'react-test-renderer';
import PageButtons from '.';
import React from 'react';
import { shallow } from 'enzyme';
const createInput = function createInput(prevPage, nextPage) {
return {
prevPage,
nextPage,
selectedTrack: 't',
selectedCohort: 'c',
getPrevNextGrades() {},
};
};
import selectors from 'data/selectors';
import thunkActions from 'data/thunkActions';
describe('PageButtons', () => {
const assertPageButtonsSnapshot = function assertPageButtonsSnapshot(input) {
const pb = renderer.create(PageButtons(input));
const tree = pb.toJSON();
expect(tree).toMatchSnapshot();
};
import { PageButtons, mapStateToProps, mapDispatchToProps } from '.';
it('prev null, next null', () => {
assertPageButtonsSnapshot(createInput(null, null));
jest.mock('@edx/paragon', () => ({
Button: () => 'Button',
}));
jest.mock('data/selectors', () => ({
__esModule: true,
default: {
grades: {
nextPage: jest.fn(state => ({ nextPage: state })),
prevPage: jest.fn(state => ({ prevPage: state })),
},
},
}));
jest.mock('data/thunkActions', () => ({
__esModule: true,
default: {
grades: {
fetchPrevNextGrades: jest.fn(),
},
},
}));
let props;
let el;
describe('PageButtons component', () => {
beforeEach(() => {
props = {
match: { params: { courseId: 'hogwarts-minerva-txmog101' } },
selectedAssignmentType: 'Transmogrification Exams',
selectedCohort: { name: 'Slytherin' },
selectedTrack: { name: 'Death Eater' },
getPrevNextGrades: jest.fn(),
nextPage: 'NEXT PAGE',
prevPage: 'prev PAGE',
};
});
it('prev null, next not null', () => {
assertPageButtonsSnapshot(createInput(null, 'np'));
describe('snapshots', () => {
beforeEach(() => {
el = shallow(<PageButtons {...props} />);
el.instance.fetchNextGrades = jest.fn().mockName('fetchNextGrades');
el.instance.fetchPrevGrades = jest.fn().mockName('fetchPrevGrades');
});
test('buttons enabled with both endpoints provided', () => {
expect(el.instance().render()).toMatchSnapshot();
});
test('nextPage disabled if not provided', () => {
el.setProps({ nextPage: undefined });
expect(el.instance().render()).toMatchSnapshot();
});
test('prevPage disabled if not provided', () => {
el.setProps({ prevPage: undefined });
expect(el.instance().render()).toMatchSnapshot();
});
});
it('prev not null, next null', () => {
assertPageButtonsSnapshot(createInput('pp', null));
describe('behavior', () => {
beforeEach(() => {
el = shallow(<PageButtons {...props} />);
});
describe('getPrevGrades', () => {
it('calls props.getPrevNextGrades with props.prevPage', () => {
el.instance().getPrevGrades();
expect(props.getPrevNextGrades).toHaveBeenCalledWith(
props.prevPage,
props.match.params.courseId,
props.selectedCohort,
props.selectedTrack,
props.selectedAssignmentType,
);
});
});
describe('getNextGrades', () => {
it('calls props.getPrevNextGrades with props.nextPage', () => {
el.instance().getNextGrades();
expect(props.getPrevNextGrades).toHaveBeenCalledWith(
props.nextPage,
props.match.params.courseId,
props.selectedCohort,
props.selectedTrack,
props.selectedAssignmentType,
);
});
});
});
it('prev not null, next not null', () => {
assertPageButtonsSnapshot(createInput('pp', 'np'));
describe('mapStateToProps', () => {
const testState = { l: 'eeeerroooooy', j: 'jjjjeeeeeeenkins' };
let mapped;
beforeEach(() => {
mapped = mapStateToProps(testState);
});
test('nextPage from grades.nextPage', () => {
expect(mapped.nextPage).toEqual(selectors.grades.nextPage(testState));
});
test('prevPage from grades.prevPage', () => {
expect(mapped.prevPage).toEqual(selectors.grades.prevPage(testState));
});
});
describe('mapDispatchToProps', () => {
test('getPrevNextGrades from thunkActions.grades.fetchPrevNextGrades', () => {
expect(
mapDispatchToProps.getPrevNextGrades,
).toEqual(thunkActions.grades.fetchPrevNextGrades);
});
});
});

View File

@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PageButtons prev not null, next not null 1`] = `
exports[`PageButtons component snapshots buttons enabled with both endpoints provided 1`] = `
<div
className="d-flex justify-content-center"
style={
@@ -9,8 +9,7 @@ exports[`PageButtons prev not null, next not null 1`] = `
}
}
>
<button
className="btn btn-outline-primary"
<Button
disabled={false}
onClick={[Function]}
style={
@@ -18,12 +17,11 @@ exports[`PageButtons prev not null, next not null 1`] = `
"margin": "20px",
}
}
type="button"
variant="outline-primary"
>
Previous Page
</button>
<button
className="btn btn-outline-primary"
</Button>
<Button
disabled={false}
onClick={[Function]}
style={
@@ -31,14 +29,14 @@ exports[`PageButtons prev not null, next not null 1`] = `
"margin": "20px",
}
}
type="button"
variant="outline-primary"
>
Next Page
</button>
</Button>
</div>
`;
exports[`PageButtons prev not null, next null 1`] = `
exports[`PageButtons component snapshots nextPage disabled if not provided 1`] = `
<div
className="d-flex justify-content-center"
style={
@@ -47,8 +45,7 @@ exports[`PageButtons prev not null, next null 1`] = `
}
}
>
<button
className="btn btn-outline-primary"
<Button
disabled={false}
onClick={[Function]}
style={
@@ -56,12 +53,11 @@ exports[`PageButtons prev not null, next null 1`] = `
"margin": "20px",
}
}
type="button"
variant="outline-primary"
>
Previous Page
</button>
<button
className="btn btn-outline-primary"
</Button>
<Button
disabled={true}
onClick={[Function]}
style={
@@ -69,14 +65,14 @@ exports[`PageButtons prev not null, next null 1`] = `
"margin": "20px",
}
}
type="button"
variant="outline-primary"
>
Next Page
</button>
</Button>
</div>
`;
exports[`PageButtons prev null, next not null 1`] = `
exports[`PageButtons component snapshots prevPage disabled if not provided 1`] = `
<div
className="d-flex justify-content-center"
style={
@@ -85,8 +81,7 @@ exports[`PageButtons prev null, next not null 1`] = `
}
}
>
<button
className="btn btn-outline-primary"
<Button
disabled={true}
onClick={[Function]}
style={
@@ -94,12 +89,11 @@ exports[`PageButtons prev null, next not null 1`] = `
"margin": "20px",
}
}
type="button"
variant="outline-primary"
>
Previous Page
</button>
<button
className="btn btn-outline-primary"
</Button>
<Button
disabled={false}
onClick={[Function]}
style={
@@ -107,47 +101,9 @@ exports[`PageButtons prev null, next not null 1`] = `
"margin": "20px",
}
}
type="button"
variant="outline-primary"
>
Next Page
</button>
</div>
`;
exports[`PageButtons prev null, next null 1`] = `
<div
className="d-flex justify-content-center"
style={
Object {
"paddingBottom": "20px",
}
}
>
<button
className="btn btn-outline-primary"
disabled={true}
onClick={[Function]}
style={
Object {
"margin": "20px",
}
}
type="button"
>
Previous Page
</button>
<button
className="btn btn-outline-primary"
disabled={true}
onClick={[Function]}
style={
Object {
"margin": "20px",
}
}
type="button"
>
Next Page
</button>
</Button>
</div>
`;

View File

@@ -1,75 +1,99 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Button } from '@edx/paragon';
export default function PageButtons({
prevPage, nextPage, selectedTrack, selectedCohort, selectedAssignmentType,
getPrevNextGrades, match,
}) {
return (
<div
className="d-flex justify-content-center"
style={{ paddingBottom: '20px' }}
>
<Button
style={{ margin: '20px' }}
variant="outline-primary"
disabled={!prevPage}
onClick={() => getPrevNextGrades(
prevPage,
match.params.courseId,
selectedCohort,
selectedTrack,
selectedAssignmentType,
)}
import selectors from 'data/selectors';
import thunkActions from 'data/thunkActions';
export class PageButtons extends React.Component {
constructor(props) {
super(props);
this.getPrevGrades = this.getPrevGrades.bind(this);
this.getNextGrades = this.getNextGrades.bind(this);
}
getPrevGrades() {
this.props.getPrevNextGrades(
this.props.prevPage,
this.props.match.params.courseId,
this.props.selectedCohort,
this.props.selectedTrack,
this.props.selectedAssignmentType,
);
}
getNextGrades() {
this.props.getPrevNextGrades(
this.props.nextPage,
this.props.match.params.courseId,
this.props.selectedCohort,
this.props.selectedTrack,
this.props.selectedAssignmentType,
);
}
render() {
return (
<div
className="d-flex justify-content-center"
style={{ paddingBottom: '20px' }}
>
Previous Page
</Button>
<Button
style={{ margin: '20px' }}
variant="outline-primary"
disabled={!nextPage}
onClick={() => getPrevNextGrades(
nextPage,
match.params.courseId,
selectedCohort,
selectedTrack,
selectedAssignmentType,
)}
>
Next Page
</Button>
</div>
);
<Button
style={{ margin: '20px' }}
variant="outline-primary"
disabled={!this.props.prevPage}
onClick={this.getPrevGrades}
>
Previous Page
</Button>
<Button
style={{ margin: '20px' }}
variant="outline-primary"
disabled={!this.props.nextPage}
onClick={this.getNextGrades}
>
Next Page
</Button>
</div>
);
}
}
PageButtons.defaultProps = {
match: {
params: {
courseId: '',
},
params: { courseId: '' },
},
nextPage: '',
prevPage: '',
selectedAssignmentType: null,
selectedCohort: null,
selectedTrack: null,
selectedAssignmentType: null,
nextPage: '',
prevPage: '',
};
PageButtons.propTypes = {
getPrevNextGrades: PropTypes.func.isRequired,
match: PropTypes.shape({
params: PropTypes.shape({
courseId: PropTypes.string,
}),
}),
selectedAssignmentType: PropTypes.string,
selectedCohort: PropTypes.shape({ name: PropTypes.string }),
selectedTrack: PropTypes.shape({ name: PropTypes.string }),
// redux
getPrevNextGrades: PropTypes.func.isRequired,
nextPage: PropTypes.string,
prevPage: PropTypes.string,
selectedAssignmentType: PropTypes.string,
selectedCohort: PropTypes.shape({
name: PropTypes.string,
}),
selectedTrack: PropTypes.shape({
name: PropTypes.string,
}),
};
export const mapStateToProps = (state) => ({
nextPage: selectors.grades.nextPage(state),
prevPage: selectors.grades.prevPage(state),
});
export const mapDispatchToProps = {
getPrevNextGrades: thunkActions.grades.fetchPrevNextGrades,
};
export default connect(mapStateToProps, mapDispatchToProps)(PageButtons);

View File

@@ -205,6 +205,8 @@ const simpleSelectors = simpleSelectorFactory(
'filteredUsersCount',
'totalUsersCount',
'gradeFormat',
'nextPage',
'prevPage',
'showSpinner',
'gradeOverrideCurrentEarnedGradedOverride',
'gradeOverrideHistoryError',

View File

@@ -287,6 +287,8 @@ describe('grades selectors', () => {
testSimpleSelector('filteredUsersCount');
testSimpleSelector('totalUsersCount');
testSimpleSelector('gradeFormat');
testSimpleSelector('nextPage');
testSimpleSelector('prevPage');
testSimpleSelector('showSpinner');
testSimpleSelector('gradeOverrideCurrentEarnedGradedOverride');
testSimpleSelector('gradeOverrideHistoryError');