Merge pull request #43 from sharetribe/proptype-utils

PropType cleanup
This commit is contained in:
Kimmo Puputti 2017-02-15 13:46:35 +02:00 committed by GitHub
commit e4d5051ffa
4 changed files with 76 additions and 55 deletions

View file

@ -3,6 +3,7 @@ import { connect } from 'react-redux';
import { Switch, Route } from 'react-router-dom';
import { NotFoundPage } from './containers';
import { NamedRedirect } from './components';
import * as propTypes from './util/propTypes';
const Routes = props => {
const { isAuthenticated, routes } = props;
@ -33,20 +34,11 @@ const Routes = props => {
);
};
const { bool, arrayOf, shape, string, func } = PropTypes;
const { bool, arrayOf } = PropTypes;
Routes.propTypes = {
isAuthenticated: bool.isRequired,
routes: arrayOf(
shape({
name: string.isRequired,
pattern: string.isRequired,
exactly: bool,
strict: bool,
component: func.isRequired,
loadData: func,
}),
).isRequired,
routes: arrayOf(propTypes.route).isRequired,
};
const mapStateToProps = state => ({ isAuthenticated: state.Auth.isAuthenticated });

View file

@ -10,32 +10,25 @@ const createSubmitHandler = onLocationChanged => formData => {
};
export const LandingPageComponent = props => {
const handleSubmit = createSubmitHandler(props.onLocationChanged);
const componentOrRedirect = props.LocationFilter && props.LocationFilter.length > 0
? <NamedRedirect name="SearchPage" query={{ location: props.LocationFilter }} />
const { onLocationChanged, filter } = props;
const handleSubmit = createSubmitHandler(onLocationChanged);
return filter.length > 0
? <NamedRedirect name="SearchPage" search={`location=${filter}`} />
: <PageLayout title="Landing page">
<HeroSection>
<HeroSearchForm className={css.form} onSubmit={handleSubmit} />
</HeroSection>
</PageLayout>;
return componentOrRedirect;
};
const { func, string } = PropTypes;
LandingPageComponent.defaultProps = { LocationFilter: '' };
LandingPageComponent.defaultProps = { filter: '' };
LandingPageComponent.propTypes = { onLocationChanged: func.isRequired, LocationFilter: string };
LandingPageComponent.propTypes = { onLocationChanged: func.isRequired, filter: string };
/**
* Container functions.
* Since we add this to global store state with combineReducers, this will only get partial state
* which is page specific.
*/
const mapStateToProps = function mapStateToProps(state) {
return state;
};
const mapStateToProps = state => ({ filter: state.LocationFilter });
const mapDispatchToProps = function mapDispatchToProps(dispatch) {
return { onLocationChanged: v => dispatch(changeLocationFilter(v)) };

View file

@ -1,7 +1,6 @@
import React, { Component, PropTypes } from 'react';
import classNames from 'classnames';
import { connect } from 'react-redux';
import { includes } from 'lodash';
import { addFlashNotification } from '../../ducks/FlashNotification.ducks';
import { addFilter, callFetchListings, loadListings } from './SearchPage.ducks';
import css from './SearchPage.css';
@ -17,21 +16,18 @@ import {
export class SearchPageComponent extends Component {
componentDidMount() {
// TODO: This should be moved to Router
const { SearchPage } = this.props;
if (!SearchPage.initialListingsLoaded) {
const { initialListingsLoaded } = this.props;
if (!initialListingsLoaded) {
this.props.onLoadListings();
}
}
render() {
const { tab, SearchPage } = this.props;
const { listings } = SearchPage;
const fakeListings = listings || [];
const selectedTab = includes(['filters', 'listings', 'map'], tab) ? tab : 'listings';
const { tab, listings } = this.props;
const filtersClassName = classNames(css.filters, { [css.open]: selectedTab === 'filters' });
const listingsClassName = classNames(css.listings, { [css.open]: selectedTab === 'listings' });
const mapClassName = classNames(css.map, { [css.open]: selectedTab === 'map' });
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' });
return (
<PageLayout title="Search page">
@ -41,12 +37,12 @@ export class SearchPageComponent extends Component {
</div>
<div className={listingsClassName}>
<SearchResultsPanel>
{fakeListings.map(l => <ListingCard key={l.id} {...l} />)}
{listings.map(l => <ListingCard key={l.id} {...l} />)}
</SearchResultsPanel>
</div>
<div className={mapClassName}>
<MapPanel>
{fakeListings.map(l => <ListingCardSmall key={l.id} {...l} />)}
{listings.map(l => <ListingCardSmall key={l.id} {...l} />)}
</MapPanel>
</div>
</div>
@ -57,29 +53,21 @@ export class SearchPageComponent extends Component {
SearchPageComponent.loadData = callFetchListings;
SearchPageComponent.defaultProps = { SearchPage: {}, tab: 'listings' };
SearchPageComponent.defaultProps = { initialListingsLoaded: false, listings: [], tab: 'listings' };
const { array, bool, func, shape, string } = PropTypes;
const { array, bool, func, oneOf } = PropTypes;
SearchPageComponent.propTypes = {
onLoadListings: func.isRequired,
SearchPage: shape({
filters: array,
initialListingsLoaded: bool,
listings: array,
loadingListings: bool,
}),
tab: string,
initialListingsLoaded: bool,
listings: array,
tab: oneOf(['filters', 'listings', 'map']).isRequired,
};
/**
* Container functions.
* Since we add this to global store state with combineReducers, this will only get partial state
* which is page specific.
*/
const mapStateToProps = function mapStateToProps(state) {
return state;
};
const mapStateToProps = state => ({
initialListingsLoaded: state.SearchPage.initialListingsLoaded,
listings: state.SearchPage.listings,
});
const mapDispatchToProps = function mapDispatchToProps(dispatch) {
return {

48
src/util/propTypes.js Normal file
View file

@ -0,0 +1,48 @@
/**
* This module defines custom PropTypes shared within the application.
*
* To learn about validating React component props with PropTypes, see:
*
* https://facebook.github.io/react/docs/typechecking-with-proptypes.html
*
* For component specific PropTypes, it's perfectly ok to inline them
* to the component itself. If the type is shared or external (SDK or
* API), however, it should be in this file for sharing with other
* components.
*
* PropTypes should usually be validated only at the lowest level
* where the props are used, not along the way in parents that pass
* along the props to their children. Those parents should usually
* just validate the presense of the prop key and that the value is
* defined. This way we get the validation errors only in the most
* specific place and avoid duplicate errros.
*/
import { PropTypes } from 'react';
import { types as sdkTypes } from 'sharetribe-sdk';
const { UUID, LatLng, LatLngBounds } = sdkTypes;
const { oneOf, string, bool, func, shape, instanceOf } = PropTypes;
// Fixed value
export const value = val => oneOf([val]);
// SDK type instances
export const uuid = instanceOf(UUID);
export const latlng = instanceOf(LatLng);
export const latlngBounds = instanceOf(LatLngBounds);
// Configuration for a single route
export const route = shape({
name: string.isRequired,
pattern: string.isRequired,
exactly: bool,
strict: bool,
component: func.isRequired,
loadData: func,
});
// User object from the API
export const user = shape({ id: uuid.isRequired, type: value('user').isRequired });
// Listing object from the API
export const listing = shape({ id: uuid.isRequired, type: value('listing').isRequired });