feat(client): use url query param on search page

This commit is contained in:
ValeraS
2019-06-10 22:53:15 +03:00
committed by Mrugesh Mohapatra
parent aee2f5e536
commit 84c46750e8
4 changed files with 76 additions and 20 deletions

View File

@@ -132,7 +132,6 @@ class DefaultLayout extends Component {
isSignedIn,
landingPage,
navigationMenu,
pathname,
removeFlashMessage,
showFooter = true
} = this.props;
@@ -151,7 +150,7 @@ class DefaultLayout extends Component {
>
<style>{fontawesome.dom.css()}</style>
</Helmet>
<WithInstantSearch pathname={pathname}>
<WithInstantSearch>
<Header
disableSettings={landingPage}
navigationMenu={navigationMenu}

View File

@@ -1,7 +1,9 @@
import React, { Component } from 'react';
import { Location } from '@reach/router';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { InstantSearch, Configure } from 'react-instantsearch-dom';
import qs from 'query-string';
import {
isSearchDropdownEnabledSelector,
@@ -15,7 +17,7 @@ import { createSelector } from 'reselect';
const propTypes = {
children: PropTypes.any,
isDropdownEnabled: PropTypes.bool,
pathname: PropTypes.string.isRequired,
location: PropTypes.object.isRequired,
query: PropTypes.string,
toggleSearchDropdown: PropTypes.func.isRequired,
updateSearchQuery: PropTypes.func.isRequired
@@ -31,33 +33,71 @@ const mapDispatchToProps = {
updateSearchQuery
};
class WithInstantSearch extends Component {
const searchStateToUrl = ({ pathname }, query) =>
`${pathname}${query ? `?${qs.stringify({ query })}` : ''}`;
const urlToSearchState = ({ search }) => qs.parse(search.slice(1));
class InstantSearchRoot extends Component {
componentDidMount() {
const { toggleSearchDropdown } = this.props;
toggleSearchDropdown(this.getSearchEnableDropdown());
toggleSearchDropdown(!this.isSearchPage());
this.setQueryFromURL();
}
componentDidUpdate(prevProps) {
const { pathname, toggleSearchDropdown, isDropdownEnabled } = this.props;
const { pathname: prevPathname } = prevProps;
const enableDropdown = this.getSearchEnableDropdown();
if (pathname !== prevPathname || isDropdownEnabled !== enableDropdown) {
const { location, toggleSearchDropdown, isDropdownEnabled } = this.props;
const enableDropdown = !this.isSearchPage();
if (isDropdownEnabled !== enableDropdown) {
toggleSearchDropdown(enableDropdown);
}
const { query, updateSearchQuery } = this.props;
if (query && pathname !== prevPathname && enableDropdown) {
updateSearchQuery('');
if (location !== prevProps.location) {
const { query, updateSearchQuery } = this.props;
if (this.isSearchPage()) {
this.setQueryFromURL();
} else if (query) {
updateSearchQuery('');
}
}
}
getSearchEnableDropdown = () => !this.props.pathname.startsWith('/search');
isSearchPage = () => this.props.location.pathname.startsWith('/search');
setQueryFromURL = () => {
if (this.isSearchPage()) {
const { updateSearchQuery, location, query } = this.props;
const { query: queryFromURL } = urlToSearchState(location);
if (query !== queryFromURL) {
updateSearchQuery(queryFromURL);
}
}
};
onSearchStateChange = ({ query }) => {
const { updateSearchQuery, query: propsQuery } = this.props;
if (propsQuery === query || typeof query === 'undefined') {
return null;
return;
}
updateSearchQuery(query);
this.updateBrowserHistory(query);
};
updateBrowserHistory = query => {
if (this.isSearchPage()) {
clearTimeout(this.debouncedSetState);
this.debouncedSetState = setTimeout(() => {
if (this.isSearchPage()) {
window.history.pushState(
{ query },
null,
searchStateToUrl(this.props.location, query)
);
}
}, 400);
}
return updateSearchQuery(query);
};
render() {
@@ -77,10 +117,25 @@ class WithInstantSearch extends Component {
}
}
WithInstantSearch.displayName = 'WithInstantSearch';
WithInstantSearch.propTypes = propTypes;
InstantSearchRoot.displayName = 'InstantSearchRoot';
InstantSearchRoot.propTypes = propTypes;
export default connect(
const InstantSearchRootConnected = connect(
mapStateToProps,
mapDispatchToProps
)(WithInstantSearch);
)(InstantSearchRoot);
const WithInstantSearch = ({ children }) => (
<Location>
{({ location }) => (
<InstantSearchRootConnected location={location}>
{children}
</InstantSearchRootConnected>
)}
</Location>
);
WithInstantSearch.displayName = 'WithInstantSearch';
WithInstantSearch.propTypes = { children: PropTypes.any };
export default WithInstantSearch;

View File

@@ -85,7 +85,7 @@ class SearchBar extends Component {
if (query) {
updateSearchQuery(query);
}
return navigate('/search');
return navigate(`/search${query ? `?query=${query}` : ''}`);
}
render() {

View File

@@ -1,5 +1,6 @@
import React, { Fragment, Component } from 'react';
import PropTypes from 'prop-types';
import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import { Index, PoweredBy } from 'react-instantsearch-dom';
import { Grid, Row, Col } from '@freecodecamp/react-bootstrap';
@@ -18,6 +19,7 @@ class SearchPage extends Component {
render() {
return (
<Fragment>
<Helmet title='Search | freeCodeCamp.org' />
<Index indexName='challenges' />
<Index indexName='guides' />
<Index indexName='youtube' />