-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from AdamZarger/narrative_filter
Narrative Filter
- Loading branch information
Showing
24 changed files
with
767 additions
and
163 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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
This file was deleted.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import AggregationBranch from './AggregationBranch' | ||
import CollapsibleFilter from './CollapsibleFilter' | ||
import { connect } from 'react-redux' | ||
import MoreOrLess from './MoreOrLess' | ||
import React from 'react' | ||
import { SLUG_SEPARATOR } from '../constants' | ||
import { sortSelThenCount } from './utils' | ||
|
||
export class Product extends React.Component { | ||
constructor( props ) { | ||
super( props ) | ||
|
||
this._onBucket = this._onBucket.bind( this ) | ||
} | ||
|
||
render() { | ||
const listComponentProps = { | ||
fieldName: 'product' | ||
} | ||
|
||
return ( | ||
<CollapsibleFilter title="Product / sub-product" | ||
desc="The type of product and sub-product the consumer identified in the complaint" | ||
showChildren={this.props.showChildren} | ||
className="aggregation"> | ||
<a href="http://files.consumerfinance.gov/f/documents/201704_cfpb_Summary_of_Product_and_Sub-product_Changes.pdf" target="_blank">Recent changes to products and sub-products</a> | ||
<MoreOrLess listComponent={AggregationBranch} | ||
listComponentProps={listComponentProps} | ||
options={this.props.options} | ||
perBucketProps={this._onBucket} /> | ||
</CollapsibleFilter> | ||
) | ||
} | ||
|
||
// -------------------------------------------------------------------------- | ||
// MoreOrLess Helpers | ||
|
||
_onBucket( bucket, props ) { | ||
props.subitems = bucket['sub_product.raw'].buckets | ||
return props | ||
} | ||
} | ||
|
||
export const mapStateToProps = state => { | ||
// See if there are an active product filters | ||
const allProducts = state.query.product || [] | ||
const selections = [] | ||
|
||
// Reduce the products to the parent keys (and dedup) | ||
allProducts.forEach( x => { | ||
const idx = x.indexOf( SLUG_SEPARATOR ) | ||
const key = idx === -1 ? x : x.substr( 0, idx ) | ||
if ( selections.indexOf( key ) === -1 ) { | ||
selections.push( key ) | ||
} | ||
} ) | ||
|
||
// Make a cloned, sorted version of the aggs | ||
const options = sortSelThenCount( state.aggs.product, selections ) | ||
|
||
return { | ||
options | ||
} | ||
} | ||
|
||
export default connect( mapStateToProps )( Product ) |
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 |
---|---|---|
@@ -1,15 +1,78 @@ | ||
import React from 'react'; | ||
import { changeFlagFilter } from '../actions/filter' | ||
import { connect } from 'react-redux' | ||
import PropTypes from 'prop-types' | ||
import React from 'react' | ||
|
||
export class SingleCheckbox extends React.Component { | ||
constructor( props ) { | ||
super( props ) | ||
this.state = { isChecked: this.props.isChecked } | ||
} | ||
|
||
componentWillReceiveProps( nextProps ) { | ||
const newState = { | ||
isChecked: nextProps.isChecked | ||
} | ||
this.setState( newState ) | ||
} | ||
|
||
componentDidUpdate() { | ||
this.props.changeFlagFilter( this.props.fieldName, this.state.isChecked ) | ||
} | ||
|
||
export default class SingleCheckbox extends React.Component { | ||
render() { | ||
return ( | ||
<section className="single-checkbox"> | ||
<h5>{this.props.title}</h5> | ||
<div className="m-form-field m-form-field__checkbox"> | ||
<input className="a-checkbox" type="checkbox" id="theCheckbox" /> | ||
<label className="a-label" htmlFor="theCheckbox">{this.props.label}</label> | ||
<input className="a-checkbox" | ||
id="theCheckbox" | ||
type="checkbox" | ||
onClick={ this._changeFlag.bind( this ) } | ||
checked={ this.state.isChecked } | ||
value={ this.props.fieldName } /> | ||
<label className="a-label" htmlFor="theCheckbox">Yes</label> | ||
</div> | ||
</section> | ||
); | ||
) | ||
} | ||
|
||
// -------------------------------------------------------------------------- | ||
// Helper Methods | ||
|
||
_changeFlag( ) { | ||
const newState = { | ||
isChecked: !this.state.isChecked | ||
} | ||
this.setState( newState ) | ||
} | ||
} | ||
|
||
// ---------------------------------------------------------------------------- | ||
// Meta | ||
|
||
SingleCheckbox.propTypes = { | ||
fieldName: PropTypes.string.isRequired, | ||
isChecked: PropTypes.bool | ||
} | ||
|
||
SingleCheckbox.defaultProps = { | ||
isChecked: false | ||
} | ||
|
||
export const mapStateToProps = state => { | ||
var queryValue = state.query.has_narrative | ||
return { | ||
isChecked: typeof queryValue !== 'undefined' && | ||
( queryValue.toString() === 'yes' || | ||
queryValue.toString() === 'true' ) | ||
} | ||
} | ||
|
||
export const mapDispatchToProps = dispatch => ( { | ||
changeFlagFilter: ( fieldName, isChecked ) => { | ||
dispatch( changeFlagFilter( fieldName, isChecked ) ) | ||
} | ||
} ) | ||
|
||
export default connect( mapStateToProps, mapDispatchToProps )( SingleCheckbox ) |
This file was deleted.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import React from 'react' | ||
import configureMockStore from 'redux-mock-store' | ||
import thunk from 'redux-thunk' | ||
import renderer from 'react-test-renderer'; | ||
import { IntlProvider } from 'react-intl'; | ||
import { Provider } from 'react-redux' | ||
import { shallow } from 'enzyme'; | ||
import ReduxProduct, {Product, mapStateToProps} from '../Product' | ||
import { slugify } from '../utils' | ||
|
||
const fixture = [ | ||
{ | ||
"sub_product.raw": { | ||
"buckets": [ | ||
{"key": "Credit reporting","doc_count": 3200}, | ||
{"key": "Other personal consumer report","doc_count": 67}, | ||
{"key": "Credit repair services","doc_count": 10} | ||
], | ||
}, | ||
"key": "Credit reporting, credit repair services, or other personal consumer reports", | ||
"doc_count": 3277 | ||
}, | ||
{ | ||
"sub_product.raw": { | ||
"buckets": [ | ||
{"key": "Conventional home mortgage","doc_count": 652}, | ||
{"key": "Conventional fixed mortgage","doc_count": 612} | ||
], | ||
}, | ||
"key": "Mortgage", | ||
"doc_count": 2299 | ||
}, | ||
{ | ||
"sub_product.raw": { | ||
"buckets": [], | ||
}, | ||
"key": "Credit reporting", | ||
"doc_count": 1052 | ||
}, | ||
{ | ||
"sub_product.raw": { | ||
"buckets": [], | ||
}, | ||
"key": "Student loan", | ||
"doc_count": 959 | ||
}, | ||
{ | ||
"sub_product.raw": { | ||
"buckets": [], | ||
}, | ||
"key": "Credit card or prepaid card", | ||
"doc_count": 836 | ||
}, | ||
{ | ||
"sub_product.raw": { | ||
"buckets": [], | ||
}, | ||
"key": "Credit card", | ||
"doc_count": 652 | ||
} | ||
] | ||
|
||
function setupEnzyme(initial) { | ||
const props = { | ||
options: initial | ||
} | ||
|
||
const target = shallow(<Product {...props} />); | ||
|
||
return { | ||
props, | ||
target | ||
} | ||
} | ||
|
||
function setupSnapshot(initial) { | ||
const middlewares = [thunk] | ||
const mockStore = configureMockStore(middlewares) | ||
const store = mockStore({ | ||
query: {}, | ||
aggs: { | ||
product: initial | ||
} | ||
}) | ||
|
||
return renderer.create( | ||
<Provider store={store}> | ||
<IntlProvider locale="en"> | ||
<ReduxProduct /> | ||
</IntlProvider> | ||
</Provider> | ||
) | ||
} | ||
|
||
describe('component:Product', () => { | ||
describe('snapshots', () => { | ||
it('renders without crashing', () => { | ||
const target = setupSnapshot([]) | ||
let tree = target.toJSON() | ||
expect(tree).toMatchSnapshot() | ||
}) | ||
|
||
it('only shows the first five items', () => { | ||
const target = setupSnapshot(fixture) | ||
let tree = target.toJSON() | ||
expect(tree).toMatchSnapshot() | ||
}) | ||
}) | ||
|
||
describe('sorting', () => { | ||
it('places selections ahead of unselected', () => { | ||
const selected = [ | ||
'Credit reporting, credit repair services, or other personal consumer reports', | ||
slugify('Credit reporting, credit repair services, or other personal consumer reports', | ||
'Other personal consumer report'), | ||
'Credit card' | ||
] | ||
const actual = mapStateToProps({ | ||
query: {product: selected}, | ||
aggs: {product: fixture} | ||
}) | ||
expect(actual.options[1]).toEqual(fixture[5]) | ||
}) | ||
|
||
it('treats child selections as parent selections', () => { | ||
const selected = [ | ||
slugify("Mortgage", 'Conventional home mortgage') | ||
] | ||
const actual = mapStateToProps({ | ||
query: {product: selected}, | ||
aggs: {product: fixture} | ||
}) | ||
expect(actual.options[0]).toEqual(fixture[1]) | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.