Skip to content

Commit

Permalink
fix: improve sd-accordion and sd-quickfact a11y (#1757)
Browse files Browse the repository at this point in the history
<!-- ## Title: Please consider adding the [skip chromatic] flag to the
PR title in case you dont need chromatic testing your changes. -->
## Description:
Closes #1496 #1500

This PR addresses:
- Adapt sd-accordion HTML using `details` and `summary` elements
- Adapt expandable sd-quickfact HTML using `details` and `summary`
elements

## Definition of Reviewable:
<!-- *PR notes: Irrelevant elements should be removed.* -->
- [x] relevant tickets are linked
  • Loading branch information
paulovareiro29 authored Jan 13, 2025
1 parent 3ff42ed commit b83d804
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-tools-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@solid-design-system/components': minor
---

Adapt `sd-accordion` and expandable `sd-quickfact` HTML to use `<details />` and `<summary />` elements.
46 changes: 38 additions & 8 deletions packages/components/src/components/accordion/accordion.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import '../icon/icon';
import { animateTo, shimKeyframesHeightAuto, stopAnimations } from '../../internal/animate';
import { css, html } from 'lit';
import { css } from 'lit';
import { customElement } from '../../internal/register-custom-element';
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry';
import { html } from 'lit/static-html.js';
import { LocalizeController } from '../../utilities/localize';
import { property, query } from 'lit/decorators.js';
import { waitForEvent } from '../../internal/event';
Expand Down Expand Up @@ -45,10 +46,12 @@ export default class SdAccordion extends SolidElement {

public localize = new LocalizeController(this);

@query('[part="base"]') accordion: HTMLElement;
@query('[part="base"]') accordion: HTMLDetailsElement;
@query('[part="header"]') header: HTMLElement;
@query('[part="content"]') body: HTMLElement;

accordionObserver: MutationObserver;

/**
* Indicates whether or not the accordion is open. You can toggle this attribute to show and hide the accordion, or you
* can use the `show()` and `hide()` methods and this attribute will reflect the accordion' open state.
Expand All @@ -61,9 +64,33 @@ export default class SdAccordion extends SolidElement {
firstUpdated() {
this.body.hidden = !this.open;
this.body.style.height = this.open ? 'auto' : '0';

if (this.open) {
this.accordion.open = true;
}

this.accordionObserver = new MutationObserver(changes => {
for (const change of changes) {
if (change.type === 'attributes' && change.attributeName === 'open') {
if (this.accordion.open) {
this.show();
} else {
this.hide();
}
}
}
});
this.accordionObserver.observe(this.accordion, { attributes: true });
}

disconnectedCallback() {
super.disconnectedCallback();
this.accordionObserver?.disconnect();
}

protected handleSummaryClick() {
protected handleSummaryClick(event: MouseEvent) {
event.preventDefault();

if (this.open) {
this.hide();
} else {
Expand Down Expand Up @@ -96,10 +123,12 @@ export default class SdAccordion extends SolidElement {
@watch('open', { waitUntilFirstUpdate: true })
async handleOpenChange() {
if (this.open) {
this.accordion.open = true;
// Show
const slShow = this.emit('sd-show', { cancelable: true });
if (slShow.defaultPrevented) {
this.open = false;
this.accordion.open = false;
return;
}

Expand All @@ -115,6 +144,7 @@ export default class SdAccordion extends SolidElement {
// Hide
const slHide = this.emit('sd-hide', { cancelable: true });
if (slHide.defaultPrevented) {
this.accordion.open = true;
this.open = true;
return;
}
Expand All @@ -126,6 +156,7 @@ export default class SdAccordion extends SolidElement {
this.body.hidden = true;
this.body.style.height = 'auto';

this.accordion.open = false;
this.emit('sd-after-hide');
}
}
Expand All @@ -150,14 +181,13 @@ export default class SdAccordion extends SolidElement {

render() {
return html`
<div part="base" class="border-y border-neutral-400">
<header
<details part="base" class="border-y border-neutral-400">
<summary
part="header"
id="header"
class=${cx(
'flex text-base gap-4 font-bold items-center cursor-pointer select-none px-4 py-3 focus-visible:focus-outline text-primary hover:bg-neutral-200 relative group'
)}
role="button"
aria-expanded=${this.open ? 'true' : 'false'}
aria-controls="content"
tabindex="0"
Expand All @@ -184,11 +214,11 @@ export default class SdAccordion extends SolidElement {
<slot name="collapse-icon" class=${cx(!this.open && 'hidden')}>
<sd-icon library="system" name="chevron-down"></sd-icon> </slot
></span>
</header>
</summary>
<div part="content" id="content" class="overflow-hidden">
<slot part="content__slot" class="block px-4 py-6" role="region" aria-labelledby="header"></slot>
</div>
</div>
</details>
`;
}

Expand Down
22 changes: 14 additions & 8 deletions packages/components/src/components/quickfact/quickfact.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import '../icon/icon';
import { css, html } from 'lit';
import { css } from 'lit';
import { customElement } from '../../internal/register-custom-element';
import { html, literal } from 'lit/static-html.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { property } from 'lit/decorators.js';
import { setDefaultAnimation } from '../../utilities/animation-registry';
Expand All @@ -27,9 +28,13 @@ export default class SdQuickfact extends SdAccordion {
@property({ type: Boolean, reflect: true }) expandable = false;

render() {
const base = this.expandable ? literal`details` : literal`div`;
const header = this.expandable ? literal`summary` : literal`div`;

/* eslint-disable lit/binding-positions, lit/no-invalid-html */
return html`
<div part="base" class="sm:p-6">
<header
<${base} part="base" class="sm:p-6">
<${header}
part="header"
id="header"
class=${cx(
Expand All @@ -55,9 +60,9 @@ export default class SdQuickfact extends SdAccordion {
class=${cx(
'flex flex-auto items-start text-left text-base leading-normal font-normal sm:leading-tight sm:text-3xl sm:text-center',
this.expandable ? 'text-primary' : 'text-black cursor-default'
)}
>${this.summary}</slot
>
)}>
${this.summary}
</slot>
<span
part="summary-icon"
class=${cx(
Expand All @@ -71,12 +76,13 @@ export default class SdQuickfact extends SdAccordion {
<slot name="collapse-icon" class=${cx(!this.open && 'hidden')}>
<sd-icon library="system" name="chevron-down"></sd-icon> </slot
></span>
</header>
</${header}>
<div part="content" id="content" class=${cx('overflow-hidden', !this.expandable && 'hidden')}>
<slot part="content__slot" class="block" role="region" aria-labelledby="header"></slot>
</div>
</div>
</${base}>
`;
/* eslint-enable lit/binding-positions, lit/no-invalid-html */
}

static styles = [
Expand Down

0 comments on commit b83d804

Please sign in to comment.