class CartDrawer extends HTMLElement { constructor() { super(); this.addEventListener('keyup', (evt) => evt.code === 'Escape' && this.close()); this.querySelector('#CartDrawer-Overlay').addEventListener('click', this.close.bind(this)); this.setHeaderCartIconAccessibility(); } setHeaderCartIconAccessibility() { const cartLink = document.querySelector('#cart-icon-bubble'); cartLink.setAttribute('role', 'button'); cartLink.setAttribute('aria-haspopup', 'dialog'); cartLink.addEventListener('click', (event) => { event.preventDefault(); this.open(cartLink); }); cartLink.addEventListener('keydown', (event) => { if (event.code.toUpperCase() === 'SPACE') { event.preventDefault(); this.open(cartLink); } }); } open(triggeredBy) { if (triggeredBy) this.setActiveElement(triggeredBy); const cartDrawerNote = this.querySelector('[id^="Details-"] summary'); if (cartDrawerNote && !cartDrawerNote.hasAttribute('role')) this.setSummaryAccessibility(cartDrawerNote); // here the animation doesn't seem to always get triggered. A timeout seem to help setTimeout(() => { this.classList.add('animate', 'active'); }); this.addEventListener( 'transitionend', () => { const containerToTrapFocusOn = this.classList.contains('is-empty') ? this.querySelector('.drawer__inner-empty') : document.getElementById('CartDrawer'); const focusElement = this.querySelector('.drawer__inner') || this.querySelector('.drawer__close'); trapFocus(containerToTrapFocusOn, focusElement); }, { once: true } ); document.body.classList.add('overflow-hidden'); } close() { this.classList.remove('active'); removeTrapFocus(this.activeElement); document.body.classList.remove('overflow-hidden'); } setSummaryAccessibility(cartDrawerNote) { cartDrawerNote.setAttribute('role', 'button'); cartDrawerNote.setAttribute('aria-expanded', 'false'); if (cartDrawerNote.nextElementSibling.getAttribute('id')) { cartDrawerNote.setAttribute('aria-controls', cartDrawerNote.nextElementSibling.id); } cartDrawerNote.addEventListener('click', (event) => { event.currentTarget.setAttribute('aria-expanded', !event.currentTarget.closest('details').hasAttribute('open')); }); cartDrawerNote.parentElement.addEventListener('keyup', onKeyUpEscape); } renderContents(parsedState) { this.querySelector('.drawer__inner').classList.contains('is-empty') && this.querySelector('.drawer__inner').classList.remove('is-empty'); this.productId = parsedState.id; this.getSectionsToRender().forEach((section) => { const sectionElement = section.selector ? document.querySelector(section.selector) : document.getElementById(section.id); sectionElement.innerHTML = this.getSectionInnerHTML(parsedState.sections[section.id], section.selector); }); setTimeout(() => { this.querySelector('#CartDrawer-Overlay').addEventListener('click', this.close.bind(this)); this.open(); }); } getSectionInnerHTML(html, selector = '.shopify-section') { return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML; } getSectionsToRender() { return [ { id: 'cart-drawer', selector: '#CartDrawer', }, { id: 'cart-icon-bubble', }, ]; } getSectionDOM(html, selector = '.shopify-section') { return new DOMParser().parseFromString(html, 'text/html').querySelector(selector); } setActiveElement(element) { this.activeElement = element; } } customElements.define('cart-drawer', CartDrawer); class CartDrawerItems extends CartItems { getSectionsToRender() { return [ { id: 'CartDrawer', section: 'cart-drawer', selector: '.drawer__inner', }, { id: 'cart-icon-bubble', section: 'cart-icon-bubble', selector: '.shopify-section', }, ]; } } customElements.define('cart-drawer-items', CartDrawerItems);