Merge pull request #94 from sharetribe/searchpage-fixes

Search page fixes
This commit is contained in:
Kimmo Puputti 2017-03-27 11:04:47 +03:00 committed by GitHub
commit 7ab44d81ae
4 changed files with 58 additions and 29 deletions

View file

@ -10,6 +10,7 @@ export const SEARCH_LISTINGS_ERROR = 'app/SearchPage/SEARCH_LISTINGS_ERROR';
const initialState = {
searchParams: null,
searchInProgress: false,
searchListingsError: null,
currentPageResultIds: [],
};
@ -20,13 +21,19 @@ const listingPageReducer = (state = initialState, action = {}) => {
const { type, payload } = action;
switch (type) {
case SEARCH_LISTINGS_REQUEST:
return { ...state, searchParams: payload.searchParams, searchListingsError: null };
return {
...state,
searchParams: payload.searchParams,
searchInProgress: true,
currentPageResultIds: [],
searchListingsError: null,
};
case SEARCH_LISTINGS_SUCCESS:
return { ...state, currentPageResultIds: resultIds(payload.data) };
return { ...state, searchInProgress: false, currentPageResultIds: resultIds(payload.data) };
case SEARCH_LISTINGS_ERROR:
// eslint-disable-next-line no-console
console.error(payload);
return { ...state, searchListingsError: payload };
return { ...state, searchInProgress: false, searchListingsError: payload };
default:
return state;
}
@ -58,8 +65,8 @@ export const searchListings = searchParams =>
return sdk.listings
.search(searchParams)
.then(response => {
dispatch(searchListingsSuccess(response));
dispatch(showListingsSuccess(response));
dispatch(searchListingsSuccess(response));
return response;
})
.catch(e => {

View file

@ -1,6 +1,6 @@
import React, { PropTypes } from 'react';
import classNames from 'classnames';
import { intlShape, injectIntl } from 'react-intl';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import config from '../../config';
import { parse } from '../../util/urlHelpers';
@ -18,35 +18,45 @@ import { searchListings } from './SearchPage.duck';
import css from './SearchPage.css';
export const SearchPageComponent = props => {
const { tab, listings, searchParams, searchListingsError, intl } = props;
const { tab, listings, searchParams, searchInProgress, searchListingsError } = props;
const filtersClassName = classNames(css.filters, { [css.open]: tab === 'filters' });
const listingsClassName = classNames(css.listings, { [css.open]: tab === 'listings' });
const mapClassName = classNames(css.map, { [css.open]: tab === 'map' });
const currencyConfig = config.currencyConfig;
const searchWasDone = searchParams && searchParams.address;
const searchWasDone = !searchInProgress && searchParams && searchParams.address;
const searchErrorMessage = intl.formatMessage({ id: 'SearchPage.searchError' });
const resultsFoundMessage = intl.formatMessage(
{ id: 'SearchPage.foundResults' },
{
count: listings.length,
address: searchParams && searchParams.address ? searchParams.address : '',
}
const searchError = (
<p style={{ color: 'red' }}>
<FormattedMessage id="SearchPage.searchError" />
</p>
);
const noResultsMessage = intl.formatMessage(
{ id: 'SearchPage.noResults' },
{
address: searchParams && searchParams.address ? searchParams.address : '',
}
const resultsFound = (
<p>
<FormattedMessage id="SearchPage.foundResults" values={{ count: listings.length }} />
</p>
);
const noResults = (
<p>
<FormattedMessage id="SearchPage.noResults" />
</p>
);
const loadingResults = (
<p>
<FormattedMessage id="SearchPage.loadingResults" />
</p>
);
return (
<PageLayout title={`Search page: ${tab}`}>
{searchListingsError ? <p style={{ color: 'red' }}>{searchErrorMessage}</p> : null}
{searchWasDone && listings.length > 0 ? <p>{resultsFoundMessage}</p> : null}
{searchWasDone && listings.length === 0 ? <p>{noResultsMessage}</p> : null}
{searchListingsError ? searchError : null}
{searchWasDone && listings.length > 0 ? resultsFound : null}
{searchWasDone && listings.length === 0 ? noResults : null}
{searchInProgress ? loadingResults : null}
<div className={css.container}>
<div className={filtersClassName}>
<FilterPanel />
@ -77,26 +87,32 @@ SearchPageComponent.defaultProps = {
searchListingsError: null,
};
const { array, oneOf, object, instanceOf } = PropTypes;
const { array, oneOf, object, instanceOf, bool } = PropTypes;
SearchPageComponent.propTypes = {
tab: oneOf(['filters', 'listings', 'map']).isRequired,
listings: array,
searchParams: object,
searchInProgress: bool.isRequired,
searchListingsError: instanceOf(Error),
intl: intlShape.isRequired,
};
const mapStateToProps = state => {
const { searchParams, searchListingsError, currentPageResultIds } = state.SearchPage;
const {
searchParams,
searchInProgress,
searchListingsError,
currentPageResultIds,
} = state.SearchPage;
return {
listings: getListingsById(state.data, currentPageResultIds),
searchParams,
searchInProgress,
searchListingsError,
};
};
const SearchPage = connect(mapStateToProps)(injectIntl(SearchPageComponent));
const SearchPage = connect(mapStateToProps)(SearchPageComponent);
SearchPage.loadData = (params, search) => {
const queryParams = parse(search, {

View file

@ -19,7 +19,12 @@ const { LatLng } = types;
describe('SearchPageComponent', () => {
it('matches snapshot', () => {
const tree = renderShallow(
<SearchPageComponent onLoadListings={v => v} dispatch={() => null} intl={fakeIntl} />
<SearchPageComponent
onLoadListings={v => v}
dispatch={() => null}
intl={fakeIntl}
searchInProgress={false}
/>
);
expect(tree).toMatchSnapshot();
});

View file

@ -20,6 +20,7 @@
"PageLayout.authInfoFailed": "Could not get authentication information.",
"PageLayout.logoutFailed": "Logout failed. Please try again.",
"SearchPage.searchError": "Search failed. Please try again.",
"SearchPage.foundResults": "{count} listings found near {address}.",
"SearchPage.noResults": "Could not find any listings near {address}."
"SearchPage.foundResults": "{count, number} {count, plural, one {listing} other {listings}} found.",
"SearchPage.noResults": "Could not find any listings.",
"SearchPage.loadingResults": "Loading search results..."
}