-
Notifications
You must be signed in to change notification settings - Fork 94
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split SearchControls into separate component (#174)
* Split SearchControls into separate component * Add testing config * Add webpack config * Add snapshot tests for Search Controls * bump version and update package-lock format
- Loading branch information
Showing
10 changed files
with
43,263 additions
and
2,467 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
const { createConfig } = require('@edx/frontend-build'); | ||
|
||
module.exports = createConfig('jest', { | ||
setupFilesAfterEnv: [ | ||
'<rootDir>/src/setupTest.js', | ||
], | ||
modulePaths: ['<rootDir>/src/'], | ||
snapshotSerializers: [ | ||
'enzyme-to-json/serializer', | ||
], | ||
}); |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { connect } from 'react-redux'; | ||
|
||
import { Button, Icon, SearchField } from '@edx/paragon'; | ||
|
||
import { | ||
fetchGrades, | ||
fetchMatchingUserGrades, | ||
} from '../../data/actions/grades'; | ||
|
||
/** | ||
* Controls for filtering the GradebookTable. Contains the "Edit Filters" button for opening the filter drawer | ||
* as well as the search box for searching by username/email. | ||
*/ | ||
export class SearchControls extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.onSubmit = this.onSubmit.bind(this); | ||
this.onChange = this.onChange.bind(this); | ||
this.onClear = this.onClear.bind(this); | ||
} | ||
|
||
/** Submitting searches for user matching the username/email in `value` */ | ||
onSubmit(value) { | ||
this.props.searchForUser( | ||
this.props.courseId, | ||
value, | ||
this.props.selectedCohort, | ||
this.props.selectedTrack, | ||
this.props.selectedAssignmentType, | ||
); | ||
} | ||
|
||
/** Changing the search value stores the key in Gradebook. Currently unused */ | ||
onChange(filterValue) { | ||
this.props.setFilterValue(filterValue); | ||
} | ||
|
||
/** Clearing the search box falls back to showing students with already applied filters */ | ||
onClear() { | ||
this.props.getUserGrades( | ||
this.props.courseId, | ||
this.props.selectedCohort, | ||
this.props.selectedTrack, | ||
this.props.selectedAssignmentType, | ||
); | ||
} | ||
|
||
render() { | ||
return ( | ||
<> | ||
<h4>Step 1: Filter the Grade Report</h4> | ||
<div className="d-flex justify-content-between"> | ||
<Button | ||
id="edit-filters-btn" | ||
className="btn-primary align-self-start" | ||
onClick={this.props.toggleFilterDrawer} | ||
> | ||
<Icon className="fa fa-filter" /> Edit Filters | ||
</Button> | ||
<div> | ||
<SearchField | ||
onSubmit={this.onSubmit} | ||
inputLabel="Search for a learner" | ||
onChange={this.onChange} | ||
onClear={this.onClear} | ||
value={this.props.filterValue} | ||
/> | ||
<small className="form-text text-muted search-help-text">Search by username, email, or student key</small> | ||
</div> | ||
</div> | ||
</> | ||
); | ||
} | ||
} | ||
|
||
SearchControls.defaultProps = { | ||
courseId: '', | ||
filterValue: '', | ||
selectedAssignmentType: '', | ||
selectedCohort: null, | ||
selectedTrack: null, | ||
}; | ||
|
||
SearchControls.propTypes = { | ||
courseId: PropTypes.string, | ||
filterValue: PropTypes.string, | ||
setFilterValue: PropTypes.func.isRequired, | ||
toggleFilterDrawer: PropTypes.func.isRequired, | ||
// From Redux | ||
getUserGrades: PropTypes.func.isRequired, | ||
searchForUser: PropTypes.func.isRequired, | ||
selectedAssignmentType: PropTypes.string, | ||
selectedCohort: PropTypes.string, | ||
selectedTrack: PropTypes.string, | ||
}; | ||
|
||
export const mapStateToProps = (state) => ({ | ||
selectedAssignmentType: state.filters.assignmentType, | ||
selectedTrack: state.filters.track, | ||
selectedCohort: state.filters.cohort, | ||
}); | ||
|
||
export const mapDispatchToProps = { | ||
getUserGrades: fetchGrades, | ||
searchForUser: fetchMatchingUserGrades, | ||
}; | ||
|
||
export default connect(mapStateToProps, mapDispatchToProps)(SearchControls); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import React from 'react'; | ||
import { shallow } from 'enzyme'; | ||
|
||
import { | ||
fetchGrades, | ||
fetchMatchingUserGrades, | ||
} from '../../data/actions/grades'; | ||
import { mapDispatchToProps, mapStateToProps, SearchControls } from './SearchControls'; | ||
|
||
jest.mock('@edx/paragon', () => ({ | ||
Icon: 'Icon', | ||
Button: 'Button', | ||
SearchField: 'SearchField', | ||
})); | ||
|
||
describe('SearchControls', () => { | ||
let props; | ||
|
||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
props = { | ||
courseId: 'course-v1:edX+DEV101+T1', | ||
filterValue: 'alice', | ||
selectedAssignmentType: 'homework', | ||
selectedCohort: 'spring term', | ||
selectedTrack: 'masters', | ||
getUserGrades: jest.fn(), | ||
searchForUser: jest.fn(), | ||
setFilterValue: jest.fn(), | ||
toggleFilterDrawer: jest.fn().mockName('toggleFilterDrawer'), | ||
}; | ||
}); | ||
|
||
const searchControls = (overriddenProps) => { | ||
props = { ...props, ...overriddenProps }; | ||
return shallow(<SearchControls {...props} />); | ||
}; | ||
|
||
describe('Component', () => { | ||
describe('onSubmit', () => { | ||
it('calls props.searchForUser with correct data', () => { | ||
const wrapper = searchControls(); | ||
wrapper.instance().onSubmit('bob'); | ||
|
||
expect(props.searchForUser).toHaveBeenCalledWith( | ||
props.courseId, | ||
'bob', | ||
props.selectedCohort, | ||
props.selectedTrack, | ||
props.selectedAssignmentType, | ||
); | ||
}); | ||
}); | ||
|
||
describe('onChange', () => { | ||
it('saves the changed search value to Gradebook state', () => { | ||
const wrapper = searchControls(); | ||
wrapper.instance().onChange('bob'); | ||
expect(props.setFilterValue).toHaveBeenCalledWith('bob'); | ||
}); | ||
}); | ||
|
||
describe('onClear', () => { | ||
it('re-runs search with existing filters', () => { | ||
const wrapper = searchControls(); | ||
wrapper.instance().onClear(); | ||
expect(props.getUserGrades).toHaveBeenCalledWith( | ||
props.courseId, | ||
props.selectedCohort, | ||
props.selectedTrack, | ||
props.selectedAssignmentType, | ||
); | ||
}); | ||
}); | ||
|
||
describe('mapStateToProps', () => { | ||
const state = { | ||
filters: { | ||
assignmentType: 'labs', | ||
track: 'honor', | ||
cohort: 'fall term', | ||
}, | ||
}; | ||
|
||
it('maps assignment type filter correctly', () => { | ||
expect(mapStateToProps(state).selectedAssignmentType).toEqual(state.filters.assignmentType); | ||
}); | ||
|
||
it('maps track filter correctly', () => { | ||
expect(mapStateToProps(state).selectedTrack).toEqual(state.filters.track); | ||
}); | ||
|
||
it('maps cohort filter correctly', () => { | ||
expect(mapStateToProps(state).selectedCohort).toEqual(state.filters.cohort); | ||
}); | ||
}); | ||
|
||
describe('mapDispatchToProps', () => { | ||
test('getUserGrades', () => { | ||
expect(mapDispatchToProps.getUserGrades).toEqual(fetchGrades); | ||
}); | ||
|
||
test('searchForUser', () => { | ||
expect(mapDispatchToProps.searchForUser).toEqual(fetchMatchingUserGrades); | ||
}); | ||
}); | ||
|
||
describe('Snapshots', () => { | ||
test('basic snapshot', () => { | ||
const wrapper = searchControls(); | ||
wrapper.instance().onChange = jest.fn().mockName('onChange'); | ||
wrapper.instance().onClear = jest.fn().mockName('onClear'); | ||
wrapper.instance().onSubmit = jest.fn().mockName('onSubmit'); | ||
expect(wrapper.instance().render()).toMatchSnapshot(); | ||
}); | ||
}); | ||
}); | ||
}); |
37 changes: 37 additions & 0 deletions
37
src/components/Gradebook/__snapshots__/SearchControls.test.jsx.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`SearchControls Component Snapshots basic snapshot 1`] = ` | ||
<React.Fragment> | ||
<h4> | ||
Step 1: Filter the Grade Report | ||
</h4> | ||
<div | ||
className="d-flex justify-content-between" | ||
> | ||
<Button | ||
className="btn-primary align-self-start" | ||
id="edit-filters-btn" | ||
onClick={[MockFunction toggleFilterDrawer]} | ||
> | ||
<Icon | ||
className="fa fa-filter" | ||
/> | ||
Edit Filters | ||
</Button> | ||
<div> | ||
<SearchField | ||
inputLabel="Search for a learner" | ||
onChange={[MockFunction onChange]} | ||
onClear={[MockFunction onClear]} | ||
onSubmit={[MockFunction onSubmit]} | ||
value="alice" | ||
/> | ||
<small | ||
className="form-text text-muted search-help-text" | ||
> | ||
Search by username, email, or student key | ||
</small> | ||
</div> | ||
</div> | ||
</React.Fragment> | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.