Product Detail

Components
  • Component to mange the view of the product detail
  • File locate: theme/src/components/productDetails/index.js
Code Snippets
<div>
  <section id="product-detail">
    <div className="container">
      <div className="gallery">
        <Gallery images={product.images} />
      </div>

      <div className="product-info">
        <h2>{product.name}</h2>
        <Price product={product} variant={selectedVariant} isAllOptionsSelected={isAllOptionsSelected} settings={settings} />

        <Options options={product.options} onChange={this.onOptionChange} />

        <div className="btn-wrap">
          <AddToCartButton product={product} variant={selectedVariant} addCartItem={this.addToCart} isAllOptionsSelected={isAllOptionsSelected} addingCartItem={addingCartItem}/>
          <a className="add-favorite" onClick={this.likeProduct}><img alt="img" src={this.state.liked ? "/assets/images/cestore/ic_favorited.svg" : "/assets/images/cestore/ic_favorite.svg"}/><span>Add To Favorites</span></a>
        </div>
      </div>

    </div>
  </section>
  <section id="product-content">
    <div className="container">
      <nav className="tab">
        <ul>
          <li><a className={this.state.showDescription ? "active" : ""} onClick={()=>this.setState({showDescription:true})}>Description</a></li>
          <li><a className={!this.state.showDescription ? "active" : ""} onClick={()=>this.setState({showDescription:false})}>Details</a></li>
        </ul>
      </nav>
      {this.state.showDescription && <Description description={product.description} />}
      {!this.state.showDescription && <Attributes attributes={product.attributes} />}
    </div>

  </section>

  <RelatedProducts
    settings={settings}
    addCartItem={this.addToCart}
    ids={product.related_product_ids}
    limit={10}
  />

  {themeSettings.show_viewed_products &&
    <ViewedProducts
      settings={settings}
      addCartItem={this.addToCart}
      product={product}
      limit={themeSettings.limit_viewed_products || 4}
    />
  }

  {themeSettings.disqus_shortname && themeSettings.disqus_shortname !== '' &&
    <section id="comments">
      <div className="container">
        <Disqus
          shortname={themeSettings.disqus_shortname}
          identifier={product.id}
          title={product.name}
          url={product.url}
        />
      </div>
    </section>
  }

  <section id="subscribe">
      <div className="container">
        <h2>Subscribe and Get 15% Off</h2>
        <form>
            <input type="text" placeholder="Your email"/>
            <a><img src="/assets/images/cestore/ic_send_mail.svg" alt="img"/></a>
        </form>
      </div>
  </section>
</div>
Screenshots

Detail

Image Galleries

Components
  • Mange the UI for the product detial galleries
  • The thumnail position can be controlled from the admin site: http://localhost:3000/admin/products
  • File locate: theme/src/components/productDetails/gallery.js
Code Snippets
<Fragment>
  <ImageGallery
    items={imagesArray}
    showThumbnails={showThumbnails}
    onClick={this.openLightbox}
    lazyLoad={true}
    slideInterval={2000}
    showNav={themeSettings.product_gallery_shownav === true}
    showBullets={showThumbnails}
    showPlayButton={false}
    showFullscreenButton={false}
    slideOnThumbnailHover={true}
    thumbnailPosition={themeSettings.product_thumbnail_position}
    onSlide={this.setPhotoIndex}
  />
  {lightboxIsOpen &&
    <Lightbox
        reactModalStyle={{ overlay: { zIndex: 1099 } }}
        mainSrc={originalImages[lightboxPhotoIndex]}
        nextSrc={originalImages[(lightboxPhotoIndex + 1) % originalImages.length]}
        prevSrc={originalImages[(lightboxPhotoIndex + originalImages.length - 1) % originalImages.length]}

        onCloseRequest={this.closeLightbox}
        onMovePrevRequest={() => this.setState({
            lightboxPhotoIndex: (lightboxPhotoIndex + originalImages.length - 1) % originalImages.length,
        })}
        onMoveNextRequest={() => this.setState({
            lightboxPhotoIndex: (lightboxPhotoIndex + 1) % originalImages.length,
        })}
    />
  }
</Fragment>
Screenshots

Detail Gallery

Comment Disqus

Components
  • In order to change the change the Disqus user, Go to Admin / Setting / Theme to change the DISQUS shortname
  • File locate: theme/src/components/productDetails/index.js and theme/src/components/comments/disqus.js
Code Snippets
{themeSettings.disqus_shortname && themeSettings.disqus_shortname !== '' &&
  <section id="comments">
    <div className="container">
      <Disqus
        shortname={themeSettings.disqus_shortname}
        identifier={product.id}
        title={product.name}
        url={product.url}
      />
    </div>
  </section>
}
import React from 'react'

const DISQUS_CONFIG = [
  'shortname', 'identifier', 'title', 'url', 'category_id', 'onNewComment'
];
let __disqusAdded = false;

function copyProps(context, props, prefix = '') {
  Object.keys(props).forEach((prop) => {
    context[prefix + prop] = props[prop];
  });

  if (typeof props.onNewComment === 'function') {
    context[prefix + 'config'] = function config() {
      this.callbacks.onNewComment = [
        function handleNewComment(comment) {
          props.onNewComment(comment);
        }
      ];
    };
  }
}

export default class Disqus extends React.PureComponent {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
    this.loadDisqus();
  }

  componentDidUpdate() {
    this.loadDisqus();
  }

  addDisqusScript() {
    if (__disqusAdded) {
      return;
    }

    const child = this.disqus = document.createElement('script');
    const parent = document.getElementsByTagName('head')[0] ||
                 document.getElementsByTagName('body')[0];

    child.async = true;
    child.type = 'text/javascript';
    child.src = '//' + this.props.shortname + '.disqus.com/embed.js';

    parent.appendChild(child);
    __disqusAdded = true;
  }

  loadDisqus() {
    const props = {};

    // Extract Disqus props that were supplied to this component
    DISQUS_CONFIG.forEach((prop) => {
      if (!!this.props[prop]) {
        props[prop] = this.props[prop];
      }
    });

    // Always set URL
    if (!props.url || !props.url.length) {
      props.url = window.location.href;
    }

    // If Disqus has already been added, reset it
    if (typeof DISQUS !== 'undefined') {
      DISQUS.reset({
        reload: true,
        config: function config() {
          copyProps(this.page, props);

          // Disqus needs hashbang URL, see https://help.disqus.com/customer/portal/articles/472107
          this.page.url = this.page.url.replace(/#/, '') + '#!newthread';
        }
      });
    } else { // Otherwise add Disqus to the page
      copyProps(window, props, 'disqus_');
      this.addDisqusScript();
    }
  }

  render() {
    return <div id="disqus_thread" />;
  }
}
Screenshots

Detail Comments