Examples
These examples show how Browser API can be leveraged to create custom widget implementations to display combos, bundles, and bundle builders.
Combos
const renderCombo = (containerSelector) => {
// Grab the API client, a location and a combo
const { client } = window.pickystory
const location = client.getLocations('combos').shift()
const combo = location.deals.shift()
// Find the designated slot in the page and add a title and product cards
const container = document.querySelector(containerSelector)
const header = document.createElement('h2')
header.textContent = 'COMBO'
container.append(header)
for (let product of combo.products) {
renderProductCard(product, container)
}
// Render the ATC button and specify its action
renderAddToCart(container, async (selections) => {
const variants = combo.getVariantsForSelections(selections)
await combo.addVariantsToCart(variants, 1)
})
}
Bundles
const renderBundle = (containerSelector) => {
// Grab the API client, a location and a bundle
const { client } = window.pickystory
const location = client.getLocations('bundles').shift()
const bundle = location.deals.shift()
// Find the designated slot in the page and add a title and product cards
const container = document.querySelector(containerSelector)
const header = document.createElement('h2')
header.textContent = 'BUNDLE'
container.append(header)
for (let product of bundle.products) {
renderProductCard(product, container)
}
// Render the ATC button and specify its action
renderAddToCart(container, async (selections) => {
const variants = bundle.getVariantsForSelections(selections)
await bundle.addVariantsToCart(variants, 1)
})
}
Bundle builders
const renderBuilder = (containerSelector) => {
// Grab the API client, a location and a builder
const { client } = window.pickystory
const location = client.getLocations('builders').shift()
const builder = location.deals.shift()
// Find the designated slot in the page and add a title and product cards
const container = document.querySelector(containerSelector)
const header = document.createElement('h2')
header.textContent = 'BUILDER'
container.append(header)
for (let product of builder.products) {
renderProductCard(product, container)
}
// Render the ATC button and specify its action
renderAddToCart(container, async (selections) => {
const variants = builder.getVariantsForSelections(selections)
await builder.addVariantsToCart(variants, 1)
})
}
Kits
Coming soon!
Looks
Coming soon!
Common
For the purpose of this demo, we assume widget slots have been added to the store HTML as below:
<div id="api-combo-container"></div>
<div id="api-bundle-container"></div>
<div id="api-builder-container"></div>
<div id="api-kit-container"></div>
<div id="api-look-container"></div>
/**
* Render a card containing product title, image and options
*/
const renderProductCard = (product, container) => {
// Show the title and the product image using simple <div>, <h3> and <img> tags
const productDiv = document.createElement('div')
container.append(productDiv)
const title = document.createElement('h3')
productDiv.append(title)
title.textContent = product.title
const image = document.createElement('img')
productDiv.append(image)
image.height = 100
image.width = 100
image.src = product.images[0].src
// Show product options and their values as dropdowns using <select>
for (let option of product.options) {
const select = document.createElement('select')
productDiv.append(select)
select.name = option.name
select.setAttribute('product-position', product.position)
select.setAttribute('option-position', option.position)
select.classList.add('picky-option')
for (let value of option.values) {
const option = document.createElement('option')
select.append(option)
option.value = value
option.textContent = value
}
}
}
/**
* Render a sample Add To Cart button
*/
const renderAddToCart = (container, onAddSelections, callToAction = 'ADD') => {
// Create a simple button and style it a bit
const button = document.createElement('button')
container.append(button)
button.type = 'button'
button.textContent = callToAction
Object.entries({
width: '100%', 'text-align': 'center', background: 'black', color: 'white', height: '40px'
}).forEach(([key, value]) => button.style.setProperty(key, value))
// Collect product option values from the relevant <select> elements
button.onclick = async () => {
const selections = [...container.querySelectorAll('.picky-option')]
.reduce((result, node) => {
const productPosition = parseInt(node.getAttribute('product-position'), 10)
const optionPosition = parseInt(node.getAttribute('option-position'), 10)
let product = result.find(({ position }) => position === productPosition)
if (!product) {
product = { position: productPosition }
result.push(product)
}
const optionAlias = 'option' + optionPosition
product[optionAlias] = node.value
return result
}, [])
// Use a naive opacity effect while cart addition is in progress
button.disabled = true
button.style.setProperty('opacity', '0.4')
// An async click handler must be provided
if (typeof onAddSelections === 'function') {
await onAddSelections(selections)
}
button.disabled = false
button.style.setProperty('opacity', '1')
}
}
(() => {
// A simple way of activating the widget once the API becomes available
let apiWaitCount = 0
const interval = window.setInterval(() => {
++apiWaitCount
if (window?.pickystory?.client) {
clearInterval(interval)
renderCombo('#api-combo-container')
renderBundle('#api-bundle-container')
renderBuilder('#api-builder-container')
} else if (apiWaitCount > 50) clearInterval(interval)
}, 50)
})()
Last updated