sharetribe/sharetribe

View on GitHub
client/app/components/sections/SearchPage/SearchPage.js

Summary

Maintainability
A
1 hr
Test Coverage
import { Component } from 'react';
import PropTypes from 'prop-types';
import r, { div } from 'r-dom';
import Immutable from 'immutable';
import classNames from 'classnames';
import styleVariables from '../../../assets/styles/variables';
import { routes as routesProp } from '../../../utils/PropTypes';

import Topbar from '../../sections/Topbar/Topbar';
import ListingCard from '../../composites/ListingCard/ListingCard';
import ListingCardPanel from '../../composites/ListingCardPanel/ListingCardPanel';
import Branding from '../../composites/Branding/Branding';
import FlashNotification from '../../composites/FlashNotification/FlashNotification';
import NoResults from '../../composites/NoResults/NoResults';

import css from './SearchPage.css';

const DEFAULT_CONTEXT = {
  marketplace_color1: styleVariables['--customColorFallback'],
};

const listingsByIds = (listings, ids) =>
  ids.map((id) => listings.get(id));

class SearchPage extends Component {

  constructor(props, context) {
    super(props, context);
    this.listings = listingsByIds(props.searchPage.listings, props.searchPage.currentPage) || new Immutable.List();
    this.listingProps = this.listingProps.bind(this);

    this.totalPages = Math.ceil(this.props.searchPage.state.get('total') / this.props.searchPage.state.get('pageSize'));
  }

  listingProps(listing, color, loggedInUsername) {
    const listingKey = listing.id.toString();
    return {
      key: `card_${listingKey}`,
      color,
      listing,
      loggedInUserIsAuthor: loggedInUsername === listing.getIn(['author', 'username']),
    };
  }

  render() {
    const { marketplace_color1: marketplaceColor1, displayBrandingInfo, linkToSharetribe } = { ...DEFAULT_CONTEXT, ...this.props.marketplace };
    const displayBranding = this.props.marketplace && displayBrandingInfo && linkToSharetribe;

    const searchResults = div(
      {
        className: classNames('SearchPage_main', css.main),
      },
      [
        r(ListingCardPanel,
          {
            className: css.listingContainer,
            currentPage: this.props.searchPage.state.get('page'),
            totalPages: this.totalPages,
            location: this.props.marketplace.location,
            pageParam: 'page',
          },
          this.listings.map((listing) =>
            r(ListingCard, this.listingProps(listing, marketplaceColor1, this.props.user.loggedInUsername))
          )
        ),
      ]);
    const noResults = r(NoResults, {
      className: classNames('SearchPage_main', css.empty),
    });

    return div({ className: classNames('SearchPage', css.SearchPage) }, [
      r(Topbar, {
        ...this.props.topbar,
        routes: this.props.routes,
      }),
      this.listings.size > 0 ? searchResults : noResults,
      displayBranding ? r(Branding, { linkToSharetribe }) : null,
      r(FlashNotification, {
        actions: this.props.actions,
        messages: this.props.flashNotifications,
      }),
    ]);
  }
}

export const SearchPageModel = Immutable.Record({
  prevPage: new Immutable.List(),
  currentPage: new Immutable.List(),
  nextPage: new Immutable.List(),
  listings: new Immutable.List(),
  state: new Immutable.Map(),
});

const { bool, func, instanceOf, shape, string } = PropTypes;

SearchPage.propTypes = {
  actions: shape({
    removeFlashNotification: func.isRequired,
  }).isRequired,
  flashNotifications: instanceOf(Immutable.List).isRequired,
  searchPage: instanceOf(SearchPageModel).isRequired,
  marketplace: PropTypes.shape({
    marketplaceColor1: string,
    location: string,
    displayBrandingInfo: bool,
    linkToSharetribe: string,
  }),
  routes: routesProp,
  topbar: shape(Topbar.propTypes).isRequired,
  user: shape({
    loggedInUsername: string,
    isAdmin: bool,
  }),
};

export default SearchPage;