diff --git a/src/DateIntervals.jsx b/src/DateIntervals.jsx index 418845c46..a34645509 100644 --- a/src/DateIntervals.jsx +++ b/src/DateIntervals.jsx @@ -11,14 +11,17 @@ export class DateIntervals extends React.Component { } _btnClassName( dateInterval ) { - const btnClass = 'date-selector '; - return dateInterval === this.props.dateInterval ? - btnClass + 'selected' : btnClass; + const classes = [ 'date-selector', 'interval-' + dateInterval ] + if ( dateInterval === this.props.dateInterval ) { + classes.push( 'selected' ) + } + return classes.join( ' ' ) } render() { return (
+

Date range (Click to modify range)

{ dateIntervals.map( dateInterval => - - - - + + + + +
+
- All - -
+

+ Map Shading +

+ + +
+

+ Date range (Click to modify range) +

-
- - - - - + + + + +
+
- All - -
+

+ Map Shading +

+ + +
+

+ Map Shading +

+ + +`; diff --git a/src/__tests__/__snapshots__/ResultsPanel.spec.jsx.snap b/src/__tests__/__snapshots__/ResultsPanel.spec.jsx.snap index 30015549c..a352f837f 100644 --- a/src/__tests__/__snapshots__/ResultsPanel.spec.jsx.snap +++ b/src/__tests__/__snapshots__/ResultsPanel.spec.jsx.snap @@ -405,40 +405,68 @@ exports[`component:Results renders map panel without crashing 1`] = `
-
- - - - - + + + + +
+
- All - -
+

+ Map Shading +

+ + +
{ describe('addStateFilter', () => { @@ -15,6 +16,19 @@ describe('action:map', () => { }) }) + describe('dataNormalizationChanged', () => { + it('creates a simple action', () => { + const expectedAction = { + type: sut.DATA_NORMALIZATION_SELECTED, + requery: REQUERY_NEVER, + value: 'foo' + } + const action = sut.dataNormalizationChanged( 'foo' ) + expect(action).toEqual(expectedAction) + }) + }) + + describe('removeStateFilter', () => { it('creates a simple action', () => { const expectedAction = { diff --git a/src/actions/map.jsx b/src/actions/map.jsx index 7ae876d49..489a81316 100644 --- a/src/actions/map.jsx +++ b/src/actions/map.jsx @@ -1,9 +1,24 @@ -import { REQUERY_ALWAYS } from '../constants' +import { REQUERY_ALWAYS, REQUERY_NEVER } from '../constants' +export const DATA_NORMALIZATION_SELECTED = 'DATA_NORMALIZATION_SELECTED' export const STATE_COMPLAINTS_SHOWN = 'STATE_COMPLAINTS_SHOWN' export const STATE_FILTER_ADDED = 'STATE_FILTER_ADDED' export const STATE_FILTER_REMOVED = 'STATE_FILTER_REMOVED' + +/** + * Indicates data normalization (yes, per capita) value was selected + * @param {object} value of the data normalization + * @returns {object} a packaged payload to be used by Redux reducers + */ +export function dataNormalizationChanged( value ) { + return { + type: DATA_NORMALIZATION_SELECTED, + requery: REQUERY_NEVER, + value + }; +} + /** * Creates an action in response after state tile clicked * diff --git a/src/constants/index.jsx b/src/constants/index.jsx index 385ef2d87..750d019bd 100644 --- a/src/constants/index.jsx +++ b/src/constants/index.jsx @@ -14,6 +14,10 @@ export const MODE_MAP = 'Map' export const MODE_LIST = 'List' export const MODE_TRENDS = 'Trends' +// map normalization modes +export const GEO_NORM_NONE = 'None' +export const GEO_NORM_PER1000 = 'Per 1000 pop.' + // Useful constants export const NARRATIVE_SEARCH_FIELD = 'complaint_what_happened' @@ -183,3 +187,323 @@ export const TILE_MAP_STATES = [ 'WV', 'WY' ] + +// 2017 ACS census via the census API: +// https://api.census.gov/data/2017/acs/acs5?get=B01003_001E,NAME&for=state +// +// B01003_001E is defined as TOTAL POPULATION +// (detailed here: https://api.census.gov/data/2016/acs/acs5/variables.html) +export const STATE_DATA = [ + { + abbr: 'AL', + name: 'Alabama', + population: '4850771', + id: '01' + }, + { + abbr: 'AK', + name: 'Alaska', + population: '738565', + id: '02' + }, + { + abbr: 'AZ', + name: 'Arizona', + population: '6809946', + id: '04' + }, + { + abbr: 'AR', + name: 'Arkansas', + population: '2977944', + id: '05' + }, + { + abbr: 'CA', + name: 'California', + population: '38982847', + id: '06' + }, + { + abbr: 'CO', + name: 'Colorado', + population: '5436519', + id: '08' + }, + { + abbr: 'CT', + name: 'Connecticut', + population: '3594478', + id: '09' + }, + { + abbr: 'DE', + name: 'Delaware', + population: '943732', + id: '10' + }, + { + abbr: 'DC', + name: 'District of Columbia', + population: '672391', + id: '11' + }, + { + abbr: 'FL', + name: 'Florida', + population: '20278447', + id: '12' + }, + { + abbr: 'GA', + name: 'Georgia', + population: '10201635', + id: '13' + }, + { + abbr: 'HI', + name: 'Hawaii', + population: '1421658', + id: '15' + }, + { + abbr: 'ID', + name: 'Idaho', + population: '1657375', + id: '16' + }, + { + abbr: 'IL', + name: 'Illinois', + population: '12854526', + id: '17' + }, + { + abbr: 'IN', + name: 'Indiana', + population: '6614418', + id: '18' + }, + { + abbr: 'IA', + name: 'Iowa', + population: '3118102', + id: '19' + }, + { + abbr: 'KS', + name: 'Kansas', + population: '2903820', + id: '20' + }, + { + abbr: 'KY', + name: 'Kentucky', + population: '4424376', + id: '21' + }, + { + abbr: 'LA', + name: 'Louisiana', + population: '4663461', + id: '22' + }, + { + abbr: 'ME', + name: 'Maine', + population: '1330158', + id: '23' + }, + { + abbr: 'MD', + name: 'Maryland', + population: '5996079', + id: '24' + }, + { + abbr: 'MA', + name: 'Massachusetts', + population: '6789319', + id: '25' + }, + { + abbr: 'MI', + name: 'Michigan', + population: '9925568', + id: '26' + }, + { + abbr: 'MN', + name: 'Minnesota', + population: '5490726', + id: '27' + }, + { + abbr: 'MS', + name: 'Mississippi', + population: '2986220', + id: '28' + }, + { + abbr: 'MO', + name: 'Missouri', + population: '6075300', + id: '29' + }, + { + abbr: 'MT', + name: 'Montana', + population: '1029862', + id: '30' + }, + { + abbr: 'NE', + name: 'Nebraska', + population: '1893921', + id: '31' + }, + { + abbr: 'NV', + name: 'Nevada', + population: '2887725', + id: '32' + }, + { + abbr: 'NH', + name: 'New Hampshire', + population: '1331848', + id: '33' + }, + { + abbr: 'NJ', + name: 'New Jersey', + population: '8960161', + id: '34' + }, + { + abbr: 'NM', + name: 'New Mexico', + population: '2084828', + id: '35' + }, + { + abbr: 'NY', + name: 'New York', + population: '19798228', + id: '36' + }, + { + abbr: 'NC', + name: 'North Carolina', + population: '10052564', + id: '37' + }, + { + abbr: 'ND', + name: 'North Dakota', + population: '745475', + id: '38' + }, + { + abbr: 'OH', + name: 'Ohio', + population: '11609756', + id: '39' + }, + { + abbr: 'OK', + name: 'Oklahoma', + population: '3896251', + id: '40' + }, + { + abbr: 'OR', + name: 'Oregon', + population: '4025127', + id: '41' + }, + { + abbr: 'PA', + name: 'Pennsylvania', + population: '12790505', + id: '42' + }, + { + abbr: 'PR', + name: 'Puerto Rico', + population: '3468963', + id: '72' + }, + { + abbr: 'RI', + name: 'Rhode Island', + population: '1056138', + id: '44' + }, + { + abbr: 'SC', + name: 'South Carolina', + population: '4893444', + id: '45' + }, + { + abbr: 'SD', + name: 'South Dakota', + population: '855444', + id: '46' + }, + { + abbr: 'TN', + name: 'Tennessee', + population: '6597381', + id: '47' + }, + { + abbr: 'TX', + name: 'Texas', + population: '27419612', + id: '48' + }, + { + abbr: 'UT', + name: 'Utah', + population: '2993941', + id: '49' + }, + { + abbr: 'VT', + name: 'Vermont', + population: '624636', + id: '50' + }, + { + abbr: 'VA', + name: 'Virginia', + population: '8365952', + id: '51' + }, + { + abbr: 'WA', + name: 'Washington', + population: '7169967', + id: '53' + }, + { + abbr: 'WV', + name: 'West Virginia', + population: '1836843', + id: '54' + }, + { + abbr: 'WI', + name: 'Wisconsin', + population: '5763217', + id: '55' + }, + { + abbr: 'WY', + name: 'Wyoming', + population: '583200', + id: '56' + } +] diff --git a/src/reducers/__tests__/map.spec.jsx b/src/reducers/__tests__/map.spec.jsx index 3bbcbb689..1b6cd68b6 100644 --- a/src/reducers/__tests__/map.spec.jsx +++ b/src/reducers/__tests__/map.spec.jsx @@ -1,8 +1,11 @@ -import target, { processAggregations, processStateAggregations } from '../map' -import * as complaintActions from '../../actions/complaints' -import * as mapActions from '../../actions/map' +import target, { + defaultState, + processAggregations, + processStateAggregations +} from '../map' +import actions from '../../actions' import stateAggs from '../__fixtures__/stateAggs' -import { TILE_MAP_STATES } from '../../constants' +import { GEO_NORM_NONE, TILE_MAP_STATES } from '../../constants' describe( 'reducer:map', () => { let action @@ -10,6 +13,7 @@ describe( 'reducer:map', () => { describe( 'reducer', () => { it( 'has a default state', () => { expect( target( undefined, {} ) ).toEqual( { + dataNormalization: GEO_NORM_NONE, isLoading: false, issue: [], product: [], @@ -19,9 +23,19 @@ describe( 'reducer:map', () => { } ) } ) + describe('handles DATA_NORMALIZATION_SELECTED', ()=>{ + action = { + type: actions.DATA_NORMALIZATION_SELECTED, + value: 'FooBar' + } + expect( target( {}, action ) ).toEqual( { + dataNormalization: 'FooBar' + } ) + }) + describe( 'handles STATES_API_CALLED actions', () => { action = { - type: complaintActions.STATES_API_CALLED, + type: actions.STATES_API_CALLED, url: 'http://www.example.org' } expect( target( {}, action ) ).toEqual( { @@ -33,7 +47,7 @@ describe( 'reducer:map', () => { describe( 'STATES_RECEIVED actions', () => { beforeEach( () => { action = { - type: complaintActions.STATES_RECEIVED, + type: actions.STATES_RECEIVED, data: { aggregations: stateAggs } @@ -195,7 +209,7 @@ describe( 'reducer:map', () => { describe( 'STATES_FAILED actions', () => { it( 'handles failed error messages', () => { action = { - type: complaintActions.STATES_FAILED, + type: actions.STATES_FAILED, error: 'foo bar' } expect( target( { @@ -222,7 +236,7 @@ describe( 'reducer:map', () => { describe( 'STATE_FILTER_ADDED actions', () => { it( 'adds filter', () => { action = { - type: mapActions.STATE_FILTER_ADDED, + type: actions.STATE_FILTER_ADDED, selectedState: { abbr: 'FO', stateName: 'Foo Bar' } } expect( target( { selectedState: false }, action ) ).toEqual( { @@ -234,7 +248,7 @@ describe( 'reducer:map', () => { describe( 'STATE_FILTER_REMOVED actions', () => { it( 'adds filter', () => { action = { - type: mapActions.STATE_FILTER_REMOVED, + type: actions.STATE_FILTER_REMOVED, stateAbbr: 'FO' } expect( target( { selectedState: false }, action ) ).toEqual( { @@ -243,6 +257,29 @@ describe( 'reducer:map', () => { } ) } ) + describe( 'URL_CHANGED actions', () => { + let action + let state + beforeEach( () => { + action = { + type: actions.URL_CHANGED, + params: {} + } + + state = { ...defaultState } + } ) + + it( 'handles empty params', () => { + expect( target( state, action ) ).toEqual( state ) + } ) + + it( 'handles dataNormalization params', () => { + action.params = { dataNormalization: 'hello' } + const actual = target( state, action ) + expect( actual.dataNormalization ).toEqual( 'hello' ) + } ) + } ) + describe( 'helper functions', () => { describe( 'processAggregations', () => { it( 'calculates percentages properly', () => { diff --git a/src/reducers/map.jsx b/src/reducers/map.jsx index b05623481..7176373c6 100644 --- a/src/reducers/map.jsx +++ b/src/reducers/map.jsx @@ -1,13 +1,11 @@ // reducer for the Map Tab -import { STATE_FILTER_ADDED, STATE_FILTER_REMOVED } from '../actions/map' -import { - STATES_API_CALLED, STATES_FAILED, STATES_RECEIVED -} from '../actions/complaints' -import { TILE_MAP_STATES } from '../constants' +import { GEO_NORM_NONE, TILE_MAP_STATES } from '../constants' +import actions from '../actions' export const defaultState = { isLoading: false, issue: [], + dataNormalization: GEO_NORM_NONE, product: [], selectedState: false, state: [] @@ -152,6 +150,40 @@ export function selectState( state, action ) { } } +/** + * Handler for the update data normalization action + * + * @param {object} state the current state in the Redux store + * @param {object} action the command being executed + * @returns {object} the new state for the Redux store + */ +export function updateDataNormalization( state, action ) { + return { + ...state, + dataNormalization: action.value + }; +} + +/** + * Processes an object of key/value strings into the correct internal format + * + * @param {object} state the current state in the Redux store + * @param {object} action the payload containing the key/value pairs + * @returns {object} a filtered set of key/value pairs with the values set to + * the correct type + */ +function processParams( state, action ) { + const params = action.params + const processed = Object.assign( {}, defaultState ) + + // Handle flag filters + if ( params.dataNormalization ) { + processed.dataNormalization = params.dataNormalization + } + + return processed +} + // ---------------------------------------------------------------------------- // Action Handlers @@ -162,11 +194,15 @@ export function selectState( state, action ) { */ export function _buildHandlerMap() { const handlers = {} - handlers[STATES_API_CALLED] = statesCallInProcess - handlers[STATES_RECEIVED] = processStatesResults - handlers[STATES_FAILED] = processStatesError - handlers[STATE_FILTER_ADDED] = selectState - handlers[STATE_FILTER_REMOVED] = deselectState + + handlers[actions.DATA_NORMALIZATION_SELECTED] = updateDataNormalization + handlers[actions.STATES_API_CALLED] = statesCallInProcess + handlers[actions.STATES_RECEIVED] = processStatesResults + handlers[actions.STATES_FAILED] = processStatesError + handlers[actions.STATE_FILTER_ADDED] = selectState + handlers[actions.STATE_FILTER_REMOVED] = deselectState + handlers[actions.URL_CHANGED] = processParams + return handlers }