diff --git a/.eslintrc b/.eslintrc index 1f9bf0f516..3c902f1ef1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -25,6 +25,10 @@ "import/no-named-as-default": 0, "import/no-named-as-default-member": 0 }, + "globals": { + "$": true, + "jQuery": true + }, "env": { "es6": true, "browser": true diff --git a/.gitignore b/.gitignore index 9a2eb85755..8081e932e9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ node_modules/ assets/css-artifacts assets/js/bundle.js assets/dist +vendor/bundle +.bundle *.js.map *.zip .idea/ diff --git a/.travis.yml b/.travis.yml index c71d4b7b3e..ca2fc44f7e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,12 @@ language: node_js node_js: - 6 + - 8 sudo: required dist: trusty install: - - bundle install - - npm install -g grunt-cli - - npm install -g bigcommerce/stencil-cli + - bundle install --path vendor/bundle - npm install script: - - grunt check - - stencil bundle + - npx grunt check + - npx stencil bundle diff --git a/CHANGELOG.md b/CHANGELOG.md index efffbda1a3..0397d658fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,17 @@ ## Draft +- Major performance improvements. Reduce Javascript bundle size from 376kb to 286kb. [#1390](https://github.com/bigcommerce/cornerstone/pull/1390) - Fixed breadcrumbs for product and category pages [#1403](https://github.com/bigcommerce/cornerstone/pull/1403) - Send GA tracking event whenever the last product is removed from the CART[#1409](https://github.com/bigcommerce/cornerstone/pull/1409) ## 3.0.0 (2018-12-21) +### Breaking Changes +- Don't load Cart resource on non-cart pages [#1401](https://github.com/bigcommerce/cornerstone/pull/1401). While the theme itself doesn't depend on + this resource on non-cart pages, this can potentially affect any scripts added by the Script Manager or the legacy footer scripts that depend on cart. + If this applies to you, you'll want to add the cart resource back on the page types that need it (via front matter). + +### Other Changes - Added defer tag to addThis and defered execution of related script [#1406](https://github.com/bigcommerce/cornerstone/pull/1406) - Fixed compare buttons for product list display [#1384](https://github.com/bigcommerce/cornerstone/pull/1384) - Remove unnecessary API call to get cookie notification status [#1380](https://github.com/bigcommerce/cornerstone/pull/1380) @@ -16,7 +23,6 @@ - Only show cookie privacy notice for EU IP addresses [#1381](https://github.com/bigcommerce/cornerstone/pull/1381) - Move Cart Quantity header value to a FE API call [#1379](https://github.com/bigcommerce/cornerstone/pull/1379) - Make display of quantity selection box on PDP configurable. [#1398](https://github.com/bigcommerce/cornerstone/pull/1398) -- Don't load Cart resource on non-Cart pages [#1401](https://github.com/bigcommerce/cornerstone/pull/1401) - Remove deprecated fields - delivery and event date, and configurable fields. [#1407](https://github.com/bigcommerce/cornerstone/pull/1407) ## 2.6.0 (2018-11-05) diff --git a/assets/js/app.js b/assets/js/app.js index 2cafa67ee4..b9e92bfdc8 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,12 +1,11 @@ __webpack_public_path__ = window.__webpack_public_path__; // eslint-disable-line -import 'babel-polyfill'; -import $ from 'jquery'; -import 'jquery-migrate'; import Global from './theme/global'; const getAccount = () => import('./theme/account'); const getLogin = () => import('./theme/auth'); +const noop = null; + const pageClasses = { account_orderstatus: getAccount, account_order: getAccount, @@ -28,27 +27,27 @@ const pageClasses = { createaccount: getLogin, getnewpassword: getLogin, forgotpassword: getLogin, - blog: () => import('./theme/blog'), - blog_post: () => import('./theme/blog-post'), + blog: noop, + blog_post: noop, brand: () => import('./theme/brand'), - brands: () => import('./theme/brands'), + brands: noop, cart: () => import('./theme/cart'), category: () => import('./theme/category'), compare: () => import('./theme/compare'), page_contact_form: () => import('./theme/contact-us'), - error: () => import('./theme/errors'), - 404: () => import('./theme/404-error'), + error: noop, + 404: noop, giftcertificates: () => import('./theme/gift-certificate'), giftcertificates_balance: () => import('./theme/gift-certificate'), giftcertificates_redeem: () => import('./theme/gift-certificate'), - default: () => import('./theme/home'), - page: () => import('./theme/page'), + default: noop, + page: noop, product: () => import('./theme/product'), amp_product_options: () => import('./theme/product'), search: () => import('./theme/search'), - rss: () => import('./theme/rss'), - sitemap: () => import('./theme/sitemap'), - newsletter_subscribe: () => import('./theme/subscribe'), + rss: noop, + sitemap: noop, + newsletter_subscribe: noop, wishlist: () => import('./theme/wishlist'), wishlists: () => import('./theme/wishlist'), }; @@ -64,32 +63,35 @@ const customClasses = {}; */ window.stencilBootstrap = function stencilBootstrap(pageType, contextJSON = null, loadGlobal = true) { const context = JSON.parse(contextJSON || '{}'); - const template = context.template; - const templateCheck = Object.keys(customClasses).indexOf(template); return { load() { - $(async () => { + $(() => { // Load globals if (loadGlobal) { Global.load(context); } + const importPromises = []; + // Find the appropriate page loader based on pageType const pageClassImporter = pageClasses[pageType]; if (typeof pageClassImporter === 'function') { - const PageClass = (await pageClassImporter()).default; - PageClass.load(context); + importPromises.push(pageClassImporter()); } - if (templateCheck > -1) { - // Find the appropriate page loader based on template - const customClassImporter = customClasses[template]; - if (typeof customClassImporter === 'function') { - const CustomClass = (await customClassImporter()).default; - CustomClass.load(context); - } + // See if there is a page class default for a custom template + const customTemplateImporter = customClasses[context.template]; + if (typeof customTemplateImporter === 'function') { + importPromises.push(customTemplateImporter()); } + + // Wait for imports to resolve, then call load() on them + Promise.all(importPromises).then(imports => { + imports.forEach(imported => { + imported.default.load(context); + }); + }); }); }, }; diff --git a/assets/js/test-unit/theme/cart.spec.js b/assets/js/test-unit/theme/cart.spec.js index fabe622607..8f061b12a6 100644 --- a/assets/js/test-unit/theme/cart.spec.js +++ b/assets/js/test-unit/theme/cart.spec.js @@ -1,10 +1,8 @@ import $ from 'jquery' import utils from '@bigcommerce/stencil-utils'; import Cart from '../../theme/cart.js'; -import * as SweetAlert from 'sweetalert2'; var dataSpy; -var swalSpy; var UpdateSpy; var c = new Cart(); beforeEach(function() { diff --git a/assets/js/theme/404-error.js b/assets/js/theme/404-error.js deleted file mode 100644 index 9fee3fa49d..0000000000 --- a/assets/js/theme/404-error.js +++ /dev/null @@ -1,3 +0,0 @@ -import PageManager from './page-manager'; - -export default class Errors404 extends PageManager {} diff --git a/assets/js/theme/account.js b/assets/js/theme/account.js index f6099cc280..c5ab713b43 100644 --- a/assets/js/theme/account.js +++ b/assets/js/theme/account.js @@ -1,13 +1,12 @@ import PageManager from './page-manager'; import _ from 'lodash'; -import $ from 'jquery'; import nod from './common/nod'; import Wishlist from './wishlist'; import validation from './common/form-validation'; import stateCountry from './common/state-country'; import { classifyForm, Validators, insertStateHiddenField } from './common/form-utils'; import { creditCardType, storeInstrument, Validators as CCValidators, Formatters as CCFormatters } from './common/payment-method'; -import swal from 'sweetalert2'; +import swal from './global/sweet-alert'; export default class Account extends PageManager { constructor(context) { diff --git a/assets/js/theme/auth.js b/assets/js/theme/auth.js index 4f1c2472e7..a17da37e3c 100644 --- a/assets/js/theme/auth.js +++ b/assets/js/theme/auth.js @@ -1,6 +1,5 @@ import PageManager from './page-manager'; import stateCountry from './common/state-country'; -import $ from 'jquery'; import nod from './common/nod'; import validation from './common/form-validation'; import forms from './common/models/forms'; diff --git a/assets/js/theme/blog-post.js b/assets/js/theme/blog-post.js deleted file mode 100644 index 8af1b0e84f..0000000000 --- a/assets/js/theme/blog-post.js +++ /dev/null @@ -1,3 +0,0 @@ -import PageManager from './page-manager'; - -export default class BlogPost extends PageManager {} diff --git a/assets/js/theme/blog.js b/assets/js/theme/blog.js deleted file mode 100644 index 1fbb796f30..0000000000 --- a/assets/js/theme/blog.js +++ /dev/null @@ -1,4 +0,0 @@ -import PageManager from './page-manager'; - -export default class Blog extends PageManager {} - diff --git a/assets/js/theme/brand.js b/assets/js/theme/brand.js index 9d897aae28..a87656981e 100644 --- a/assets/js/theme/brand.js +++ b/assets/js/theme/brand.js @@ -1,10 +1,12 @@ import { hooks } from '@bigcommerce/stencil-utils'; import CatalogPage from './catalog'; -import $ from 'jquery'; +import compareProducts from './global/compare-products'; import FacetedSearch from './common/faceted-search'; export default class Brand extends CatalogPage { onReady() { + compareProducts(this.context.urls); + if ($('#facetedSearch').length > 0) { this.initFacetedSearch(); } else { diff --git a/assets/js/theme/brands.js b/assets/js/theme/brands.js deleted file mode 100644 index 448a1baac1..0000000000 --- a/assets/js/theme/brands.js +++ /dev/null @@ -1,3 +0,0 @@ -import PageManager from './page-manager'; - -export default class Brands extends PageManager {} diff --git a/assets/js/theme/cart.js b/assets/js/theme/cart.js index 0793feb196..f747b7f086 100644 --- a/assets/js/theme/cart.js +++ b/assets/js/theme/cart.js @@ -1,11 +1,10 @@ import PageManager from './page-manager'; -import $ from 'jquery'; import _ from 'lodash'; import giftCertCheck from './common/gift-certificate-validator'; import utils from '@bigcommerce/stencil-utils'; import ShippingEstimator from './cart/shipping-estimator'; import { defaultModal } from './global/modal'; -import swal from 'sweetalert2'; +import swal from './global/sweet-alert'; export default class Cart extends PageManager { onReady() { diff --git a/assets/js/theme/cart/shipping-estimator.js b/assets/js/theme/cart/shipping-estimator.js index 65467b545b..0b5d1eb4a6 100644 --- a/assets/js/theme/cart/shipping-estimator.js +++ b/assets/js/theme/cart/shipping-estimator.js @@ -1,9 +1,8 @@ -import $ from 'jquery'; import stateCountry from '../common/state-country'; import nod from '../common/nod'; import utils from '@bigcommerce/stencil-utils'; import { Validators } from '../common/form-utils'; -import swal from 'sweetalert2'; +import swal from '../global/sweet-alert'; export default class ShippingEstimator { constructor($element) { diff --git a/assets/js/theme/catalog.js b/assets/js/theme/catalog.js index 352e842971..6084495130 100644 --- a/assets/js/theme/catalog.js +++ b/assets/js/theme/catalog.js @@ -1,5 +1,4 @@ import PageManager from './page-manager'; -import $ from 'jquery'; import urlUtils from './common/url-utils'; import Url from 'url'; diff --git a/assets/js/theme/category.js b/assets/js/theme/category.js index 9086863815..a1814079cb 100644 --- a/assets/js/theme/category.js +++ b/assets/js/theme/category.js @@ -1,10 +1,12 @@ import { hooks } from '@bigcommerce/stencil-utils'; import CatalogPage from './catalog'; -import $ from 'jquery'; +import compareProducts from './global/compare-products'; import FacetedSearch from './common/faceted-search'; export default class Category extends CatalogPage { onReady() { + compareProducts(this.context.urls); + if ($('#facetedSearch').length > 0) { this.initFacetedSearch(); } else { diff --git a/assets/js/theme/common/carousel.js b/assets/js/theme/common/carousel.js index cef835315c..5d297f3fec 100644 --- a/assets/js/theme/common/carousel.js +++ b/assets/js/theme/common/carousel.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import 'slick-carousel'; export default function () { @@ -10,8 +9,8 @@ export default function () { // Alternative image styling for IE, which doesn't support objectfit if (typeof document.documentElement.style.objectFit === 'undefined') { - $('.heroCarousel-slide').each(() => { - const $container = $(this); + $('.heroCarousel-slide').each((index, element) => { + const $container = $(element); const imgUrl = $container.find('img').data('lazy'); if (imgUrl) { diff --git a/assets/js/theme/common/collapsible-group.js b/assets/js/theme/common/collapsible-group.js index 575662f719..894423df5e 100644 --- a/assets/js/theme/common/collapsible-group.js +++ b/assets/js/theme/common/collapsible-group.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import { CollapsibleEvents } from '../common/collapsible'; const PLUGIN_KEY = 'collapsible-group'; diff --git a/assets/js/theme/common/collapsible.js b/assets/js/theme/common/collapsible.js index 4ea23c3677..101d4b82b3 100644 --- a/assets/js/theme/common/collapsible.js +++ b/assets/js/theme/common/collapsible.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import _ from 'lodash'; import mediaQueryListFactory from './media-query-list'; diff --git a/assets/js/theme/common/faceted-search.js b/assets/js/theme/common/faceted-search.js index 69a122fde4..d9eb035a03 100644 --- a/assets/js/theme/common/faceted-search.js +++ b/assets/js/theme/common/faceted-search.js @@ -1,5 +1,4 @@ import { hooks, api } from '@bigcommerce/stencil-utils'; -import $ from 'jquery'; import _ from 'lodash'; import Url from 'url'; import urlUtils from './url-utils'; diff --git a/assets/js/theme/common/form-utils.js b/assets/js/theme/common/form-utils.js index 913969c4f5..b8c45240ad 100644 --- a/assets/js/theme/common/form-utils.js +++ b/assets/js/theme/common/form-utils.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import _ from 'lodash'; import nod from './nod'; import forms from './models/forms'; diff --git a/assets/js/theme/common/form-validation.js b/assets/js/theme/common/form-validation.js index 72f736bd92..03658440fa 100644 --- a/assets/js/theme/common/form-validation.js +++ b/assets/js/theme/common/form-validation.js @@ -1,5 +1,3 @@ -import $ from 'jquery'; - /** * Validate that the given date for the day/month/year select inputs is within potential range * @param $formField diff --git a/assets/js/theme/common/nod-functions/min-max-validate.js b/assets/js/theme/common/nod-functions/min-max-validate.js index b8dd6ebd2b..a72488cfe7 100644 --- a/assets/js/theme/common/nod-functions/min-max-validate.js +++ b/assets/js/theme/common/nod-functions/min-max-validate.js @@ -1,5 +1,4 @@ import _ from 'lodash'; -import $ from 'jquery'; function minMaxValidate(minInputSelector, maxInputSelector) { function validate(cb) { diff --git a/assets/js/theme/common/payment-method.js b/assets/js/theme/common/payment-method.js index d0869bc6e1..82d7a42bb4 100644 --- a/assets/js/theme/common/payment-method.js +++ b/assets/js/theme/common/payment-method.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import creditcards from 'creditcards'; /** diff --git a/assets/js/theme/common/product-details.js b/assets/js/theme/common/product-details.js index 14ba07684b..76fe076e26 100644 --- a/assets/js/theme/common/product-details.js +++ b/assets/js/theme/common/product-details.js @@ -1,11 +1,9 @@ -import $ from 'jquery'; import utils from '@bigcommerce/stencil-utils'; import 'foundation-sites/js/foundation/foundation'; import 'foundation-sites/js/foundation/foundation.reveal'; import ImageGallery from '../product/image-gallery'; -import modalFactory from '../global/modal'; +import modalFactory, { showAlertModal } from '../global/modal'; import _ from 'lodash'; -import swal from 'sweetalert2'; import Wishlist from '../wishlist'; export default class ProductDetails { @@ -370,10 +368,7 @@ export default class ProductDetails { const tmp = document.createElement('DIV'); tmp.innerHTML = errorMessage; - return swal({ - text: tmp.textContent || tmp.innerText, - type: 'error', - }); + return showAlertModal(tmp.textContent || tmp.innerText); } // Open preview modal and update content diff --git a/assets/js/theme/common/select-option-plugin.js b/assets/js/theme/common/select-option-plugin.js index 63c6d6c9b2..4723a0cb4e 100644 --- a/assets/js/theme/common/select-option-plugin.js +++ b/assets/js/theme/common/select-option-plugin.js @@ -1,5 +1,3 @@ -import $ from 'jquery'; - /** * Visually hides the option from user by moving option to an invisible * and disabled select placeholder element. diff --git a/assets/js/theme/common/state-country.js b/assets/js/theme/common/state-country.js index 367a0927a5..f6b5b6b8d8 100644 --- a/assets/js/theme/common/state-country.js +++ b/assets/js/theme/common/state-country.js @@ -1,8 +1,7 @@ -import $ from 'jquery'; import utils from '@bigcommerce/stencil-utils'; import _ from 'lodash'; import { insertStateHiddenField } from './form-utils'; -import swal from 'sweetalert2'; +import { showAlertModal } from '../global/modal'; /** * If there are no options from bcapp, a text field will be sent. This will create a select element to hold options after the remote request. @@ -130,11 +129,7 @@ export default function (stateElement, context = {}, options, callback) { utils.api.country.getByName(countryName, (err, response) => { if (err) { - swal({ - text: context.state_error, - type: 'error', - }); - + showAlertModal(context.state_error); return callback(err); } diff --git a/assets/js/theme/common/url-utils.js b/assets/js/theme/common/url-utils.js index a23b95ca57..550706c616 100644 --- a/assets/js/theme/common/url-utils.js +++ b/assets/js/theme/common/url-utils.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import Url from 'url'; const urlUtils = { diff --git a/assets/js/theme/compare.js b/assets/js/theme/compare.js index 679b53be6a..e962008126 100644 --- a/assets/js/theme/compare.js +++ b/assets/js/theme/compare.js @@ -1,17 +1,16 @@ import PageManager from './page-manager'; -import $ from 'jquery'; -import swal from 'sweetalert2'; +import { showAlertModal } from './global/modal'; +import compareProducts from './global/compare-products'; export default class Compare extends PageManager { onReady() { + compareProducts(this.context.urls); + const message = this.context.compareRemoveMessage; $('body').on('click', '[data-comparison-remove]', event => { if (this.context.comparisons.length <= 2) { - swal({ - text: message, - type: 'error', - }); + showAlertModal(message); event.preventDefault(); } }); diff --git a/assets/js/theme/contact-us.js b/assets/js/theme/contact-us.js index 383c464bf9..c32d4c1b0f 100644 --- a/assets/js/theme/contact-us.js +++ b/assets/js/theme/contact-us.js @@ -1,6 +1,5 @@ import PageManager from './page-manager'; import nod from './common/nod'; -import $ from 'jquery'; import forms from './common/models/forms'; export default class ContactUs extends PageManager { diff --git a/assets/js/theme/errors.js b/assets/js/theme/errors.js deleted file mode 100644 index 0d7552eb47..0000000000 --- a/assets/js/theme/errors.js +++ /dev/null @@ -1,3 +0,0 @@ -import PageManager from './page-manager'; - -export default class Errors extends PageManager {} diff --git a/assets/js/theme/gift-certificate.js b/assets/js/theme/gift-certificate.js index d039db741b..12f19bc2b6 100644 --- a/assets/js/theme/gift-certificate.js +++ b/assets/js/theme/gift-certificate.js @@ -1,5 +1,4 @@ import PageManager from './page-manager'; -import $ from 'jquery'; import nod from './common/nod'; import giftCertChecker from './common/gift-certificate-validator'; import formModel from './common/models/forms'; diff --git a/assets/js/theme/global.js b/assets/js/theme/global.js index 20b44159cb..6bf9d0d061 100644 --- a/assets/js/theme/global.js +++ b/assets/js/theme/global.js @@ -1,4 +1,4 @@ -import $ from 'jquery'; +import './global/jquery-migrate'; import './common/select-option-plugin'; import PageManager from './page-manager'; import quickSearch from './global/quick-search'; @@ -8,13 +8,11 @@ import menu from './global/menu'; import foundation from './global/foundation'; import quickView from './global/quick-view'; import cartPreview from './global/cart-preview'; -import compareProducts from './global/compare-products'; import privacyCookieNotification from './global/cookieNotification'; import maintenanceMode from './global/maintenanceMode'; import carousel from './common/carousel'; import 'lazysizes'; import loadingProgressBar from './global/loading-progress-bar'; -import sweetAlert from './global/sweet-alert'; import svgInjector from './global/svg-injector'; export default class Global extends PageManager { @@ -29,14 +27,12 @@ export default class Global extends PageManager { currencySelector(); foundation($(document)); quickView(this.context); - compareProducts(this.context.urls); carousel(); menu(); mobileMenuToggle(); privacyCookieNotification(); maintenanceMode(this.context.maintenanceMode); loadingProgressBar(); - sweetAlert(); svgInjector(); } } diff --git a/assets/js/theme/global/cart-preview.js b/assets/js/theme/global/cart-preview.js index 9820f62b28..881a4bbe8b 100644 --- a/assets/js/theme/global/cart-preview.js +++ b/assets/js/theme/global/cart-preview.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import 'foundation-sites/js/foundation/foundation'; import 'foundation-sites/js/foundation/foundation.dropdown'; import utils from '@bigcommerce/stencil-utils'; diff --git a/assets/js/theme/global/compare-products.js b/assets/js/theme/global/compare-products.js index 57315dc104..0af1fe045c 100644 --- a/assets/js/theme/global/compare-products.js +++ b/assets/js/theme/global/compare-products.js @@ -1,6 +1,5 @@ -import $ from 'jquery'; import _ from 'lodash'; -import swal from 'sweetalert2'; +import { showAlertModal } from './modal'; function decrementCounter(counter, item) { const index = counter.indexOf(item); @@ -58,10 +57,7 @@ export default function (urlContext) { const productsToCompare = $this.find('input[name="products\[\]"]:checked'); if (productsToCompare.length <= 1) { - swal({ - text: 'You must select at least two products to compare', - type: 'error', - }); + showAlertModal('You must select at least two products to compare'); event.preventDefault(); } }); @@ -70,11 +66,7 @@ export default function (urlContext) { const $clickedCheckedInput = $('body').find('input[name="products\[\]"]:checked'); if ($clickedCheckedInput.length <= 1) { - swal({ - text: 'You must select at least two products to compare', - type: 'error', - }); - + showAlertModal('You must select at least two products to compare'); return false; } }); diff --git a/assets/js/theme/global/cookieNotification.js b/assets/js/theme/global/cookieNotification.js index 31cfb0e490..45348714d0 100644 --- a/assets/js/theme/global/cookieNotification.js +++ b/assets/js/theme/global/cookieNotification.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import utils from '@bigcommerce/stencil-utils'; /** diff --git a/assets/js/theme/global/currency-selector.js b/assets/js/theme/global/currency-selector.js index ef3b5fa176..1f093226e8 100644 --- a/assets/js/theme/global/currency-selector.js +++ b/assets/js/theme/global/currency-selector.js @@ -1,5 +1,3 @@ -import $ from 'jquery'; - export default function () { $(document.body).on('click', '.currencySelector', () => { $('.currency-selection-list').toggleClass('active'); diff --git a/assets/js/theme/global/jquery-migrate.js b/assets/js/theme/global/jquery-migrate.js new file mode 100644 index 0000000000..a074d7f75c --- /dev/null +++ b/assets/js/theme/global/jquery-migrate.js @@ -0,0 +1,12 @@ +// Needed because we use Foundation 5.5, which expects jQuery 2.x. However, +// rather than bringing in all of jquery-migrate, we're cherry picking individual +// fixes needed for Foundation. +import init from './jquery-migrate/init'; +import traversing from './jquery-migrate/traversing'; +import data from './jquery-migrate/data'; +import events from './jquery-migrate/event'; + +init(); +traversing(); +data(); +events(); diff --git a/assets/js/theme/global/jquery-migrate/data.js b/assets/js/theme/global/jquery-migrate/data.js new file mode 100644 index 0000000000..8072997b31 --- /dev/null +++ b/assets/js/theme/global/jquery-migrate/data.js @@ -0,0 +1,41 @@ +// From https://github.com/jquery/jquery-migrate/blob/master/src/data.js +// +// https://jquery.com/upgrade-guide/3.0/#data + +/* eslint-disable prefer-rest-params */ +export default function () { + const oldData = jQuery.data; + jQuery.data = function (elem, name, value) { + let curData; + + // Name can be an object, and each entry in the object is meant to be set as data + if (name && typeof name === 'object' && arguments.length === 2) { + curData = jQuery.hasData(elem) && oldData.call(this, elem); + const sameKeys = {}; + for (const key in name) { + if (key !== jQuery.camelCase(key)) { + curData[key] = name[key]; + } else { + sameKeys[key] = name[key]; + } + } + + oldData.call(this, elem, sameKeys); + return name; + } + + // If the name is transformed, look for the un-transformed name in the data object + if (name && typeof name === 'string' && name !== jQuery.camelCase(name)) { + curData = jQuery.hasData(elem) && oldData.call(this, elem); + if (curData && name in curData) { + if (arguments.length > 2) { + curData[name] = value; + } + return curData[name]; + } + } + + return oldData.apply(this, arguments); + }; +} +/* eslint-enable prefer-rest-params */ diff --git a/assets/js/theme/global/jquery-migrate/event.js b/assets/js/theme/global/jquery-migrate/event.js new file mode 100644 index 0000000000..1213327c9e --- /dev/null +++ b/assets/js/theme/global/jquery-migrate/event.js @@ -0,0 +1,35 @@ +// From https://github.com/jquery/jquery-migrate/blob/master/src/event.js +// +// https://jquery.com/upgrade-guide/3.0/#breaking-change-load-unload-and-error-removed + +/* eslint-disable prefer-rest-params */ +export default function () { + jQuery.each(['load', 'unload', 'error'], (_, name) => { + const oldLoad = jQuery.fn.load; + + jQuery.fn[name] = function handler() { + const args = Array.prototype.slice.call(arguments, 0); + + // If this is an ajax load() the first arg should be the string URL; + // technically this could also be the "Anything" arg of the event .load() + // which just goes to show why this dumb signature has been deprecated! + // jQuery custom builds that exclude the Ajax module justifiably die here. + if (name === 'load' && typeof args[0] === 'string') { + return oldLoad.apply(this, args); + } + + args.splice(0, 0, name); + if (arguments.length) { + return this.on.apply(this, args); + } + + // Use .triggerHandler here because: + // - load and unload events don't need to bubble, only applied to window or image + // - error event should not bubble to window, although it does pre-1.7 + // See http://bugs.jquery.com/ticket/11820 + this.triggerHandler.apply(this, args); + return this; + }; + }); +} +/* eslint-enable prefer-rest-params */ diff --git a/assets/js/theme/global/jquery-migrate/init.js b/assets/js/theme/global/jquery-migrate/init.js new file mode 100644 index 0000000000..c3b307da88 --- /dev/null +++ b/assets/js/theme/global/jquery-migrate/init.js @@ -0,0 +1,29 @@ +// From https://github.com/jquery/jquery-migrate/blob/1.x-stable/src/core.js +// +// https://jquery.com/upgrade-guide/3.0/#breaking-change-deprecated-context-and-selector-properties-removed + +/* eslint-disable prefer-rest-params */ +export default function () { + const oldInit = jQuery.fn.init; + + jQuery.fn.init = function (selector, context) { + const ret = oldInit.apply(this, arguments); + + // Fill in selector and context properties so .live() works + if (selector && selector.selector !== undefined) { + // A jQuery object, copy its properties + ret.selector = selector.selector; + ret.context = selector.context; + } else { + ret.selector = typeof selector === 'string' ? selector : ''; + if (selector) { + ret.context = selector.nodeType ? selector : context || document; + } + } + + return ret; + }; + + jQuery.fn.init.prototype = jQuery.fn; +} +/* eslint-enable prefer-rest-params */ diff --git a/assets/js/theme/global/jquery-migrate/traversing.js b/assets/js/theme/global/jquery-migrate/traversing.js new file mode 100644 index 0000000000..995a8e699a --- /dev/null +++ b/assets/js/theme/global/jquery-migrate/traversing.js @@ -0,0 +1,16 @@ +// From https://github.com/jquery/jquery-migrate/blob/1.x-stable/src/traversing.js +// +// https://jquery.com/upgrade-guide/3.0/#breaking-change-deprecated-context-and-selector-properties-removed + +/* eslint-disable prefer-rest-params */ +export default function () { + const oldFnFind = jQuery.fn.find; + + jQuery.fn.find = function (selector) { + const ret = oldFnFind.apply(this, arguments); + ret.context = this.context; + ret.selector = this.selector ? `${this.selector} ${selector}` : selector; + return ret; + }; +} +/* eslint-enable prefer-rest-params */ diff --git a/assets/js/theme/global/loading-progress-bar.js b/assets/js/theme/global/loading-progress-bar.js index 5327a7f595..f357438bb7 100644 --- a/assets/js/theme/global/loading-progress-bar.js +++ b/assets/js/theme/global/loading-progress-bar.js @@ -1,8 +1,41 @@ +import Nanobar from 'nanobar'; + export default function () { - require('pace').start({ - document: false, - ajax: { - trackMethods: ['GET', 'POST'], - }, + // Create the nanobar instance + const nanobar = new Nanobar(); + + // Timer for moving progress bar during ajax calls + let timer = null; + let current = 0; + + function clearTimer() { + if (timer) { + clearInterval(timer); + timer = null; + } + } + + function setTimer() { + clearTimer(); + + current = 0; + timer = setInterval(() => { + current += 3; + if (current <= 100) { + nanobar.go(current); + } else { + clearTimer(); + } + }, 50); + } + + // Attach global jquery handlers to listen for ajax start + $(document).ajaxSend(() => { + setTimer(); + }); + + $(document).ajaxComplete(() => { + nanobar.go(100); + clearTimer(); }); } diff --git a/assets/js/theme/global/maintenanceMode.js b/assets/js/theme/global/maintenanceMode.js index fcfd4009e3..40c583ba55 100644 --- a/assets/js/theme/global/maintenanceMode.js +++ b/assets/js/theme/global/maintenanceMode.js @@ -1,5 +1,3 @@ -import $ from 'jquery'; - /** * Show the maintenance mode message to store administrators * @param maintenanceMode diff --git a/assets/js/theme/global/menu.js b/assets/js/theme/global/menu.js index 2c60189f3e..0187215c50 100644 --- a/assets/js/theme/global/menu.js +++ b/assets/js/theme/global/menu.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import collapsibleFactory from '../common/collapsible'; import collapsibleGroupFactory from '../common/collapsible-group'; diff --git a/assets/js/theme/global/mobile-menu-toggle.js b/assets/js/theme/global/mobile-menu-toggle.js index 9e71651e88..a9f5b825f8 100644 --- a/assets/js/theme/global/mobile-menu-toggle.js +++ b/assets/js/theme/global/mobile-menu-toggle.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import _ from 'lodash'; import mediaQueryListFactory from '../common/media-query-list'; import { CartPreviewEvents } from './cart-preview'; diff --git a/assets/js/theme/global/modal.js b/assets/js/theme/global/modal.js index fa95f275d3..a3fffbb840 100644 --- a/assets/js/theme/global/modal.js +++ b/assets/js/theme/global/modal.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import foundation from './foundation'; const bodyActiveClass = 'has-activeModal'; @@ -245,7 +244,21 @@ export default function modalFactory(selector = '[data-reveal]', options = {}) { * Return the default page modal */ export function defaultModal() { - const modal = modalFactory('#modal')[0]; + return modalFactory('#modal')[0]; +} - return modal; +/* + * Return the default alert modal + */ +export function alertModal() { + return modalFactory('#alert-modal')[0]; +} + +/* + * Display the given message in the default alert modal + */ +export function showAlertModal(message) { + const modal = alertModal(); + modal.open(); + modal.updateContent(`${message}`); } diff --git a/assets/js/theme/global/quick-search.js b/assets/js/theme/global/quick-search.js index 11615f8db2..08b94c7544 100644 --- a/assets/js/theme/global/quick-search.js +++ b/assets/js/theme/global/quick-search.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import _ from 'lodash'; import utils from '@bigcommerce/stencil-utils'; import StencilDropDown from './stencil-dropdown'; diff --git a/assets/js/theme/global/quick-view.js b/assets/js/theme/global/quick-view.js index 935ee487c7..21ea8aaf63 100644 --- a/assets/js/theme/global/quick-view.js +++ b/assets/js/theme/global/quick-view.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import 'foundation-sites/js/foundation/foundation'; import 'foundation-sites/js/foundation/foundation.dropdown'; import utils from '@bigcommerce/stencil-utils'; diff --git a/assets/js/theme/global/reveal-close.js b/assets/js/theme/global/reveal-close.js index 1bf7214e20..c68955af81 100644 --- a/assets/js/theme/global/reveal-close.js +++ b/assets/js/theme/global/reveal-close.js @@ -1,5 +1,3 @@ -import $ from 'jquery'; - const revealCloseAttr = 'revealClose'; const revealCloseSelector = `[data-${revealCloseAttr}]`; const revealSelector = '[data-reveal]'; diff --git a/assets/js/theme/global/stencil-dropdown.js b/assets/js/theme/global/stencil-dropdown.js index f47b3394ab..0fa4504340 100644 --- a/assets/js/theme/global/stencil-dropdown.js +++ b/assets/js/theme/global/stencil-dropdown.js @@ -1,5 +1,3 @@ -import $ from 'jquery'; - export default class StencilDropdown { constructor(extendables) { this.extendables = extendables; diff --git a/assets/js/theme/global/sweet-alert.js b/assets/js/theme/global/sweet-alert.js index 92d0eea5cc..8711d1cf3c 100644 --- a/assets/js/theme/global/sweet-alert.js +++ b/assets/js/theme/global/sweet-alert.js @@ -1,10 +1,11 @@ -import swal from 'sweetalert2'; +import sweetAlert from 'sweetalert2'; -export default function () { - // Set defaults for sweetalert2 popup boxes - swal.setDefaults({ - buttonsStyling: false, - confirmButtonClass: 'button', - cancelButtonClass: 'button', - }); -} +// Set defaults for sweetalert2 popup boxes +sweetAlert.setDefaults({ + buttonsStyling: false, + confirmButtonClass: 'button', + cancelButtonClass: 'button', +}); + +// Re-export +export default sweetAlert; diff --git a/assets/js/theme/global/text-truncate.js b/assets/js/theme/global/text-truncate.js index 326dad2c7e..eb5b8e5d72 100644 --- a/assets/js/theme/global/text-truncate.js +++ b/assets/js/theme/global/text-truncate.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import _ from 'lodash'; export default class TextTruncate { diff --git a/assets/js/theme/home.js b/assets/js/theme/home.js deleted file mode 100644 index 4ff5c26368..0000000000 --- a/assets/js/theme/home.js +++ /dev/null @@ -1,3 +0,0 @@ -import PageManager from './page-manager'; - -export default class Home extends PageManager {} diff --git a/assets/js/theme/page-manager.js b/assets/js/theme/page-manager.js index 29060a8d2a..e4a957a5dd 100644 --- a/assets/js/theme/page-manager.js +++ b/assets/js/theme/page-manager.js @@ -1,5 +1,3 @@ -import $ from 'jquery'; - export default class PageManager { constructor(context) { this.context = context; diff --git a/assets/js/theme/page.js b/assets/js/theme/page.js deleted file mode 100644 index aaa6ecfce6..0000000000 --- a/assets/js/theme/page.js +++ /dev/null @@ -1,3 +0,0 @@ -import PageManager from './page-manager'; - -export default class Page extends PageManager {} diff --git a/assets/js/theme/product.js b/assets/js/theme/product.js index 84aa74e1be..7c5162f450 100644 --- a/assets/js/theme/product.js +++ b/assets/js/theme/product.js @@ -1,7 +1,6 @@ /* Import all product specific js */ -import $ from 'jquery'; import PageManager from './page-manager'; import Review from './product/reviews'; import collapsibleFactory from './common/collapsible'; diff --git a/assets/js/theme/product/image-gallery.js b/assets/js/theme/product/image-gallery.js index ae833eb11b..6c2f4d5759 100644 --- a/assets/js/theme/product/image-gallery.js +++ b/assets/js/theme/product/image-gallery.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import 'easyzoom'; import _ from 'lodash'; diff --git a/assets/js/theme/product/reviews.js b/assets/js/theme/product/reviews.js index 65ac5d942a..10bd2f1dc7 100644 --- a/assets/js/theme/product/reviews.js +++ b/assets/js/theme/product/reviews.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import nod from '../common/nod'; import { CollapsibleEvents } from '../common/collapsible'; import forms from '../common/models/forms'; diff --git a/assets/js/theme/product/video-gallery.js b/assets/js/theme/product/video-gallery.js index bdac41a127..2351ef28da 100644 --- a/assets/js/theme/product/video-gallery.js +++ b/assets/js/theme/product/video-gallery.js @@ -1,5 +1,3 @@ -import $ from 'jquery'; - export class VideoGallery { constructor($element) { this.$player = $element.find('[data-video-player]'); diff --git a/assets/js/theme/rss.js b/assets/js/theme/rss.js deleted file mode 100644 index fe66ff779c..0000000000 --- a/assets/js/theme/rss.js +++ /dev/null @@ -1,4 +0,0 @@ -import PageManager from './page-manager'; - -export default class RSS extends PageManager {} - diff --git a/assets/js/theme/search.js b/assets/js/theme/search.js index 5484d671fb..c7fa3d78a6 100644 --- a/assets/js/theme/search.js +++ b/assets/js/theme/search.js @@ -1,7 +1,7 @@ import { hooks } from '@bigcommerce/stencil-utils'; import CatalogPage from './catalog'; -import $ from 'jquery'; import FacetedSearch from './common/faceted-search'; +import compareProducts from './global/compare-products'; import urlUtils from './common/url-utils'; import Url from 'url'; import collapsibleFactory from './common/collapsible'; @@ -70,6 +70,8 @@ export default class Search extends CatalogPage { } onReady() { + compareProducts(this.context.urls); + const $searchForm = $('[data-advanced-search-form]'); const $categoryTreeContainer = $searchForm.find('[data-search-category-tree]'); const url = Url.parse(window.location.href, true); diff --git a/assets/js/theme/sitemap.js b/assets/js/theme/sitemap.js deleted file mode 100644 index ed2a5468da..0000000000 --- a/assets/js/theme/sitemap.js +++ /dev/null @@ -1,3 +0,0 @@ -import PageManager from './page-manager'; - -export default class SiteMap extends PageManager {} diff --git a/assets/js/theme/subscribe.js b/assets/js/theme/subscribe.js deleted file mode 100644 index 46a9ac34b5..0000000000 --- a/assets/js/theme/subscribe.js +++ /dev/null @@ -1,3 +0,0 @@ -import PageManager from './page-manager'; - -export default class Subscribe extends PageManager {} diff --git a/assets/js/theme/wishlist.js b/assets/js/theme/wishlist.js index 62bef80d73..f988c6de00 100644 --- a/assets/js/theme/wishlist.js +++ b/assets/js/theme/wishlist.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import 'foundation-sites/js/foundation/foundation'; import 'foundation-sites/js/foundation/foundation.reveal'; import nod from './common/nod'; diff --git a/assets/scss/components/_components.scss b/assets/scss/components/_components.scss index e6353b6e01..2465213c8e 100644 --- a/assets/scss/components/_components.scss +++ b/assets/scss/components/_components.scss @@ -9,8 +9,8 @@ // Slick carousel @import "vendor/slick/component"; -// Pace Ajax loading progress bar -@import "vendor/pace/component"; +// Nanobar Ajax loading progress bar +@import "vendor/nanobar/component"; // SweetAlert2 replacement for JavaScript alert/confirmations @import "vendor/sweetalert2/component"; diff --git a/assets/scss/components/foundation/modal/_modal.scss b/assets/scss/components/foundation/modal/_modal.scss index 68b42a0a89..e80e134df8 100644 --- a/assets/scss/components/foundation/modal/_modal.scss +++ b/assets/scss/components/foundation/modal/_modal.scss @@ -4,8 +4,6 @@ .modal { - // scss-lint:disable ImportantRule - left: 50%; margin: 0 auto; max-height: 90%; max-width: 95%; @@ -13,9 +11,6 @@ outline: none; overflow: hidden; padding: 0; - right: 0; - top: 50% !important; - transform: translate(-50%, -50%); } .modal--large { @@ -63,3 +58,38 @@ -webkit-overflow-scrolling: touch; padding: $reveal-modal-padding; } + +.modal--alert { + background-color: $alert-modal-bgColor; + border-radius: $alert-modal-borderRadius; + font-family: $alert-font-body; + padding: 40px 20px 20px 20px; + text-align: center; + + .modal-content { + color: $alert-font-bodyColor; + font-size: $alert-font-bodySize; + } + + .button-container { + margin: $alert-button-wrapperMargin; + + .confirm { + background-color: $alert-button-bgColor; + border-color: $alert-button-borderColor; + color: $alert-button-color; + } + + .confirm:focus, .confirm:hover { + background-color: $alert-button-bgColorHover; + border-color: $alert-button-borderColorHover; + color: $alert-button-colorHover; + } + + .confirm:active { + background-color: $alert-button-bgColorActive; + border-color: $alert-button-borderColorActive; + color: $alert-button-colorActive; + } + } +} diff --git a/assets/scss/components/vendor/pace/_component.scss b/assets/scss/components/vendor/nanobar/_component.scss similarity index 75% rename from assets/scss/components/vendor/pace/_component.scss rename to assets/scss/components/vendor/nanobar/_component.scss index efdfb19b0c..4d8d04f4b5 100644 --- a/assets/scss/components/vendor/pace/_component.scss +++ b/assets/scss/components/vendor/nanobar/_component.scss @@ -1,5 +1,5 @@ // ============================================================================= -// PACE - AJAX LOADING PROGRESS +// NANOBAR - AJAX LOADING PROGRESS // ============================================================================= -@import "pace"; +@import "nanobar"; diff --git a/assets/scss/components/vendor/pace/_pace.scss b/assets/scss/components/vendor/nanobar/_nanobar.scss similarity index 54% rename from assets/scss/components/vendor/pace/_pace.scss rename to assets/scss/components/vendor/nanobar/_nanobar.scss index 1305fe96a0..c888ae375c 100644 --- a/assets/scss/components/vendor/pace/_pace.scss +++ b/assets/scss/components/vendor/nanobar/_nanobar.scss @@ -1,22 +1,19 @@ // ============================================================================= -// PACE - AJAX LOADING PROGRESS +// NANOBAR - AJAX LOADING PROGRESS // ============================================================================= -.pace { +.nanobar { + width: 100%; + height: remCalc(5px); + z-index: 9999; + top: 0; pointer-events: none; user-select: none; -} -.pace-inactive { - display: none; -} - -.pace-progress { - background-color: stencilColor("pace-progress-backgroundColor"); - height: remCalc(5px); - position: fixed; - right: 100%; - top: 0; - width: 100%; - z-index: 2000; + .bar { + width: 0; + height: 100%; + transition: height .1s; + background-color: stencilColor("pace-progress-backgroundColor"); + } } diff --git a/lighthouse-config.js b/lighthouse-config.js new file mode 100644 index 0000000000..acb2a0a4bd --- /dev/null +++ b/lighthouse-config.js @@ -0,0 +1,46 @@ +const throttling = { + disabled: { + rttMs: 0, + throughputKbps: 0, + requestLatencyMs: 0, + downloadThroughputKbps: 0, + uploadThroughputKbps: 0, + cpuSlowdownMultiplier: 0, + } +}; + +module.exports = { + settings: { + output: 'json', + maxWaitForLoad: 45 * 1000, + throttlingMethod: 'provided', + throttling: throttling.disabled, + auditMode: false, + gatherMode: false, + disableStorageReset: false, + disableDeviceEmulation: true, + emulatedFormFactor: 'none', + blockedUrlPatterns: null, + additionalTraceCategories: null, + extraHeaders: null, + onlyAudits: null, + onlyCategories: null, + skipAudits: null, + }, + passes: [ + { + passName: 'defaultPass', + recordTrace: true, + useThrottling: false, + pauseAfterLoadMs: 1000, + networkQuietThresholdMs: 1000, + cpuQuietThresholdMs: 1000, + gatherers: [], + }, + ], + audits: [ + 'time-to-first-byte', + 'metrics/first-meaningful-paint', + 'metrics/interactive', + ], +}; diff --git a/package.json b/package.json index 9d3fe4a6c3..00891ad7d8 100644 --- a/package.json +++ b/package.json @@ -6,42 +6,37 @@ "author": "BigCommerce", "license": "MIT", "dependencies": { + "@babel/polyfill": "^7.0.0", "@bigcommerce/stencil-utils": "^4.0.0", - "babel-polyfill": "^6.26.0", "creditcards": "^3.0.1", "easyzoom": "^2.5.0", "foundation-sites": "^5.5.3", "jquery": "^3.3.1", - "jquery-migrate": "^1.4.1", "jstree": "vakata/jstree", "lazysizes": "4.1.2", "lodash": "^4.17.4", + "nanobar": "^0.4.2", "nod-validate": "^2.0.12", - "pace": "hubspot/pace#a03f1f1de62c9cea6c88b2267b8d7a83858b6cb6", "slick-carousel": "^1.8.1", "svg-injector": "^1.1.3", - "sweetalert2": "^6.10.1" + "sweetalert2": "^6.11.5" }, "devDependencies": { + "@babel/core": "^7.2.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0", + "@babel/preset-env": "^7.2.0", "@bigcommerce/citadel": "^2.15.1", - "babel-core": "^6.26.0", - "babel-eslint": "^8.2.2", - "babel-loader": "^7.1.4", - "babel-plugin-dynamic-import-webpack": "^1.0.2", - "babel-plugin-lodash": "^3.3.2", - "babel-plugin-syntax-dynamic-import": "^6.18.0", - "babel-plugin-transform-regenerator": "^6.26.0", - "babel-plugin-transform-runtime": "^6.23.0", - "babel-preset-env": "^1.6.1", + "@bigcommerce/stencil-cli": "^1.15.4", + "babel-loader": "^8.0.4", + "babel-plugin-lodash": "^3.3.4", "clean-webpack-plugin": "^0.1.19", - "core-js": "^2.5.0", - "es6-shim": "^0.35.3", "eslint": "^4.8.0", "eslint-config-airbnb": "^16.0.0", "eslint-plugin-import": "^2.7.0", "eslint-plugin-jsx-a11y": "^6.0.2", "eslint-plugin-react": "^7.4.0", "grunt": "^1.0.1", + "grunt-cli": "^1.3.2", "grunt-eslint": "^20.0.0", "grunt-karma": "^2.0.0", "grunt-scss-lint": "^0.5.0", @@ -49,7 +44,7 @@ "imports-loader": "^0.7.1", "jasmine-core": "^2.2.0", "karma": "^1.7.0", - "karma-babel-preprocessor": "^7.0.0", + "karma-babel-preprocessor": "^8.0.0-beta.0", "karma-coverage": "^1.1.1", "karma-es6-shim": "^1.0.0", "karma-jasmine": "^1.1.0", @@ -57,16 +52,19 @@ "karma-sourcemap-loader": "0.3.7", "karma-verbose-reporter": "0.0.6", "karma-webpack": "^2.0.4", + "lighthouse": "^4.0.0-alpha.2-3.2.1", "load-grunt-config": "^0.19.2", "lodash-webpack-plugin": "^0.11.2", - "regenerator-runtime": "^0.11.0", + "npx": "^10.2.0", "time-grunt": "^1.2.2", - "webpack": "^4.1.1", - "webpack-cli": "^2.0.11", + "webpack": "^4.27.1", + "webpack-bundle-analyzer": "^3.0.3", + "webpack-cli": "^3.1.2", "webpack-merge": "^4.1.2" }, "scripts": { - "build": "webpack --config webpack.prod.js", - "buildDev": "webpack --config webpack.dev.js" + "build": "npx webpack --config webpack.prod.js", + "buildDev": "npx webpack --config webpack.dev.js", + "lighthouse": "npx lighthouse --config-path=lighthouse-config.js --quiet --output json --chrome-flags=\"--headless\" $URL | jq '.audits | map_values(.rawValue)'" } } diff --git a/templates/components/common/alert-modal.html b/templates/components/common/alert-modal.html new file mode 100644 index 0000000000..a6028624b9 --- /dev/null +++ b/templates/components/common/alert-modal.html @@ -0,0 +1,7 @@ + diff --git a/templates/components/common/body.html b/templates/components/common/body.html index b5f12e4371..afb89a64d6 100644 --- a/templates/components/common/body.html +++ b/templates/components/common/body.html @@ -4,4 +4,5 @@ {{#block "page"}} {{/block}} {{> components/common/modal}} + {{> components/common/alert-modal}} diff --git a/templates/layout/base.html b/templates/layout/base.html index 4ea2166849..639a5b9c6e 100644 --- a/templates/layout/base.html +++ b/templates/layout/base.html @@ -43,7 +43,6 @@ -