Skip to content
Snippets Groups Projects
Commit 5bbf8f22 authored by Markus Kalijärvi's avatar Markus Kalijärvi
Browse files

Merge branch 'NXSTAGE-400-accordion-anchors' into 'development'

Nxstage 400 accordion anchors

See merge request julkiset-sivut/design-system-lib!44
parents eecf5e29 3d888165
No related branches found
No related tags found
No related merge requests found
import { Component, Prop,h } from '@stencil/core';
import {Component, Prop, h} from '@stencil/core';
@Component({
tag: 'hy-accordion-container',
......@@ -8,16 +8,22 @@ import { Component, Prop,h } from '@stencil/core';
export class AccordionContainer {
@Prop() accordionid?: string;
/*
* data-allow-toggle
Allow for each toggle to both open and close individually
data-allow-multiple
Allow for multiple accordion sections to be expanded at the same time.
Assumes data-allow-toggle otherwise you would not be able to close any of the accordions
*/
render() {
const classAttributes = ["hy-accordion-container", "js-hy-accordion"];
const id = this.accordionid.toLowerCase().replace(/\W/g,'-');
const classAttributes = ['hy-accordion-container', 'js-hy-accordion'];
const id = this.accordionid.toLowerCase().replace(/\W/g, '-');
return (
<div id={id} class={classAttributes.join(" ")} data-allow-multiple="true" data-allow-toggle="true">
<div id={id} class={classAttributes.join(' ')} data-allow-multiple="true" data-allow-toggle="true">
<slot></slot>
</div>
);
}
}
......@@ -30,6 +30,12 @@
transition: all 0.25s ease-in-out;
width: 100%;
.hy-accordion__link {
text-decoration: none;
&:hover {
text-decoration: none;
}
}
.hy-accordion__button {
align-items: center;
border: none;
......
import {Component, Prop, State, Element, h} from '@stencil/core';
import {Component, Listen, Prop, State, Element, h} from '@stencil/core';
@Component({
tag: 'hy-accordion-item',
......@@ -14,116 +14,159 @@ export class AccordionItem {
this.ready = true;
}
render() {
componentDidRender() {
if (window.location.hash) {
this.expandPanelByAnchor(window.location.hash);
}
}
@Listen('hashchange', {target: 'window'})
onHashChange() {
if (window.location.hash) {
this.expandPanelByAnchor(window.location.hash);
}
}
@Listen('keydown')
handleKeyDown(event: KeyboardEvent) {
const containerId = this.el.parentElement.id;
let accordion = document.querySelectorAll(`#${containerId}`)[0];
const triggers = Array.prototype.slice.call(accordion.querySelectorAll('.hy-accordion__button'));
let target = event.target as HTMLButtonElement;
const key = event.which.toString();
// 33 = Page Up, 34 = Page Down
const ctrlModifier = event.ctrlKey && key.match(/33|34/);
if (target.classList.contains('hy-accordion__button')) {
// Up/ Down arrow and Control + Page Up/ Page Down keyboard operations
// 38 = Up, 40 = Down
if (key.match(/38|40/) || ctrlModifier) {
const index = triggers.indexOf(target);
const direction = key.match(/34|40/) ? 1 : -1;
const length = triggers.length;
const newIndex = (index + length + direction) % length;
triggers[newIndex].focus();
event.preventDefault();
} else if (key.match(/35|36/)) {
// 35 = End, 36 = Home keyboard operations
switch (key) {
// Go to first accordion
case '36':
triggers[0].focus();
break;
// Go to last accordion
case '35':
triggers[triggers.length - 1].focus();
break;
}
event.preventDefault();
}
}
}
@Listen('click')
handleClick(event) {
const containerId = this.el.parentElement.id;
function collapseSection(element) {
element.style.height = 0 + 'px';
element.setAttribute('data-collapsed', 'true');
setTimeout(() => {
element.style.display = 'none';
}, 250);
let target = event.target as HTMLTextAreaElement;
const targetElement = target.tagName.toLowerCase();
const possibleTags = [targetElement].some((r) => ['svg', 'path', 'button', 'a'].indexOf(r) >= 0);
let accordion = document.querySelectorAll(`#${containerId}`)[0];
const allowMultiple = accordion.hasAttribute('data-allow-multiple');
const allowToggle = allowMultiple ? allowMultiple : accordion.hasAttribute('data-allow-toggle');
if (target && possibleTags) {
if (targetElement !== 'button') {
target = target.closest('.hy-accordion__button');
}
let targetParent = target.closest('.hy-accordion__item');
let targetContent = targetParent.querySelectorAll('.hy-accordion__content')[0];
const isExpanded = target.getAttribute('aria-expanded') == 'true';
const active = accordion.querySelector('[aria-expanded="true"]');
if (!allowMultiple && active && active !== target) {
active.setAttribute('aria-expanded', 'false');
this.collapseSection(targetContent);
if (targetParent.classList.contains('hy-accordion__item__is-open')) {
targetParent.classList.remove('hy-accordion__item__is-open');
}
accordion.querySelector(`#${active.getAttribute('aria-controls')}`).setAttribute('aria-hidden', 'true');
if (!allowToggle) {
active.removeAttribute('aria-disabled');
}
}
if (!isExpanded) {
this.expandSection(targetContent);
target.setAttribute('aria-expanded', 'true');
targetParent.classList.add('hy-accordion__item__is-open');
accordion.querySelector(`#${target.getAttribute('aria-controls')}`).setAttribute('aria-hidden', 'false');
if (!allowToggle) {
target.setAttribute('aria-disabled', 'true');
}
} else if (allowToggle && isExpanded) {
target.setAttribute('aria-expanded', 'false');
this.collapseSection(targetContent);
if (targetParent.classList.contains('hy-accordion__item__is-open')) {
targetParent.classList.remove('hy-accordion__item__is-open');
}
accordion.querySelector(`#${target.getAttribute('aria-controls')}`).setAttribute('aria-hidden', 'true');
}
event.preventDefault();
event.stopImmediatePropagation();
}
}
function expandSection(element) {
element.style.display = 'block';
element.style.height = element.scrollHeight + 'px';
element.setAttribute('data-collapsed', 'false');
expandPanelByAnchor(anchor) {
if (anchor.length > 0) {
anchor = anchor.substr(1);
let target = document.querySelectorAll(`[id=${anchor}]`)[0];
if (target && target.classList.contains('hy-accordion__button')) {
let targetParent = target.closest('.hy-accordion__item');
let targetContent = targetParent.querySelectorAll('.hy-accordion__content')[0];
this.expandSection(targetContent);
target.setAttribute('aria-expanded', 'true');
targetParent.classList.add('hy-accordion__item__is-open');
const containerId = targetParent.parentElement.parentElement.id;
if (containerId.length > 0) {
let accordion = document.querySelectorAll(`#${containerId}`)[0];
accordion.querySelector(`#${target.getAttribute('aria-controls')}`).setAttribute('aria-hidden', 'false');
}
}
}
}
collapseSection(element) {
element.style.height = 0 + 'px';
element.setAttribute('data-collapsed', 'true');
setTimeout(() => {
element.style.display = 'none';
}, 250);
}
expandSection(element) {
element.style.display = 'block';
element.style.height = element.scrollHeight + 'px';
element.setAttribute('data-collapsed', 'false');
}
render() {
const containerId = this.el.parentElement.id;
if (this.ready && containerId.length > 0) {
document.querySelectorAll(`#${containerId}`).forEach(function (accordion) {
const allowMultiple = accordion.hasAttribute('data-allow-multiple');
const allowToggle = allowMultiple ? allowMultiple : accordion.hasAttribute('data-allow-toggle');
const triggers = Array.prototype.slice.call(accordion.querySelectorAll('.hy-accordion__button'));
accordion.addEventListener('click', function (event) {
let target = event.target as HTMLTextAreaElement;
const targetElement = target.tagName.toLowerCase();
const possibleTags = [targetElement].some((r) => ['svg', 'path', 'button'].indexOf(r) >= 0);
if (target && possibleTags) {
if (targetElement !== 'button') {
target = target.closest('.hy-accordion__button');
}
let targetParent = target.closest('.hy-accordion__item');
let targetContent = targetParent.querySelectorAll('.hy-accordion__content')[0];
const isExpanded = target.getAttribute('aria-expanded') == 'true';
const active = accordion.querySelector('[aria-expanded="true"]');
if (!allowMultiple && active && active !== target) {
active.setAttribute('aria-expanded', 'false');
collapseSection(targetContent);
if (targetParent.classList.contains('hy-accordion__item__is-open')) {
targetParent.classList.remove('hy-accordion__item__is-open');
}
accordion.querySelector(`#${active.getAttribute('aria-controls')}`).setAttribute('aria-hidden', 'true');
if (!allowToggle) {
active.removeAttribute('aria-disabled');
}
}
if (!isExpanded) {
expandSection(targetContent);
target.setAttribute('aria-expanded', 'true');
targetParent.classList.add('hy-accordion__item__is-open');
accordion.querySelector(`#${target.getAttribute('aria-controls')}`).setAttribute('aria-hidden', 'false');
if (!allowToggle) {
target.setAttribute('aria-disabled', 'true');
}
} else if (allowToggle && isExpanded) {
target.setAttribute('aria-expanded', 'false');
collapseSection(targetContent);
if (targetParent.classList.contains('hy-accordion__item__is-open')) {
targetParent.classList.remove('hy-accordion__item__is-open');
}
accordion.querySelector(`#${target.getAttribute('aria-controls')}`).setAttribute('aria-hidden', 'true');
}
event.preventDefault();
event.stopImmediatePropagation();
}
});
if (accordion) {
accordion.addEventListener('keydown', function (event: KeyboardEvent) {
const target = event.target as HTMLButtonElement;
const key = event.which.toString();
// 33 = Page Up, 34 = Page Down
const ctrlModifier = event.ctrlKey && key.match(/33|34/);
if (target.classList.contains('hy-accordion__button')) {
// Up/ Down arrow and Control + Page Up/ Page Down keyboard operations
// 38 = Up, 40 = Down
if (key.match(/38|40/) || ctrlModifier) {
const index = triggers.indexOf(target);
const direction = key.match(/34|40/) ? 1 : -1;
const length = triggers.length;
const newIndex = (index + length + direction) % length;
triggers[newIndex].focus();
event.preventDefault();
} else if (key.match(/35|36/)) {
// 35 = End, 36 = Home keyboard operations
switch (key) {
// Go to first accordion
case '36':
triggers[0].focus();
break;
// Go to last accordion
case '35':
triggers[triggers.length - 1].focus();
break;
}
event.preventDefault();
}
}
});
accordion.querySelectorAll('.hy-accordion__button').forEach(function (trigger) {
trigger.addEventListener('focus', function () {
trigger.classList.add('focus');
......@@ -138,20 +181,24 @@ export class AccordionItem {
const classAttributes = ['hy-accordion__item'];
const titleAsId = this.accordiontitle.toLowerCase().replace(/\W/g, '-');
const accordionItemHref = '#' + titleAsId + '--title';
return (
<div class={classAttributes.join(' ')}>
<div class="hy-accordion--heading">
<button
aria-expanded="false"
aria-controls={`${titleAsId}--content`}
class="hy-accordion__button"
id={`${titleAsId}--title`}
>
<span class="hy-accordion--heading__icon">
<hy-icon icon={'hy-icon-caret-down'} size={20} />
</span>
{this.accordiontitle}
</button>
<a href={accordionItemHref} class="hy-accordion__link" tabindex="-1">
<button
aria-expanded="false"
aria-controls={`${titleAsId}--content`}
class="hy-accordion__button"
id={`${titleAsId}--title`}
>
<span class="hy-accordion--heading__icon">
<hy-icon icon={'hy-icon-caret-down'} size={20} />
</span>
{this.accordiontitle}
</button>
</a>
</div>
<div
aria-labelledBy={`${titleAsId}--title`}
......
......@@ -167,13 +167,66 @@
</hy-site-header>
</div>
<section>
<a href="#facebook_ads_example">Click here to see an example of how a company uses Facebook ads.</a>
</section>
<section>
<a href="#this-is-an-accordion-item-3--title">Click here to go to Accordion Item 3</a>
</section>
<hy-banner
text-title="Check upcoming conferences"
text-description="Take advantage of our curated list of high profile conferences near Helsinki and in the Nordics."
url="#this-is-an-accordion-item-2--title"
url-title="Show list of conferences"
sc-label="List of conferences"
>
</hy-banner>
<section>
<a href="#this-is-an-accordion-item-2--title">Click here to go to Accordion Item 2</a>
</section>
<hy-banner
text-title="Check upcoming conferences"
text-description="Take advantage of our curated list of high profile conferences near Helsinki and in the Nordics."
url="#this-is-an-accordion-item-2--title"
url-title="Show list of conferences"
sc-label="List of conferences"
>
</hy-banner>
<section>
<a href="#this-is-an-accordion-item-2--title">Click here to go to Accordion Item 2</a>
</section>
<hy-banner
text-title="Check upcoming conferences"
text-description="Take advantage of our curated list of high profile conferences near Helsinki and in the Nordics."
url="https://helsinki.fi"
url="#this-is-an-accordion-item-2--title"
url-title="Show list of conferences"
sc-label="List of conferences"
>
</hy-banner>
<hy-accordion-container accordionId="example-accordion">
<hy-accordion-item accordiontitle="This is an accordion item 1">
<hy-paragraph-text>Accordion content</hy-paragraph-text>
</hy-accordion-item>
<hy-accordion-item accordiontitle="This is an accordion item 2">
<hy-paragraph-text>Accordion content</hy-paragraph-text>
</hy-accordion-item>
<hy-accordion-item accordiontitle="This is an accordion item 3">
<hy-paragraph-text>Accordion content</hy-paragraph-text>
</hy-accordion-item>
</hy-accordion-container>
<hy-key-figure-group
data-items='[{"heading":"3500 h","description":"Opetusta annettiin Helsingin yliopistossa vuonna 2019"},
{"heading":"87%","description":"Hakijoista valitsi Helsingin yliopiston ensisijaiseksi vaihtoehdoksi."},
{"heading":"4/5","description":"Key Highlight description text updated"}]'
>
</hy-key-figure-group>
<a id="facebook_ads_example">This is the Facebook ad example I want to link to.</a>
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment