From 8bf1cb6985ff0d054c50ae302dc57d5bb87ddb2b Mon Sep 17 00:00:00 2001 From: Toni Sinisalo Date: Wed, 12 Feb 2025 18:11:12 +0200 Subject: [PATCH 1/2] UHF-11427: UHF-11427: Using a twig filter to generate a unique id for data-accordion-id, and using it in state management instead of an id to remove dependency on helfi_toc/table_of_contents. --- dist/js/accordion.min.js | 2 +- hdbt.libraries.yml | 2 -- src/js/accordion/accordion-item.js | 2 +- src/js/accordion/accordion.js | 3 ++- templates/component/accordion.twig | 5 +++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dist/js/accordion.min.js b/dist/js/accordion.min.js index d02417d0a..885569ce0 100644 --- a/dist/js/accordion.min.js +++ b/dist/js/accordion.min.js @@ -1 +1 @@ -!function(){"use strict";var e={7936:function(e,t,s){s.d(t,{A:function(){return i}});class i{static accordionItemElement="helfi-accordion-item";static toggleElement="accordion-item__button--toggle";static closeElement="accordion-item__button--close";static ariaExpandedElements=["accordion-item__button--toggle"];static contentElement="accordion-item__content";constructor(e,t,s,i){this.element=e,this.localState=t,this.parentCallback=i,this._id=e.querySelector(".helfi-accordion__header").id,this._isOpen=this.localState.loadItemState(this._id),this.element.style="--js-accordion-open-time:0s",this.setHidden(),this.addEventListeners(),this.handleLinkAnchor(s),this.setAriaOpen(),this.element.style=null}open=()=>{this._isOpen=!0,this.setAriaOpen(),this.setHidden(),this.parentCallback(),this.localState.saveItemState(this.id,this.isOpen)};close=()=>{this._isOpen=!1,this.setAriaOpen(),this.changeFocus(),this.setHidden(),this.parentCallback(),this.localState.saveItemState(this.id,this.isOpen)};closeWithoutFocus=()=>{this._isOpen=!1,this.setAriaOpen(),this.setHidden(),this.parentCallback(),this.localState.saveItemState(this.id,this.isOpen)};toggle=e=>{i.isClick(e.which)&&(this.isOpen?this.close():this.open())};handleLinkAnchor=e=>{if(!e||0===e.length)return;const t=this.element.querySelector(e);t&&(this.open(),t.scrollIntoView())};setAriaOpen=()=>{i.ariaExpandedElements.forEach((e=>{this.element.getElementsByClassName(e)[0].setAttribute("aria-expanded",this.isOpen)}))};setHidden=()=>{const e=this.element.getElementsByClassName(i.contentElement)[0];this.isOpen?e.classList.remove("is-hidden"):e.classList.add("is-hidden")};changeFocus=()=>this.element.querySelector(`.${i.toggleElement}`).focus();addEventListeners=()=>{const e=this.element.getElementsByClassName(i.toggleElement)[0];e.addEventListener("mouseup",this.toggle),e.addEventListener("keypress",this.toggle);const t=this.element.getElementsByClassName(i.closeElement)[0];t.addEventListener("mouseup",this.close),t.addEventListener("keypress",this.close)};static isClick(e){return 1===e||13===e||32===e}get id(){return this._id}get isOpen(){return this._isOpen}}},5966:function(e,t,s){s.d(t,{A:function(){return a}});var i=s(7936);var n={open_all:{fi:"Avaa kaikki",en:"Open all",sv:"Öppna alla"},close_all:{fi:"Sulje kaikki",en:"Close all",sv:"Stäng alla"}},o=s(609);class a{static accordionWrapper="component--accordion";static toggleAllElement="js-accordion__button--toggle-all";static toggleAllOpen="accordion__button--is-open";static toggleAllClosed="accordion__button--is-closed";static headerlessTypes=["headerless","hardcoded"];constructor(e,t,s,i="default"){this.type=i,this.isSingleItemAccordion=!1,this.currentLanguage=o.A.getCurrentLanguage(),this.accordion=e,this.localState=t,this.accordionItems=[],this.childAccordion=null,this.urlHash=s,this.initializeAccordion(s),this.addEventListeners(),this.showToggleButton(),this.updateToggleButtonLabel()}initializeAccordion=()=>{const e=this.accordion.getElementsByClassName(i.A.accordionItemElement);this.isSingleItemAccordion=1===e.length,Array.from(e).forEach((e=>{this.accordionItems.push(new i.A(e,this.localState,this.urlHash,this.updateToggleButtonLabel))}))};addEventListeners=()=>{if(a.isHeaderless(this.type)||this.isSingleItemAccordion)return;const e=this.accordion.getElementsByClassName(a.toggleAllElement)[0];e.addEventListener("mouseup",this.toggleItems),e.addEventListener("keypress",this.toggleItems)};addChildAccordion=e=>{this.childAccordion=e};showToggleButton=()=>{a.isHeaderless(this.type)||this.isSingleItemAccordion?this.accordion.getElementsByClassName(a.toggleAllElement)[0]?.classList.add("is-hidden"):this.accordion.getElementsByClassName(a.toggleAllElement)[0]?.classList.remove("is-hidden")};updateToggleButtonLabel=()=>{const e=this.accordion.getElementsByClassName(a.toggleAllElement)[0];e&&(this.areAllItemsOpen()?e.querySelector("span").textContent=n.close_all?.[this.currentLanguage]??n.close_all.en:e.querySelector("span").textContent=n.open_all?.[this.currentLanguage]??n.open_all.en,this.toggleAllLabelUpdate())};getAccordionItemById=e=>this.accordionItems.find((t=>t.id===e));toggleItems=()=>this.areAllItemsOpen()?this.closeAll():this.openAll();openAll=()=>{this.accordionItems.forEach((e=>e.open())),this.childAccordion?.openAll(),this.updateToggleButtonLabel(),this.toggleAllLabelUpdate()};closeAll=()=>{this.accordionItems.forEach((e=>e.closeWithoutFocus())),this.childAccordion?.closeAll(),this.updateToggleButtonLabel(),this.toggleAllLabelUpdate();this.accordion.getElementsByClassName(a.toggleAllElement)[0].focus()};toggleAllLabelUpdate=()=>{const e=this.accordion.getElementsByClassName(a.toggleAllElement)[0];e&&this.areAllItemsOpen()?(e.classList.remove(a.toggleAllClosed),e.classList.add(a.toggleAllOpen)):(e.classList.remove(a.toggleAllOpen),e.classList.add(a.toggleAllClosed))};areAllItemsOpen=()=>this.accordionItems?.every((e=>e.isOpen))&&this.areChildItemsOpen();areChildItemsOpen=()=>this.childAccordion?.areAllItemsOpen()??this.accordionItems?.every((e=>e.isOpen));static isHeaderless=e=>a.headerlessTypes.includes(e);isHardcoded=()=>"hardcoded"===this.type}},609:function(e,t,s){s.d(t,{A:function(){return n}});class i{constructor(e){this.data={},this.storageKey=e,this.loadData()}loadData(){let e=null;try{e=localStorage.getItem(this.storageKey)}catch(e){i.handleError(e)}this.data=e?JSON.parse(e):{}}saveData(){try{localStorage.setItem(this.storageKey,JSON.stringify(this.data))}catch(e){i.handleError(e)}}setValue(e,t){this.data[e]=t,this.saveData()}getValue(e){return this.data[e]||null}addValue(e,t){this.data[e]||(this.data[e]=[]),"string"!=typeof t||this.data[e].includes(t)||(this.data[e].push(t),this.saveData())}getValues(e){return this.data[e]||null}removeValue(e,t){if(this.data[e]){const s=this.data[e].indexOf(t);s>-1&&(this.data[e].splice(s,1),this.saveData())}}static handleError(e){if(!(e instanceof ReferenceError))throw e}}class n{constructor(){this.storageManager=new i("helfi-settings"),this.site=window.drupalSettings.helfi_instance_name||"",this.page=window.drupalSettings.path.currentPath,this.siteAccordionStates=JSON.parse(this.storageManager.getValue(this.getStorageKey()))||{},this.pageAccordionStates=this.siteAccordionStates[this.page]||{},this.AccordionsOpen=n.isCookieSet("helfi_accordions_open")}static isCookieSet=e=>{const t=document.cookie.split("; ");for(let s=0;s`${this.site}-accordion`;saveItemState=(e,t)=>{if(!this.site)return!1;this.siteAccordionStates[this.page]||(this.siteAccordionStates[this.page]={}),!1===t?delete this.siteAccordionStates[this.page][e]:this.siteAccordionStates[this.page][e]=t,this.storageManager.setValue(this.getStorageKey(),JSON.stringify(this.siteAccordionStates))};loadItemState=e=>!!this.AccordionsOpen||!!this.site&&!!this.pageAccordionStates[e];static getCurrentLanguage=()=>window.drupalSettings.path.currentLanguage}}},t={};function s(i){var n=t[i];if(void 0!==n)return n.exports;var o=t[i]={exports:{}};return e[i](o,o.exports,s),o.exports}s.d=function(e,t){for(var i in t)s.o(t,i)&&!s.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var i=s(5966),n=s(609),o=s(7936);class a{constructor(){a.handleTableOfContentsHash()}static handleTableOfContentsHash=()=>{window.addEventListener("hashchange",(()=>{const{hash:e}=window.location;if(!e||e.length<2)return;let t=!1;if(window.helfiAccordions.forEach((s=>{const i=s.getAccordionItemById(e.replace("#",""));i&&(t=!0,i.handleLinkAnchor(e))})),!t){const t=document.querySelector(`${e}`);if(!t)return;const s=t.closest(`.${o.A.accordionItemElement}`);s&&window.helfiAccordions.forEach((e=>{const t=s.querySelector(".helfi-accordion__header").id,i=e.getAccordionItemById(t);i&&i.handleLinkAnchor()}))}}))}}window.helfiAccordions=[];const l=new n.A,{hash:c}=(new a,window.location),r=document.body;new MutationObserver(((e,t)=>{const s=document.querySelectorAll(`.${i.A.accordionWrapper}`);if(s.length>window.helfiAccordions.length)try{s.forEach(((e,t)=>{const s=(n=Array.from(e.classList)).includes("component--no-header")?"headerless":n.includes("component--hardcoded")?"hardcoded":"default";var n;const o=i.A.isHeaderless(s),a=0===t&&"hardcoded"!==s?"default":s,r=new i.A(e,l,c,a);window.helfiAccordions.push(r),!r.isHardcoded(s)&&o&&t>0&&window.helfiAccordions[t-1].addChildAccordion(r)}))}catch(e){console.error(e),t.disconnect()}})).observe(r,{attributes:!0,childList:!0,subtree:!0})}(); \ No newline at end of file +!function(){"use strict";var e={7936:function(e,t,s){s.d(t,{A:function(){return i}});class i{static accordionItemElement="helfi-accordion-item";static toggleElement="accordion-item__button--toggle";static closeElement="accordion-item__button--close";static ariaExpandedElements=["accordion-item__button--toggle"];static contentElement="accordion-item__content";constructor(e,t,s,i){this.element=e,this.localState=t,this.parentCallback=i,this._id=e.dataset.accordionId,this._isOpen=this.localState.loadItemState(this._id),this.element.style="--js-accordion-open-time:0s",this.setHidden(),this.addEventListeners(),this.handleLinkAnchor(s),this.setAriaOpen(),this.element.style=null}open=()=>{this._isOpen=!0,this.setAriaOpen(),this.setHidden(),this.parentCallback(),this.localState.saveItemState(this.id,this.isOpen)};close=()=>{this._isOpen=!1,this.setAriaOpen(),this.changeFocus(),this.setHidden(),this.parentCallback(),this.localState.saveItemState(this.id,this.isOpen)};closeWithoutFocus=()=>{this._isOpen=!1,this.setAriaOpen(),this.setHidden(),this.parentCallback(),this.localState.saveItemState(this.id,this.isOpen)};toggle=e=>{i.isClick(e.which)&&(this.isOpen?this.close():this.open())};handleLinkAnchor=e=>{if(!e||0===e.length)return;const t=this.element.querySelector(e);t&&(this.open(),t.scrollIntoView())};setAriaOpen=()=>{i.ariaExpandedElements.forEach((e=>{this.element.getElementsByClassName(e)[0].setAttribute("aria-expanded",this.isOpen)}))};setHidden=()=>{const e=this.element.getElementsByClassName(i.contentElement)[0];this.isOpen?e.classList.remove("is-hidden"):e.classList.add("is-hidden")};changeFocus=()=>this.element.querySelector(`.${i.toggleElement}`).focus();addEventListeners=()=>{const e=this.element.getElementsByClassName(i.toggleElement)[0];e.addEventListener("mouseup",this.toggle),e.addEventListener("keypress",this.toggle);const t=this.element.getElementsByClassName(i.closeElement)[0];t.addEventListener("mouseup",this.close),t.addEventListener("keypress",this.close)};static isClick(e){return 1===e||13===e||32===e}get id(){return this._id}get isOpen(){return this._isOpen}}},5966:function(e,t,s){s.d(t,{A:function(){return a}});var i=s(7936);var n={open_all:{fi:"Avaa kaikki",en:"Open all",sv:"Öppna alla"},close_all:{fi:"Sulje kaikki",en:"Close all",sv:"Stäng alla"}},o=s(609);class a{static accordionWrapper="component--accordion";static toggleAllElement="js-accordion__button--toggle-all";static toggleAllOpen="accordion__button--is-open";static toggleAllClosed="accordion__button--is-closed";static headerlessTypes=["headerless","hardcoded"];constructor(e,t,s,i="default"){this.type=i,this.isSingleItemAccordion=!1,this.currentLanguage=o.A.getCurrentLanguage(),this.accordion=e,this.localState=t,this.accordionItems=[],this.childAccordion=null,this.urlHash=s,this.initializeAccordion(s),this.addEventListeners(),this.showToggleButton(),this.updateToggleButtonLabel()}initializeAccordion=()=>{const e=this.accordion.getElementsByClassName(i.A.accordionItemElement);this.isSingleItemAccordion=1===e.length,Array.from(e).forEach((e=>{this.accordionItems.push(new i.A(e,this.localState,this.urlHash,this.updateToggleButtonLabel))}))};addEventListeners=()=>{if(a.isHeaderless(this.type)||this.isSingleItemAccordion)return;const e=this.accordion.getElementsByClassName(a.toggleAllElement)[0];e.addEventListener("mouseup",this.toggleItems),e.addEventListener("keypress",this.toggleItems)};addChildAccordion=e=>{this.childAccordion=e};showToggleButton=()=>{a.isHeaderless(this.type)||this.isSingleItemAccordion?this.accordion.getElementsByClassName(a.toggleAllElement)[0]?.classList.add("is-hidden"):this.accordion.getElementsByClassName(a.toggleAllElement)[0]?.classList.remove("is-hidden")};updateToggleButtonLabel=()=>{const e=this.accordion.getElementsByClassName(a.toggleAllElement)[0];e&&(this.areAllItemsOpen()?e.querySelector("span").textContent=n.close_all?.[this.currentLanguage]??n.close_all.en:e.querySelector("span").textContent=n.open_all?.[this.currentLanguage]??n.open_all.en,this.toggleAllLabelUpdate())};getAccordionItemById=e=>this.accordionItems.find((t=>t.id===e));toggleItems=()=>this.areAllItemsOpen()?this.closeAll():this.openAll();openAll=()=>{this.accordionItems.forEach((e=>e.open())),this.childAccordion?.openAll(),this.updateToggleButtonLabel(),this.toggleAllLabelUpdate()};closeAll=()=>{this.accordionItems.forEach((e=>e.closeWithoutFocus())),this.childAccordion?.closeAll(),this.updateToggleButtonLabel(),this.toggleAllLabelUpdate();this.accordion.getElementsByClassName(a.toggleAllElement)[0].focus()};toggleAllLabelUpdate=()=>{const e=this.accordion.getElementsByClassName(a.toggleAllElement)[0];e&&this.areAllItemsOpen()?(e.classList.remove(a.toggleAllClosed),e.classList.add(a.toggleAllOpen)):(e.classList.remove(a.toggleAllOpen),e.classList.add(a.toggleAllClosed))};areAllItemsOpen=()=>this.accordionItems?.every((e=>e.isOpen))&&this.areChildItemsOpen();areChildItemsOpen=()=>this.childAccordion?.areAllItemsOpen()??this.accordionItems?.every((e=>e.isOpen));static isHeaderless=e=>a.headerlessTypes.includes(e);isHardcoded=()=>"hardcoded"===this.type}},609:function(e,t,s){s.d(t,{A:function(){return n}});class i{constructor(e){this.data={},this.storageKey=e,this.loadData()}loadData(){let e=null;try{e=localStorage.getItem(this.storageKey)}catch(e){i.handleError(e)}this.data=e?JSON.parse(e):{}}saveData(){try{localStorage.setItem(this.storageKey,JSON.stringify(this.data))}catch(e){i.handleError(e)}}setValue(e,t){this.data[e]=t,this.saveData()}getValue(e){return this.data[e]||null}addValue(e,t){this.data[e]||(this.data[e]=[]),"string"!=typeof t||this.data[e].includes(t)||(this.data[e].push(t),this.saveData())}getValues(e){return this.data[e]||null}removeValue(e,t){if(this.data[e]){const s=this.data[e].indexOf(t);s>-1&&(this.data[e].splice(s,1),this.saveData())}}static handleError(e){if(!(e instanceof ReferenceError))throw e}}class n{constructor(){this.storageManager=new i("helfi-settings"),this.site=window.drupalSettings.helfi_instance_name||"",this.page=window.drupalSettings.path.currentPath,this.siteAccordionStates=JSON.parse(this.storageManager.getValue(this.getStorageKey()))||{},this.pageAccordionStates=this.siteAccordionStates[this.page]||{},this.AccordionsOpen=n.isCookieSet("helfi_accordions_open")}static isCookieSet=e=>{const t=document.cookie.split("; ");for(let s=0;s`${this.site}-accordion`;saveItemState=(e,t)=>{if(!this.site)return!1;this.siteAccordionStates[this.page]||(this.siteAccordionStates[this.page]={}),!1===t?delete this.siteAccordionStates[this.page][e]:this.siteAccordionStates[this.page][e]=t,this.storageManager.setValue(this.getStorageKey(),JSON.stringify(this.siteAccordionStates))};loadItemState=e=>!!this.AccordionsOpen||!!this.site&&!!this.pageAccordionStates[e];static getCurrentLanguage=()=>window.drupalSettings.path.currentLanguage}}},t={};function s(i){var n=t[i];if(void 0!==n)return n.exports;var o=t[i]={exports:{}};return e[i](o,o.exports,s),o.exports}s.d=function(e,t){for(var i in t)s.o(t,i)&&!s.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var i=s(5966),n=s(609),o=s(7936);class a{constructor(){a.handleTableOfContentsHash()}static handleTableOfContentsHash=()=>{window.addEventListener("hashchange",(()=>{const{hash:e}=window.location;if(!e||e.length<2)return;let t=!1;if(window.helfiAccordions.forEach((s=>{const i=s.getAccordionItemById(e.replace("#",""));i&&(t=!0,i.handleLinkAnchor(e))})),!t){const t=document.querySelector(`${e}`);if(!t)return;const s=t.closest(`.${o.A.accordionItemElement}`);s&&window.helfiAccordions.forEach((e=>{const t=s.querySelector(".helfi-accordion__header").id,i=e.getAccordionItemById(t);i&&i.handleLinkAnchor()}))}}))}}window.helfiAccordions=[];const l=new n.A,{hash:c}=(new a,window.location),r=document.body;new MutationObserver(((e,t)=>{const s=document.querySelectorAll(`.${i.A.accordionWrapper}`);if(s.length>window.helfiAccordions.length)try{s.forEach(((e,t)=>{const s=(n=Array.from(e.classList)).includes("component--no-header")?"headerless":n.includes("component--hardcoded")?"hardcoded":"default";var n;const o=i.A.isHeaderless(s),a=0===t&&"hardcoded"!==s?"default":s,r=new i.A(e,l,c,a);window.helfiAccordions.push(r),!r.isHardcoded(s)&&o&&t>0&&window.helfiAccordions[t-1].addChildAccordion(r)}))}catch(e){console.error(e),t.disconnect()}})).observe(r,{attributes:!0,childList:!0,subtree:!0})}(); \ No newline at end of file diff --git a/hdbt.libraries.yml b/hdbt.libraries.yml index a32937fcf..136145d4a 100644 --- a/hdbt.libraries.yml +++ b/hdbt.libraries.yml @@ -13,8 +13,6 @@ accordion: attributes: { defer: true }, minified: true } - dependencies: - - helfi_toc/table_of_contents helfi_calculator: version: 1.x diff --git a/src/js/accordion/accordion-item.js b/src/js/accordion/accordion-item.js index 19b7165f2..3e7045c4b 100644 --- a/src/js/accordion/accordion-item.js +++ b/src/js/accordion/accordion-item.js @@ -19,7 +19,7 @@ export default class AccordionItem { this.parentCallback = parentCallback; // Use header id as this objects id since header id is unique. - this._id = element.querySelector('.helfi-accordion__header').id; + this._id = element.dataset.accordionId; this._isOpen = this.localState.loadItemState(this._id); this.element.style = '--js-accordion-open-time:0s'; // do not animate accordions on pageload diff --git a/src/js/accordion/accordion.js b/src/js/accordion/accordion.js index 6b2a8b6c4..e6c79a3f9 100644 --- a/src/js/accordion/accordion.js +++ b/src/js/accordion/accordion.js @@ -21,9 +21,10 @@ const getAccordionType = (classes) => { const callback = (mutations, observer) => { const items = document.querySelectorAll(`.${HelfiAccordion.accordionWrapper}`); + if (items.length > window.helfiAccordions.length) { try { - items.forEach((accordionElement, index) => { + items.forEach((accordionElement, index) => { const type = getAccordionType(Array.from(accordionElement.classList)); const isHeaderless = HelfiAccordion.isHeaderless(type); diff --git a/templates/component/accordion.twig b/templates/component/accordion.twig index ef2a5ca18..3cf864185 100644 --- a/templates/component/accordion.twig +++ b/templates/component/accordion.twig @@ -10,9 +10,10 @@ {% set controlled_content_id = "accordion-item-content--" ~ random() %} {% set close_button_labelled_by_id = "accordion-item__button--close--" ~ random() %} +{% set unique_id = heading[0]['#context']['value']|clean_class|clean_unique_id %} -
- <{{ heading_level|default('h2') }} class="accordion-item__header helfi-accordion__header" data-accordion-id="{{heading[0]['#context']['value']|clean_class}}"> +
+ <{{ heading_level|default('h2') }} class="accordion-item__header helfi-accordion__header" data-accordion-id="{{unique_id}}">