diff --git a/Gemfile b/Gemfile index 7d1b5b1..28c37dd 100644 --- a/Gemfile +++ b/Gemfile @@ -25,7 +25,7 @@ gem 'rubocop', require: false gem 'therubyracer' gem 'font-awesome-rails', '~> 4.3.0.0' gem 'modernizr-rails' -gem "react_on_rails", git: 'https://github.com/shakacode/react_on_rails' +gem "react_on_rails" gem 'sass-rails', '~> 4.0.3' gem 'uglifier', '>= 1.0.3' gem 'foundation-rails', '~> 5.4.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index 8614606..91eed2f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,18 +7,6 @@ GIT rails_autolink (~> 1.0) rest-client (~> 1.6) -GIT - remote: https://github.com/shakacode/react_on_rails - revision: 3fe05f917145876e62846d9689c4ae82778f8c6b - specs: - react_on_rails (6.0.3) - addressable - connection_pool - execjs (~> 2.5) - foreman - rails (>= 3.2) - rainbow (~> 2.1) - GEM remote: https://rubygems.org/ specs: @@ -279,10 +267,17 @@ GEM rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.1.0) - rake (11.1.2) + rake (11.2.2) rb-fsevent (0.9.7) rb-inotify (0.9.7) ffi (>= 0.5.0) + react_on_rails (6.0.5) + addressable + connection_pool + execjs (~> 2.5) + foreman + rails (>= 3.2) + rainbow (~> 2.1) ref (2.0.0) responders (2.1.1) railties (>= 4.2.0, < 5.1) @@ -435,7 +430,7 @@ DEPENDENCIES pry-rails quiet_assets rails (~> 4.2) - react_on_rails! + react_on_rails rspec-activemodel-mocks rspec-rails (~> 3.0) rubocop @@ -456,4 +451,4 @@ DEPENDENCIES yard BUNDLED WITH - 1.12.4 + 1.12.5 diff --git a/client/.eslintrc b/client/.eslintrc index 75c63b2..780ce3e 100644 --- a/client/.eslintrc +++ b/client/.eslintrc @@ -88,7 +88,7 @@ "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq "guard-for-in": 0, // http://eslint.org/docs/rules/guard-for-in "no-caller": 2, // http://eslint.org/docs/rules/no-caller - "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return + "no-else-return": 0, // http://eslint.org/docs/rules/no-else-return "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null "no-eval": 2, // http://eslint.org/docs/rules/no-eval "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native @@ -189,34 +189,6 @@ "react/react-in-jsx-scope": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md "react/self-closing-comp": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md "react/wrap-multilines": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/wrap-multilines.md - "react/sort-comp": [2, { // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md - "order": [ - "displayName", - "propTypes", - "contextTypes", - "childContextTypes", - "mixins", - "statics", - "defaultProps", - "/^_(?!(on|get|render))/", - "constructor", - "getDefaultProps", - "getInitialState", - "state", - "getChildContext", - "componentWillMount", - "componentDidMount", - "componentWillReceiveProps", - "shouldComponentUpdate", - "componentWillUpdate", - "componentDidUpdate", - "componentWillUnmount", - "/^_?on.+$/", - "/^_?get.+$/", - "/^_?create.+$/", - "/^_?render.+$/", - "render" - ] - }] + "react/sort-comp": 2 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md } } diff --git a/client/app/bundles/SearchPage/actions/filters.js b/client/app/bundles/SearchPage/actions/filters.js index 168168b..1199d0e 100644 --- a/client/app/bundles/SearchPage/actions/filters.js +++ b/client/app/bundles/SearchPage/actions/filters.js @@ -1,11 +1,19 @@ import { createAction } from 'redux-actions'; +import performSearch from './performSearch'; import _ from 'lodash'; export const ADD_FILTER = 'ADD_FILTER'; export const addFilter = createAction(ADD_FILTER); export const REMOVE_FILTER = 'REMOVE_FILTER'; -export const removeFilter = createAction(REMOVE_FILTER); +const removeFilterAction = createAction(REMOVE_FILTER); + +export function removeFilter(filter) { + return (dispatch) => { + dispatch(removeFilterAction(filter)); + dispatch(performSearch()); + }; +} // This is a custom action function, it takes as an argument the filter to be toggled // And returns a function which accepts two arguments, dispatch and getState @@ -22,7 +30,7 @@ export default function toggleFilter(filter) { // _.some is equivalent to Enumerable#any? in Ruby if (_.some(state.filters, (f) => _.isEqual(f, filter))) - dispatch(removeFilter(filter)); + dispatch(removeFilterAction(filter)); else dispatch(addFilter(filter)); }; diff --git a/client/app/bundles/SearchPage/actions/performSearch.js b/client/app/bundles/SearchPage/actions/performSearch.js index 63645b5..6787713 100644 --- a/client/app/bundles/SearchPage/actions/performSearch.js +++ b/client/app/bundles/SearchPage/actions/performSearch.js @@ -13,7 +13,7 @@ export default function performSearch() { const groupedFacets = _.groupBy(state.filters, 'facet'); const searchCategory = {tab: state.searchTabs.activeTab}; - if(searchCategory.tab != 'All') + if (searchCategory.tab !== 'All') _.assign(baseQueryParams, searchCategory); // This reduces the object of grouped facets, which looks like this diff --git a/client/app/bundles/SearchPage/components/searchPanel/index.js b/client/app/bundles/SearchPage/components/searchPanel/index.js index aa138ac..698cbf5 100644 --- a/client/app/bundles/SearchPage/components/searchPanel/index.js +++ b/client/app/bundles/SearchPage/components/searchPanel/index.js @@ -1,7 +1,6 @@ import React, { Component, PropTypes } from 'react'; import togglePanel from '../../actions/togglePanel'; import { removeFilter } from '../../actions/filters'; -import performSearch from '../../actions/performSearch'; import _ from 'lodash'; import Panel from './panel'; diff --git a/client/app/bundles/SearchPage/components/searchTab/categoryTab.js b/client/app/bundles/SearchPage/components/searchTab/categoryTab.js index ee88c37..3b11b1a 100644 --- a/client/app/bundles/SearchPage/components/searchTab/categoryTab.js +++ b/client/app/bundles/SearchPage/components/searchTab/categoryTab.js @@ -17,18 +17,20 @@ export default class categoryTab extends Component { } onCategoryChange(e) { - const { dispatch, categoryName } = this.props; e.preventDefault(); + const { dispatch, categoryName } = this.props; + dispatch(searchTabs(categoryName)); dispatch(performSearch()); } render() { const { categoryName, count, activeCategory } = this.props; - let activeTag = (categoryName === activeCategory) ? 'active': ''; + const activeTag = (categoryName === activeCategory) ? 'active' : ''; + return (
  • - + {categoryName} {count} diff --git a/client/app/bundles/SearchPage/components/searchTab/dropdown.js b/client/app/bundles/SearchPage/components/searchTab/dropdown.js index fabd57c..3015ceb 100644 --- a/client/app/bundles/SearchPage/components/searchTab/dropdown.js +++ b/client/app/bundles/SearchPage/components/searchTab/dropdown.js @@ -16,7 +16,7 @@ export default class DropDown extends Component { categoryStats: PropTypes.arrayOf( PropTypes.shape({ category: PropTypes.string.isRequired, - count: PropTypes.string.isRequired + count: PropTypes.string.isRequired, }) ).isRequired, activeCategory: PropTypes.string.isRequired, @@ -24,19 +24,6 @@ export default class DropDown extends Component { dispatch: PropTypes.func.isRequired, }; - selectedStatus(tab) { - const { dropdownIsVisible } = this.state; - return (!dropdownIsVisible) && - !_.includes(PRIMARY_TABS, tab) - } - - moreTitleCount(tab) { - if(_.includes(PRIMARY_TABS, tab)) { - tab = 'More'; - } - let b = _.find(this.props.categoryStats, {category: tab}); - return b.count; - } constructor(props, context) { super(props, context); @@ -49,76 +36,85 @@ export default class DropDown extends Component { menuStyle: { position: 'absolute', top: '0px', - left: '9999px' + left: '9999px', }, - categoryStats: props.categoryStats + categoryStats: props.categoryStats, }; - // We should bind `this` to click event handler right here - _.bindAll(this, '_hideDropdown', '_toggleDropdown', '_stopPropagation', '_adjustPosition'); + _.bindAll(this, 'hideDropdown', 'toggleDropdown', 'handleClick', 'adjustPosition'); } - _adjustPosition() { - let vbox = this.refs.more_dropdown_menu.getBoundingClientRect(); - this.state.menuStyle.top = (vbox.bottom+window.pageYOffset)+'px'; - this.state.menuStyle.left = (vbox.left+window.pageXOffset)+'px'; - } componentDidMount() { - // Hide dropdown block on click outside the block - window.addEventListener('click', this._hideDropdown, false); - this._adjustPosition(); + window.addEventListener('click', this.hideDropdown, false); + + this.adjustPosition(); } componentWillUnmount() { - // Remove click event listener on component unmount - window.removeEventListener('click', this._hideDropdown, false); + window.removeEventListener('click', this.hideDropdown, false); } - _stopPropagation(e) { - this._adjustPosition(); - // Stop bubbling of click event on click inside the dropdown content - this._toggleDropdown(); - e.stopPropagation(); - e.nativeEvent.stopImmediatePropagation(); + selectedStatus(tab) { + const { dropdownIsVisible } = this.state; + + return !dropdownIsVisible && !_.includes(PRIMARY_TABS, tab); } + moreTitleCount(tab) { + let tabName = tab; + if (_.includes(PRIMARY_TABS, tab)) + tabName = 'More'; - _toggleDropdown() { + return _.find(this.props.categoryStats, {category: tabName}).count; + } + + adjustPosition() { + const vbox = this.refs.more_dropdown_menu.getBoundingClientRect(); + + this.state.menuStyle.top = `${vbox.bottom + window.pageYOffset}px`; + this.state.menuStyle.left = `${vbox.left + window.pageXOffset}px`; + } + + toggleDropdown() { const { dropdownIsVisible } = this.state; - this._adjustPosition(); - // Toggle dropdown block visibility + + this.adjustPosition(); + this.setState({ dropdownIsVisible: !dropdownIsVisible }); } + handleClick(e) { + this.adjustPosition(); + this.toggleDropdown(); + + e.stopPropagation(); + e.nativeEvent.stopImmediatePropagation(); + } - _hideDropdown() { + hideDropdown() { this.setState({ dropdownIsVisible: false }); } - _handleFocus() { - // Make active on focus + handleFocus() { this.setState({ dropdownIsActive: true }); } - - _handleBlur() { - // Clean up everything on blur + handleBlur() { this.setState({ dropdownIsVisible: false, - dropdownIsActive: false + dropdownIsActive: false, }); } getMenuName() { const {categoryName, activeCategory} = this.props; - if(!_.isUndefined(activeCategory) && !_.includes(PRIMARY_TABS, activeCategory)) + if (!_.isUndefined(activeCategory) && !_.includes(PRIMARY_TABS, activeCategory)) return activeCategory; else return categoryName; - } render() { @@ -127,7 +123,7 @@ export default class DropDown extends Component { const tabClass = classNames({active: this.selectedStatus(activeCategory)}); const tabMenus = _.chain(categoryStats) - .filter((e) => (e.category != 'More')) + .filter((e) => e.category !== 'More') .map((tab, index) =>