Skip to content

Commit

Permalink
XCORNER-21 cart page
Browse files Browse the repository at this point in the history
  • Loading branch information
mgbelegu committed Mar 6, 2024
1 parent eb0f1d5 commit e3d4be7
Show file tree
Hide file tree
Showing 18 changed files with 387 additions and 244 deletions.
5 changes: 5 additions & 0 deletions assets/js/theme/cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import CartItemDetails from './common/cart-item-details'
import q$, { q$$ } from './global/selector'
import trigger from './common/utils/trigger'
import toggle from './global/toggle'
import addReviewsToCartItems from './custom/add-reviews-to-cart-items'

export default class Cart extends PageManager {
onReady() {
Expand All @@ -30,6 +31,10 @@ export default class Cart extends PageManager {

this.setApplePaySupport()
this.bindEvents()

const { storefrontApiToken } = this.context

addReviewsToCartItems(storefrontApiToken)
}

setApplePaySupport() {
Expand Down
9 changes: 6 additions & 3 deletions assets/js/theme/cart/shipping-estimator.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default class ShippingEstimator {
tap: announceInputErrorMessage,
})

q$('.js-shipping-estimate-submit', this.$element)?.addEventListener('click', (event) => {
q$('.js-shipping-estimate-submit', this.$element).addEventListener('click', (event) => {
// estimator error messages are being injected in html as a result
// of user submit; clearing and adding role on submit provides
// regular announcement of these error messages
Expand Down Expand Up @@ -140,7 +140,10 @@ export default class ShippingEstimator {
// When you change a country, you swap the state/province between an input and a select dropdown
// Not all countries require the province to be filled
// We have to remove this class when we swap since nod validation doesn't cleanup for us
q$(this.shippingEstimator).querySelector('.js-form-field-success').classList.remove('js-form-field-success')
const shippingEstimator = q$(this.shippingEstimator)
if (shippingEstimator) {
shippingEstimator.querySelector('.js-form-field-success')?.classList.remove('js-form-field-success')
}
})
}

Expand Down Expand Up @@ -177,7 +180,7 @@ export default class ShippingEstimator {
event.preventDefault()

utils.api.cart.getShippingQuotes(params, 'cart/shipping-quotes', (err, response) => {
q$('.js-shipping-quotes').innerHTML = response.content
q$('.js-shipping-quotes').innerHTML = response?.content

// bind the select button
q$('.js-select-shipping-quote').addEventListener('click', (clickEvent) => {
Expand Down
52 changes: 28 additions & 24 deletions assets/js/theme/common/state-country.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,27 @@ import q$, { q$$ } from '../global/selector'
* 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.
* @returns {HTMLElement}
*/
function makeStateRequired(stateElement, context) {
function makeStateRequired(stateElement) {
/* eslint-disable no-param-reassign */
stateElement.innerHTML = `
<select
id=${stateElement.id}
name=${stateElement.getAttribute('name')}
data-label=${stateElement.dataset.label}
data-field-type=${stateElement.dataset.fieldType}
>
</select>
`
const selectElement = document.createElement('select')

selectElement.className = 'c-form__input c-form__input--select u-width-full'

selectElement.id = stateElement.id
selectElement.name = stateElement.getAttribute('name')
selectElement.setAttribute('data-label', stateElement.dataset.label)
selectElement.setAttribute('data-field-type', stateElement.dataset.fieldType)

stateElement.replaceWith(selectElement)

const $hiddenInput = q$$('[name*="FormFieldIsText"]')
$hiddenInput.forEach(($hi) => $hi.remove())

const $newElement = q$('[data-field-type="State"]')
const $prevElement = $newElement.previousElementSibling
if ($prevElement.querySelector('small') === null) {
if ($prevElement?.querySelector('small') === null) {
// String is injected from localizer
$prevElement.insertAdjacentHTML('beforeend', `<small>${context.required}</small>`)
$prevElement.insertAdjacentHTML('beforeend', `<small class="u-required">*</small>`)
} else {
$prevElement.querySelector('small').style.display = 'block'
}
Expand All @@ -43,22 +44,25 @@ function makeStateRequired(stateElement, context) {
*/
function makeStateOptional(stateElement) {
/* eslint-disable no-param-reassign */
stateElement.innerHTML = `
<input
type="text"
id=${stateElement.id}
name=${stateElement.getAttribute('name')}
data-label=${stateElement.dataset.label}
data-field-type=${stateElement.dataset.fieldType}
class="js-form-input"
/>
`
const inputElement = document.createElement('input')
inputElement.type = 'text'

inputElement.className = 'js-form-input c-form__input u-width-full'

inputElement.id = stateElement.id
inputElement.name = stateElement.getAttribute('name')
inputElement.setAttribute('data-label', stateElement.dataset.label)
inputElement.setAttribute('data-field-type', stateElement.dataset.fieldType)

stateElement.replaceWith(inputElement)

const $newElement = q$('[data-field-type="State"]')
if ($newElement !== null) {
insertStateHiddenField($newElement)

$newElement.previousElementSibling.querySelector('small').style.display = 'none'
if ($newElement.previousElementSibling?.querySelector('small')) {
$newElement.previousElementSibling.querySelector('small').style.display = 'none'
}
}

return $newElement
Expand All @@ -75,7 +79,7 @@ function addOptions(statesArray, $selectElement, options) {

container.push(`<option value="">${statesArray.prefix}</option>`)

if (!isEmpty($selectElement)) {
if (isEmpty($selectElement)) {
statesArray.states.forEach((stateObj) => {
if (options.useIdForStates) {
container.push(`<option value="${stateObj.id}">${stateObj.name}</option>`)
Expand Down
15 changes: 8 additions & 7 deletions assets/js/theme/common/utils/form-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,13 +321,14 @@ const Validators = {
* @param field
*/
cleanUpStateValidation: (field) => {
const $fieldClassElement = q$(`[data-type="${field.dataset.fieldType}"]`)

Object.keys(nod.classes).forEach((value) => {
if ($fieldClassElement.classList.contains(nod.classes[value])) {
$fieldClassElement.classList.remove(nod.classes[value])
}
})
if (field.dataset.fieldType) {
const $fieldClassElement = q$(`[data-field-type="${field.dataset.fieldType}"]`)
Object.keys(nod.classes).forEach((value) => {
if ($fieldClassElement.classList.contains(nod.classes[value])) {
$fieldClassElement.classList.remove(nod.classes[value])
}
})
}
},
}

Expand Down
62 changes: 62 additions & 0 deletions assets/js/theme/custom/add-reviews-to-cart-items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Add reviews to cart items via GraphQL
*/
export default function addReviewsToCartItems(storefrontApiToken) {
const cartItems = document.querySelectorAll('.js-item-row')
if (cartItems.length > 0) {
cartItems.forEach((product) => {
const productId = product.getAttribute('data-type-product-id')
fetch('/graphql', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${storefrontApiToken}`,
},
body: JSON.stringify({
query: `
query getProductRating {
site {
product(entityId: ${productId}) {
reviewSummary {
numberOfReviews
summationOfRatings
}
}
}
}
`,
}),
})
.then((res) => res.json())
.then((data) => {
const summationOfRatings = data?.data?.site?.product?.reviewSummary?.summationOfRatings
const numberOfReviews = data?.data?.site?.product?.reviewSummary?.numberOfReviews
const ratingElement = document.querySelector(`[data-type-product-id="${productId}"] .c-cart__item-rating`)
const ratingLink = document.querySelector(`[data-type-product-id="${productId}"] .c-cart__item-rating-link`)

ratingLink.innerHTML = `${numberOfReviews} reviews`

const filledStarSVG = `
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
<path d="M6 0L7.66869 4.21848L12 4.58359L8.7 7.55587L9.7082 12L6 9.61848L2.2918 12L3.3 7.55587L0 4.58359L4.33131 4.21848L6 0Z" fill="#C1A45A"></path>
</svg>`

const unfilledStarSVG = `
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
<path d="M6 1.35931L7.20375 4.4024L7.31778 4.69068L7.62669 4.71672L10.8074 4.98484L8.36538 7.18435L8.14752 7.38056L8.21239 7.66649L8.95026 10.919L6.27019 9.19777L6 9.02425L5.72981 9.19777L3.04974 10.919L3.78761 7.66649L3.85247 7.38056L3.63462 7.18435L1.19259 4.98484L4.37331 4.71672L4.68222 4.69068L4.79625 4.4024L6 1.35931Z" stroke="#C1A45A"></path>
</svg>`

if (ratingElement) {
for (let i = 1; i <= 5; i++) {
if (i <= summationOfRatings) {
ratingElement.insertAdjacentHTML('beforeend', filledStarSVG)
} else {
ratingElement.insertAdjacentHTML('beforeend', unfilledStarSVG)
}
}
}
})
})
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.c-cart__content {
margin-bottom: rem-calc(24px);
}

.c-cart__item-wrapper {
display: flex;
flex-direction: column;
Expand All @@ -6,25 +10,36 @@

.c-cart__item {
display: flex;
flex-direction: column;
gap: rem-calc(24px);

@include bp(small) {
flex-direction: row;
gap: rem-calc(48px);
}
}

.c-cart__item-image {
.c-cart__item-image-wrapper {
position: relative;
display: block;
aspect-ratio: 17.75 / 26.5;
width: 100%;
width: 50%;
height: auto;
max-width: 17.75rem;
object-fit: cover;

img {
position: absolute;
@include bp(small) {
width: 100%;
height: 100%;
object-fit: cover;
}
}

.c-cart__item-image {
position: absolute;
width: 100%;
height: 100%;
object-fit: cover;
}

.c-cart__item-information {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -119,3 +134,35 @@
outline: none;
cursor: pointer;
}

.c-cart__checkout-button-wrapper {
display: flex;
flex-direction: column;
align-items: flex-end;
}

.c-cart__item-rating-wrapper {
display: flex;
align-items: center;
gap: rem-calc(8px);
}

.c-cart__item-rating-link {
color: $color-primary-300;
font-family: $font-semi-bold;
font-size: rem-calc(14px);
line-height: rem-calc(16px);
font-weight: 600;
letter-spacing: 0;
text-align: center;

&:hover {
color: $color-primary-300;
}
}

.c-cart__item-head {
display: flex;
flex-direction: column;
gap: rem-calc(12px);
}
5 changes: 4 additions & 1 deletion assets/scss/components/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@
@import 'search';
@import 'search-box';
@import 'cta-panel';
@import 'cart-content';
@import 'cart';
@import 'totals';
@import 'shipping-estimator';
@import 'coupon-form';
5 changes: 5 additions & 0 deletions assets/scss/components/_coupon-form.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.c-coupon-form {
display: flex;
flex-direction: column;
gap: rem-calc(12px);
}
7 changes: 7 additions & 0 deletions assets/scss/components/_shipping-estimator.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.c-shipping-estimator {
display: flex;
flex-direction: column;
align-items: flex-end;
text-align: right;
gap: rem-calc(12px);
}
47 changes: 47 additions & 0 deletions assets/scss/components/_totals.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.c-totals__subtotal {
display: flex;
justify-content: flex-end;
align-items: center;
gap: rem-calc(24px);
}

.c-totals__shipping-total {
display: flex;
justify-content: flex-end;
align-items: center;
gap: rem-calc(24px);
margin-bottom: rem-calc(12px);
}

.c-totals__label {
color: $color-neutrals-300;
font-family: $font-semi-bold;
font-size: rem-calc(20px);
line-height: rem-calc(24px);
font-weight: 600;
letter-spacing: 0;
text-align: center;
}

.c-totals__value {
@extend .c-totals__label;
color: $color-black;
}

.c-totals__shipping-estimator {
margin-bottom: rem-calc(24px);
}

.c-totals__cart-total {
font-size: rem-calc(32px);
line-height: rem-calc(40px);
text-align: right;
margin-block: rem-calc(24px);
}

.c-totals__checkout-button {
display: flex;
justify-content: center;
align-items: center;
gap: rem-calc(8px);
}
5 changes: 5 additions & 0 deletions assets/scss/pages/cart.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.p-cart__totals {
display: flex;
flex-direction: column;
align-items: flex-end;
}
1 change: 1 addition & 0 deletions assets/scss/pages/pages.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
@import './page';
@import './blog';
@import './product';
@import './cart';
2 changes: 1 addition & 1 deletion lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"multiple": "check out with multiple addresses",
"or": "or"
},
"button": "Check out",
"button": "Go to checkout",
"empty_cart": "Your cart is empty",
"title": "Click here to proceed to checkout",
"item": "Item",
Expand Down
Loading

0 comments on commit e3d4be7

Please sign in to comment.