Template Files
Templates Directory
Custom Templates
Customize Stencil Checkout
Handlebars Syntax and Helpers
Handlebars.js
Handlebars Helpers
Stencil Object Model
Stencil Objects

Basic npm Example

Compatible with Cornerstone versions earlier than 2.x.x

Because this example involves editing the webpack.conf.js file, the way this example is outlined will align with only with Cornerstone versions earlier than 2.x.x. This is due to the transition to Webpack 4 in Cornerstone 2.0.0, which deprecated the webpack.conf.js file.

What We’re Building

This example will use the Foundation-datepicker.js plugin to implement a datepicker for product pages’ Delivery/Event Date fields.

Screenshot of final product
Screenshot of final product

Set Up Your Store

To test this example, you’ll want your (sandbox or production) store to include at least a couple of products that have a Delivery/Event Date configured. (In production, you’d typically use this feature for things like seasonally themed products, temporary promotions, or event tickets.)

For product configuration steps in the BigCommerce control panel, please see this support article.

Install Dependencies

Use the following command to install this example’s required dependencies:

npm install --save-dev css-loader moment foundation-datepicker style-loader

The above command’s options are:

  • --save-dev saves the dependencies as DevDependencies; this flags them as required for development, but not at runtime. This stack overflow thread provides further context to this concept.

  • css-loader and style-loader are webpack loaders, used together:

    • css-loader resolves @import and url() expressions in CSS files.
    • style-loader generically loads stylesheets by injecting a <style> tag.
  • moment is a JavaScript component parses, validates, and displays dates and times.

  • foundation-datepicker specifies the datepicker package to install.

Configure Webpack loaders in webpack.conf.js

The css and style loaders are used to import CSS and to inject it into the DOM, respectively:

{
    test: /\.css$/,
    loader: 'style-loader!css-loader',
}
CSS and style loaders in context
CSS and style loaders in context

Import the Dependencies

Import these new dependencies into <theme-name>/assets/js/theme/product.js.

In <theme-name>/assets/js/app.js, notice that there is a mapping between the product page and the product.js script:

const PageClasses = {
    mapping: {
        ...
        'pages/product': product,

That is, when a user navigates to the product page, the product.js script is run. First its constructor will be run, followed by the methods before, loaded, and after – in that order.

Configure the loaded() Method

We’ll use the loaded method to initialize our datepicker widget:

import $ from 'jquery';
import PageManager from '../page-manager';
import Review from './product/reviews';
import collapsibleFactory from './common/collapsible';
import ProductDetails from './common/product-details';
import videoGallery from './product/video-gallery';
import { classifyForm } from './common/form-utils';
import 'foundation-datepicker/js/foundation-datepicker.min.js';
import 'foundation-datepicker/css/foundation-datepicker.min.css';
import moment from 'moment';

...

loaded(next) {
    let validator;

    // Init collapsible
    collapsibleFactory();

    this.productDetails = new ProductDetails($('.productView'), this.context);

    videoGallery();

    const $reviewForm = classifyForm('.writeReview-form');
    const review = new Review($reviewForm);

    $('body').on('click', '[data-reveal-id="modal-review-form"]', () => {
        validator = review.registerValidation();
    });

    $reviewForm.on('submit', () => {
        if (validator) {
            validator.performCheck();
            return validator.areAll('valid');
        }

        return false;
    });

    let $deliveryDateMth = $('#deliveryDateMth');
    let $deliveryDateDay = $('#deliveryDateDay');
    let $deliveryDateYr = $('#deliveryDateYr');
    let earliestDate = moment(this.context.product.event_date.date_start, "MMM Do YYYY");
    let latestDate = moment(this.context.product.event_date.date_end, "MMM Do YYYY");

    $('#deliveryDate').fdatepicker({
        leftArrow:'<<',
        rightArrow:'>>',
        onRender: function (date) {
            return moment(date).isBetween(earliestDate, latestDate, null, '[]') ? '' : 'disabled';
        }
    })
    .on('changeDate', function(event) {
        let date = event.date;
        $deliveryDateMth.val(date.getMonth() + 1);
        $deliveryDateDay.val(date.getDate());
        $deliveryDateYr.val(date.getFullYear());
    })
    .data('datepicker');

    next();
}

Highlighted below is the new code added to the loaded method:

Code addition to loaded method in product.js
Code addition to loaded method in product.js

Update the <theme-name>/templates/components/products/product-view.html template, replacing the existing {{#if product.event_date}} block with the following:

{{#if product.event_date}}
    {{inject 'product' product}}
    <div class="form-field">
        <label class="form-label form-label--alternate form-label--inlineSmall">
        {{product.event_date.name}}:
        <small>{{lang 'common.required'}}</small>
        </label>
        <input type="text" class="form-input" id="deliveryDate" name="EventDate[Date]" required>
        <input type="hidden" id="deliveryDateMth" name="EventDate[Mth]">
        <input type="hidden" id="deliveryDateDay" name="EventDate[Day]">
        <input type="hidden" id="deliveryDateYr" name="EventDate[Yr]">
    </div>
{{/if}}

Note that we’re “injecting” the product here, so we have access to its properties. This could be done closer to the root of the tree, but it’s been placed here for proximity to the code that requires it.

We also needed to add form fields for the EventDate[Mth], EventDate[Day], and EventDate[Yr] data, which we update whenever the changeDate event occurs. This conforms to the data format that the server expects. These fields are hidden from the user.