Skip to content

Commit

Permalink
Quick order keyboard interactions (Shopify#2962)
Browse files Browse the repository at this point in the history
* Add basic functionality.
Add placeholder.
Make a row rerender instead of the whole section.

* Style placeholder

* Fix removeAll button

* Remove comments and unnecessary code.

* Remove debouncing.

* Remove placeholder for input

* Add selection on focus for inputs

* Move eventListener to a parent element

* Put debouncing back

* Refactor switchVariants method

* Refactor allInputs array

* Remove unnecessary input placeholder

* Fix typo

* Refactor variants names

* Remove unused prop

* Put back prop 'name' for updateQuantity

* Refactoring

* [Quick Order List] Prevent selected inputs being covered by total bar or header (Shopify#3278)

* Prevent input elements going under total bar or sticky header during keyboard navigation.

* Fix a typo

* Refactoring

* Increase z-index for volume pricing popup

* Fix conflicts after merge
  • Loading branch information
eugenekasimov committed Feb 29, 2024
1 parent e2ba8f4 commit ee456f6
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 18 deletions.
4 changes: 2 additions & 2 deletions assets/quantity-popover.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ quantity-popover volume-pricing li {

.quantity-popover__info.global-settings-popup {
width: 100%;
z-index: 2;
z-index: 3;
position: absolute;
background-color: rgb(var(--color-background));
max-width: 36rem;
Expand Down Expand Up @@ -161,4 +161,4 @@ quantity-popover .quantity__rules .divider:nth-child(2)::before {
quantity-popover .quantity__button:not(:focus-visible):not(.focused),
quantity-popover .quantity__input:not(:focus-visible):not(.focused) {
background-color: initial;
}
}
6 changes: 5 additions & 1 deletion assets/quick-order-list.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ quick-order-list .quantity__button {
width: calc(3.5rem / var(--font-body-scale));
}

.quick-order-list__contents {
position: relative;
}

.quick-order-list__container {
padding-bottom: 4rem;
}
Expand All @@ -36,7 +40,7 @@ quick-order-list .quantity__button {
.quick-order-list__total {
position: sticky;
bottom: 0;
z-index: 1;
z-index: 2;
background-color: rgb(var(--color-background));
}

Expand Down
86 changes: 71 additions & 15 deletions assets/quick-order-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@ customElements.define('quick-order-list-remove-button', QuickOrderListRemoveButt
class QuickOrderListRemoveAllButton extends HTMLElement {
constructor() {
super();
const allVariants = Array.from(document.querySelectorAll('[data-variant-id]'));
const allVariants = Array.from(document.querySelectorAll('[data-quantity-variant-id]'));
const items = {}
let hasVariantsInCart = false;
this.quickOrderList = this.closest('quick-order-list');

allVariants.forEach((variant) => {
const cartQty = parseInt(variant.dataset.cartQty);
const cartQty = parseInt(variant.dataset.cartQuantity);
if (cartQty > 0) {
hasVariantsInCart = true;
items[parseInt(variant.dataset.variantId)] = 0;
items[parseInt(variant.dataset.quantityVariantId)] = 0;
}
});

Expand Down Expand Up @@ -67,12 +66,28 @@ class QuickOrderList extends HTMLElement {
add: 'ADD',
update: 'UPDATE'
}
this.defineInputsAndQuickOrderTable();
this.quickOrderListId = 'quick-order-list'
this.variantItemStatusElement = document.getElementById('shopping-cart-variant-item-status');
const form = this.querySelector('form');
this.inputFieldHeight = this.querySelector('.variant-item__quantity-wrapper').offsetHeight;
this.stickyHeaderElement = document.querySelector('sticky-header');

if (this.stickyHeaderElement) {
this.stickyHeader = {
height: this.stickyHeaderElement.offsetHeight,
type: `${this.stickyHeaderElement.getAttribute('data-sticky-type')}`
};
}

form.addEventListener('submit', this.onSubmit.bind(this));
this.totalBarPosition = window.innerHeight - this.querySelector('.quick-order-list__total').offsetHeight;

window.addEventListener('resize', () => {
this.totalBarPosition = window.innerHeight - this.querySelector('.quick-order-list__total').offsetHeight;
this.stickyHeader.height = this.stickyHeaderElement ? this.stickyHeaderElement.offsetHeight: null;
});

form.addEventListener('submit', this.onSubmit.bind(this));
const debouncedOnChange = debounce((event) => {
this.onChange(event);
}, ON_CHANGE_DEBOUNCE_TIMER);
Expand Down Expand Up @@ -102,6 +117,12 @@ class QuickOrderList extends HTMLElement {
}
}

defineInputsAndQuickOrderTable() {
this.allInputsArray = Array.from(this.querySelectorAll('input[type="number"]'));
this.quickOrderListTable = this.querySelector('.quick-order-list__table');
this.quickOrderListTable.addEventListener('focusin', this.switchVariants.bind(this));
}

onChange(event) {
const inputValue = parseInt(event.target.value);
const cartQuantity = parseInt(event.target.dataset.cartQuantity);
Expand Down Expand Up @@ -160,23 +181,63 @@ class QuickOrderList extends HTMLElement {
];
}

renderSections(parsedState) {
renderSections(parsedState, id) {
this.getSectionsToRender().forEach((section => {
const sectionElement = document.getElementById(section.id);
if (sectionElement && sectionElement.parentElement && sectionElement.parentElement.classList.contains('drawer')) {
parsedState.items.length > 0 ? sectionElement.parentElement.classList.remove('is-empty') : sectionElement.parentElement.classList.add('is-empty');

setTimeout(() => {
document.querySelector('#CartDrawer-Overlay').addEventListener('click', this.cart.close.bind(this.cart));
});
}
const elementToReplace = sectionElement && sectionElement.querySelector(section.selector) ? sectionElement.querySelector(section.selector) : sectionElement;
if (elementToReplace) {
elementToReplace.innerHTML =
this.getSectionInnerHTML(parsedState.sections[section.section], section.selector);
if (section.selector === '.js-contents' && id !== undefined) {
elementToReplace.querySelector(`#Variant-${id}`).innerHTML =
this.getSectionInnerHTML(parsedState.sections[section.section], `#Variant-${id}`);
} else {
elementToReplace.innerHTML = this.getSectionInnerHTML(parsedState.sections[section.section], section.selector);
}
}
}));
this.defineInputsAndQuickOrderTable();
}

switchVariants(event) {
if (event.target.tagName !== 'INPUT') {
return;
}
this.variantListInput = event.target;
this.variantListInput.select()
this.variantListInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
e.target.blur();
const currentIndex = this.allInputsArray.indexOf(e.target);

if (!e.shiftKey) {
const nextIndex = currentIndex + 1;
const nextVariant = this.allInputsArray[nextIndex] || this.allInputsArray[0];
nextVariant.select();
} else {
const previousIndex = currentIndex - 1;
const previousVariant = this.allInputsArray[previousIndex] || this.allInputsArray[this.allInputsArray.length - 1];
previousVariant.select();
}
}
});

const inputTopBorder = this.variantListInput.getBoundingClientRect().top;
const inputBottomBorder = this.variantListInput.getBoundingClientRect().bottom;
const stickyHeaderBottomBorder = this.stickyHeaderElement && this.stickyHeaderElement.getBoundingClientRect().bottom;
const totalBarCrossesInput = inputBottomBorder > this.totalBarPosition;
const inputOutsideOfViewPort = inputBottomBorder < this.inputFieldHeight;
const stickyHeaderCrossesInput = this.stickyHeaderElement && this.stickyHeader.type !== 'on-scroll-up' && this.stickyHeader.height > inputTopBorder;
const stickyHeaderScrollupCrossesInput = this.stickyHeaderElement && this.stickyHeader.type === 'on-scroll-up' && this.stickyHeader.height > inputTopBorder && stickyHeaderBottomBorder > 0;

if (totalBarCrossesInput || inputOutsideOfViewPort || stickyHeaderCrossesInput || stickyHeaderScrollupCrossesInput) {
this.variantListInput.scrollIntoView({ block: 'center', behavior: 'smooth' });
}
}

updateMultipleQty(items) {
Expand Down Expand Up @@ -258,7 +319,7 @@ class QuickOrderList extends HTMLElement {

this.classList.toggle('is-empty', parsedState.item_count === 0);

this.renderSections(parsedState);
this.renderSections(parsedState, id);

let hasError = false;

Expand All @@ -269,10 +330,6 @@ class QuickOrderList extends HTMLElement {
hasError = true;
}

const variantItem = document.getElementById(`Variant-${id}`);
if (variantItem && variantItem.querySelector(`[name="${name}"]`)) {
variantItem.querySelector(`[name="${name}"]`).focus();
}
publish(PUB_SUB_EVENTS.cartUpdate, { source: this.quickOrderListId, cartData: parsedState });

if (hasError) {
Expand Down Expand Up @@ -380,7 +437,6 @@ class QuickOrderList extends HTMLElement {
if (enable) {
quickOrderList.classList.add('quick-order-list__container--disabled');
[...quickOrderListItems].forEach((overlay) => overlay.classList.remove('hidden'));
document.activeElement.blur();
this.variantItemStatusElement.setAttribute('aria-hidden', false);
} else {
quickOrderList.classList.remove('quick-order-list__container--disabled');
Expand Down

0 comments on commit ee456f6

Please sign in to comment.