brandlabs-bigcommerce-wishlist
v0.1.0
Published
Easy-to-use wishlist functionality for BigCommerce themes.
Downloads
12
Readme
BigCommerce Wishlist
Easy-to-use Wishlist functionality for BigCommerce themes.
This package provides simple JavaScript methods to:
- Check if a product has already been added to a wishlist.
- Add or remove a product from a wishlist.
- Create, update or remove wishlists.
- Get wishlists details.
- Event listeners for changes on wishlists.
In this document:
- Info
- Setup
- :point_right: API Reference :point_left:
- Maintenance
- License
Info
- URL: https://github.com/brandlabs/bigcommerce-wishlist
- Platform: BigCommerce
Setup
Install package
Install the package as a dependency of your Stencil theme:
npm install brandlabs-bigcommerce-wishlist --save-dev
Required template
The post install script will create a Handlebars template file named json-this.html
in your Stencil theme at templates/components/custom
folder.
This template file is required for the package to work
Use it in theme
- Import the package and you'll get a
WishlistManager
instance. - Provide
context
to theinit
method. (optional) - Use the available methods.
import wishlistManager from 'brandlabs-bigcommerce-wishlist';
export default class ProductDetails {
constructor($scope, context, productAttributesData = {}) {
// ...
this.showAddedToWishlist();
}
async showAddedToWishlist() {
// Initialize
await wishlistManager.init(this.context);
const productId = $('input[name="product_id"]', this.$scope).val();
// Check if product belongs to one of the customer's wishlists
const isAddedToWishlist = await wishlistManager.isProductInAnyWishlist(productId);
if (isAddedToWishlist) {
$('.added-to-wishlist').show();
} else {
$('.added-to-wishlist').hide();
}
}
}
Try it yourself
To quickly try it from browser, simply add these two lines to assets/js/theme/global.js
:
// Append this line to the imports:
import wishlistManager from 'brandlabs-bigcommerce-wishlist';
export default class Global extends PageManager {
onReady() {
// Add this line so you can use the wishlistManager as "wm" from console:
window.wm = wishlistManager;
}
}
Perform login as a customer into the store, and the wm
(wishlist manager) functionality will be available from Dev Tools console to create, update and remove wishlists, add and remove products from wishlists, etc.
Use console.log
as argument to Promise's then
, to see the results:
wm.getAllWishlists().then(console.log)
ECMAScript 6
The source code from this package is written using ES6 features.
Default Webpack configuration from Cornerstone theme uses Babel to transpile ES6 code within assets/js
folder. It does not transpile ES6 code from node_modules
.
To get the transpiled code for this package in your theme bundle, there are three options.
Choose one of them:
- Import the transpiled version from
dist
folder:
import wishlistManager from 'brandlabs-bigcommerce-wishlist/dist/wishlist-manager.min.js';
- Include
brandlabs-bigcommerce-wishlist
in the paths to transpile. Atwebpack.common.js
, update theinclude
regular expression:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: /(assets\/js|assets\\js|stencil-utils|brandlabs-bigcommerce-wishlist)/,
- Use an alias for the
dist
version. Atwebpack.common.js
, add one entry for the wishlist package:
module.exports = {
resolve: {
alias: {
'brandlabs-bigcommerce-wishlist': path.resolve(__dirname, 'node_modules/brandlabs-bigcommerce-wishlist/dist/wishlist-manager.min.js'),
API Reference
The main functionality mimics the BigCommerce Wishlist API:
Most methods are asynchronous, returning a Promise which will resolve into the expected result, or reject upon an error.
METHODS
- getAllWishlists - Get all wishlists for the current customer.
- getWishlist - Get specified wishlist details for the current customer.
- createWishlist - Create a new wishlist for the current customer.
- deleteWishlist - Remove a wishlist from the current customer.
- updateWishlist - Update a wishlist for the current customer.
- addWishlistItem - Add a product to a customer wishlist.
- deleteWishlistItem - Remove a product from a customer wishlist.
- getAllWishlistsDetails - Get all wishlists for the current customer, including the list of items for each one of them.
- isProductInAnyWishlist - Verify if a specific product belongs to any of the customer's wishlists.
- on - Register an event listener.
- off - Remove an event listener.
- init - Initialize the instance with optional context data.
PROPERTIES
- useCache - Whether to use the cache or not.
- cacheDuration - Time to expire cached data, in minutes.
- urls - Paths to the remote AJAX requests.
- events - Event names.
- wishlists - Wishlists data.
:warning: Note: In the examples below, we provide both one then
callback and one event listener, to illustrate how to use them. It is not necessary to use both in real applications. Please choose the option which fits better to your use case.
# async getAllWishlists(forceFetch = false)
Get all wishlists for the current customer.
Arguments:
{boolean} forceFetch
- True to ignore the cache, False otherwise. Defaults tofalse
.
Returns:
{Promise<array>} wishlists
Usage:
Each object in the returned array shall contain the following properties:
id
name
is_public
num_items
token
view_url
edit_url
delete_url
share_url
add_url
wm.getAllWishlists().then(wishlists => {
const totalNumberOfItems = wishlists.reduce((total, { num_items }) => total + num_items, 0);
console.log(`Total number of items in all wishlists is ${totalNumberOfItems}`);
});
# async getWishlist(wishlistid, forceFetch = false)
Get specified wishlist details for the current customer.
Arguments:
{integer} wishlistid
- Wishlist ID.{boolean} forceFetch
- True to ignore the cache, False otherwise. Defaults tofalse
.
Returns:
{Promise<object>} wishlist
Usage:
The returned object shall contain the following properties:
id
name
is_public
is_editable
items
token
share_url
Note that the properties returned from getWishlist
differ from the properties returned from getAllWishlists
.
This is not a "feature" of the package. This reflects what is actually returned from BigCommerce server.
While the wishlist objects returned from getAllWishlists
provide the number of items as num_items
, the object returned from getWishlist
contains an items
array with the actual items.
wm.getWishlist(9).then(({ name, items, is_public, share_url }) => {
console.log(`The "${name}" wishlist contains ${items.length} item${items.length === 1 ? '' : 's'}.`);
if (is_public) {
console.log(`It can be shared through this URL: ${share_url}`);
} else {
console.log(`It is a private list.`);
}
});
# async createWishlist({ name, is_public, product_id })
Create a new wishlist for the current customer.
Arguments:
{Object} params
- The wishlist attributes.{string} name
- Wishlist name (required).{boolean} is_public
- Indicates if wishlist is public. Defaults tofalse
.{integer} product_id
- ID of a product to be added to the wishlist (optional).
Returns:
{Promise<object>} wishlist
Fires:
createWishlist({ wishlist })
event
Usage:
The returned wishlist object varies:
- If no
product_id
is provided, then it will contain the same properties returned bygetAllWishlists
- If a
product_id
is provided, then it will contain the same properties returned bygetWishlist
The createWishlist
event is triggered. The event handler function is called immediately before the Promise is resolved.
wm.on('createWishlist', ({ wishlist }) => {
console.log(`Wishlist "${wishlist.name}" has been created.`);
});
wm.createWishlist({ name: 'Best Wishes' }).then(({ id }) => {
console.log(`The ID for the new wishlist is ${id}.`);
});
# async deleteWishlist(wishlistid)
Remove a wishlist from the current customer.
Arguments:
{integer} wishlistid
- Wishlist ID.
Returns:
{Promise<undefined>}
Fires:
deleteWishlist({ wishlistid })
event
Usage:
The deleteWishlist
event is triggered. The event handler function is called immediately before the Promise is resolved.
wm.on('deleteWishlist', ({ wishlistid }) => {
console.log(`Wishlist ID ${wishlistid} has been deleted.`);
});
wm.deleteWishlist(17).then(() => alert('Wishlist deleted!')).catch(error => {
console.log(`Something went wrong when trying to delete the wishlist.`);
});
# async updateWishlist(wishlistid, { name, is_public })
Update a wishlist for the current customer.
Arguments:
{integer} wishlistid
- Wishlist ID.{Object} params
- The wishlist attributes.{string} name
- Wishlist name (required).{boolean} is_public
- Indicates if wishlist is public (required).
Returns:
{Promise<object>} wishlist
Fires:
updateWishlist({ wishlist })
event
Usage:
Both arguments (name
and is_public
) will always be updated. Even if you want to change only one of them, and keep the other, you need to provide values for both name
and is_public
. Otherwise, the name
will be set to an empty string, and is_public
will be set to false.
The returned wishlist object contains the same properties returned in getAllWishlists
The updateWishlist
event is triggered. The event handler function is called immediately before the Promise is resolved.
wm.on('updateWishlist', ({ wishlist }) => {
console.log(`Wishlist ID "${wishlist.id}" has been updated.`);
});
wm.updateWishlist(9, { name: 'New Wishes', is_public: true }).then(({ name, is_public }) => {
console.log(`Updated wishlist attributes: name = "${name}", is_public = ${is_public ? 'true' : 'false'}.`);
});
# async addWishlistItem(wishlistid, product_id)
Add a product to a customer wishlist.
Arguments:
{integer} wishlistid
- Wishlist ID.{integer} product_id
- ID of a product to be added to the wishlist.
Returns:
{Promise<item>}
Fires:
addWishlistItem({ wishlist, item })
event
Usage:
The addWishlistItem
event is triggered. The event handler function is called immediately before the Promise is resolved.
The returned item
object contains several product properties, including product_id
, name
and sku
. It contains also its own id
.
wm.on('addWishlistItem', ({ wishlist, item }) => {
console.log(`Product "${item.name}" added to wishlist "${wishlist.name}".`);
});
wm.addWishlistItem(9, 15).then(item => {
console.log(item);
});
# async deleteWishlistItem(wishlistid, item_id)
Remove a product from a customer wishlist.
Arguments:
{integer} wishlistid
- Wishlist ID.{integer} item_id
- Wishlist item ID.
Returns:
{Promise<undefined>}
Fires:
deleteWishlistItem({ wishlistid, item_id })
event
Usage:
The deleteWishlistItem
event is triggered. The event handler function is called immediately before the Promise is resolved.
wm.on('deleteWishlistItem', ({ wishlistid, item_id }) => {
console.log(`Item ID ${item_id} from Wishlist ID ${wishlistid} has been removed.`);
const wishlist = wm.wishlists[wishlistid];
console.log(`Wishlist "${wishlist.name}" contains ${wishlist.num_items} items now.`);
});
wm.deleteWishlistItem(9, 23).then(() => alert('Wishlist item deleted!')).catch(error => {
console.log(`Something went wrong when trying to remove item from wishlist.`);
});
# async getAllWishlistsDetails()
Get all wishlists for the current customer, including the list of items for each one of them.
Returns:
{Promise<array>} wishlists
Usage:
Each object in the returned array shall contain the following properties:
id
name
is_public
is_editable
items
num_items
token
view_url
edit_url
delete_url
share_url
add_url
wm.getAllWishlistsDetails().then(wishlists => {
const totalNumberOfItems = wishlists.reduce((total, { items }) => total + items.length, 0);
console.log(`Total number of items in all wishlists is ${totalNumberOfItems}`);
});
# async isProductInAnyWishlist(product_id)
Verify if a specific product belongs to any of the customer's wishlists.
Returns:
{Promise<boolean>} belongs
Usage:
The Promise resolves to True if the specified product ID belongs to any wishlist, False otherwise.
const product_id = 15;
wm.isProductInAnyWishlist(product_id).then(wished => {
console.log(`Product ID ${product_id} is ${wished ? 'a wished product!' : 'not in any wishlist.'}`);
});
# on(name, callback)
Register an event listener.
Arguments:
{string} name
- The event name.{Function} callback
- The function to run when the event is triggered.
Usage:
All methods which changes the wishlists trigger events.
See the reference of each method for details on the provided event parameters:
const onCreateWishlist = ({ wishlist }) => {
console.log(`A new ${wishlist.is_public ? 'public' : 'private' } wishlist called "${wishlist.name}" has been created.`);
};
wm.on('createWishlist', onCreateWishlist);
# off(name, callback)
Remove an event listener.
Arguments:
{string} name
- The event name.{Function} callback
- The function to be removed from listeners.
Usage:
In order to be able to remove an event listener, it is important to have a reference to it.
So, if your code will remove event listeners, it is better to avoid anonymous functions when registering them.
// GOOD EXAMPLE:
const onCreateWishlist = ({ wishlist }) => {
console.log(`A new ${wishlist.is_public ? 'public' : 'private'} wishlist called "${wishlist.name}" has been created.`);
};
wm.on('createWishlist', onCreateWishlist);
wm.off('createWishlist', onCreateWishlist);
// AVOID USING ANONYMOUS FUNCTIONS:
wm.on('deleteWishlist', () => console.log('A wishlist has been removed.'));
wm.off('deleteWishlist', wm.eventListeners['deleteWishlist'][0]); // Not reliable. Not recommended.
# async init({ wishlists, urls } = {})
Initialize the instance with optional context data.
Arguments:
{Object} context
- Stencil context.{array} wishlists
- Wishlists.{object} urls
- URLs.
Usage:
The call to init
method is not mandatory. It is optional. The context
parameter is also optional.
This is exactly what init
does:
// Use wishlists URLs from context
if (urls && urls.account && urls.account.wishlists) {
Object.assign(this.urls, urls.account.wishlists);
}
// Cache warming
if (this.useCache) {
this.getAllWishlistsFromCache();
}
// Initial wishlists fetch, if not available in context
if (Array.isArray(wishlists)) {
this.setWishlists(wishlists);
} else {
await this.getAllWishlists(true);
}
:one: Stencil context usually contains wishlists URL information, at context.urls.account.wishlists
. The init
method will configure the instance to use the context URLs. This usually will make no difference at all, since the configured defaults correspond to the values used by all BigCommerce stores.
:two: Loads data from cache into the instance's wishlists
property.
:three: If a wishlists
array is provided in context
, it will be used as initial source of truth, populating the instance's wishlists
property data. Otherwise, an AJAX call to BigCommerce will be made to retrieve the initial wishlists
data.
Thus, this initial HTTP request to BigCommerce server can be avoided if wishlists
is injected into the context. This can be achieved from the Handlebars page templates in the theme. This is done through a front-matter entry, and the usage of Handlebars inject
helper, like this:
---
customer:
wishlists:
limit:10
---
{{inject "wishlists" customer.wishlists}}
# wm.useCache
Type:
boolean
Default:
true
Details: From the moment this is set to
false
, the cache will not be read anymore. If set totrue
, the cache will be available for reading. This setting does not affect the write operations to the cache.
// To completely skip reading cache, turn it off before calling init
wm.useCache = false;
wm.init(this.context);
# wm.cacheDuration
Type:
integer
Default:
60
Details: Time to expire cached data, in minutes. This does not imply on cache data deletion. If there is data stored in cache 90 minutes ago, it will be discarded when reading. In this case, if
cacheDuration
is modified to 120, the next cache read operations will not discard anumore the data which has been stored in cache 90 minutes ago.
// It is recommended to set the preferred value for cacheDuration before calling init
wm.cacheDuration = 360;
wm.init(this.context);
# wm.urls
Type:
object
Default:
{
base: window.location.origin,
all: '/wishlist.php',
add: '/wishlist.php?action=addwishlist',
edit: '/wishlist.php?action=editwishlist',
delete: '/wishlist.php?action=deletewishlist',
view: '/wishlist.php?action=viewwishlistitems',
add_item: '/wishlist.php?action=add',
delete_item: '/wishlist.php?action=remove',
share: '/wishlist.php?action=sharewishlist',
}
- Details: URLs used for the remote AJAX requests. They can be changed/configured.
// Use a test or proxy domain to receive the requests
wm.urls.base = 'https://example.com';
# wm.events
Type:
object
Default:
{
createWishlist: 'createWishlist',
deleteWishlist: 'deleteWishlist',
updateWishlist: 'updateWishlist',
addWishlistItem: 'addWishlistItem',
deleteWishlistItem: 'deleteWishlistItem',
}
- Details: Instead of using hard-coded strings, it is possible to refer the events from this object property. The event names can also be changed/configured.
wm.events.createWishlist = 'wishlistCreated';
wm.events.deleteWishlist = 'wishlistDeleted';
wm.on('wishlistCreated', () => 'A wishlist has been created.');
wm.on(wm.events.deleteWishlist, () => 'A wishlist has been deleted.');
wm.on(wm.events.updateWishlist, () => 'A wishlist has been updated.');
# wm.wishlists
Type:
object
Details: Contains all wishlist data read from cache, or from remote requests, or changed via any of the method calls. It always contains the most complete and up-to-date wishlists data the WishlistManager instance is aware of. It is a dictonary keyed by the wishlist ID.
async foo() {
await wm.getWishlist(5);
const productIds = wm.wishlists[5].items.map(item => item.product_id);
}
async bar() {
await wm.getAllWishlistsDetails();
console.log(wm.wishlists); // ALL data is in here
}
The wishlists
property is suitable to be used in a reactive context.
Here is a Vue JS 3 example:
wm.wishlists = Vue.reactive({});
wm.init();
Vue.createApp({
data: () => ({ wishlists: wm.wishlists }),
template: `
<table id="wm">
<tr><th>ID</th><th>Name</th><th>Shared</th><th>Items</th></tr>
<tr v-for="wishlist in wishlists">
<td>{{ wishlist.id }}</td>
<td>{{ wishlist.name }}</td>
<td>{{ wishlist.is_public ? 'public' : 'private' }}</td>
<td v-if="wishlist.items">{{ wishlist.items.map(item => item.name).join(', ') }}</td>
<td v-else>{{ wishlist.num_items }} item{{ wishlist.num_items === 1 ? '' : 's' }}</td>
</tr>
</table>`,
}).mount($('<div>').appendTo('body').get(0));
With this setup, any change done using the "create", "update", "delete", "add item" or "remove item" is automatically reflected in the HTML table, without any then
callback, any event listener or any other kind of post-processing.
Maintenance
For development of this package, we use:
- ESLint for linting JS code
- Jest for testing
- Webpack for bundling
Lint
To lint the scripts, run npm run lint
Test
To perform the tests, run npm run test
Bundle
To generate the dist
folder and build bundle assets, run npm run build
License
This project is licensed under the MIT License - see the LICENSE file for details