-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Disclosure and Accordion elements
- Loading branch information
1 parent
6c62efd
commit 0df80d3
Showing
6 changed files
with
265 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Accordion | ||
|
||
**A custom element for building accessible accordions.** | ||
|
||
The accordion element wraps multiple disclosure elements, and ensures only one of these is expanded at a time. | ||
|
||
## Example | ||
|
||
```js | ||
import { AccordionElement } from 'inclusive-elements'; | ||
|
||
window.customElements.define('ui-accordion', AccordionElement); | ||
``` | ||
|
||
```html | ||
<ui-accordion> | ||
<ui-disclosure> | ||
<h2> | ||
<button type="button">Section A</button> | ||
</h2> | ||
<div> | ||
Details | ||
</div> | ||
</ui-disclosure> | ||
|
||
<ui-disclosure> | ||
<h2> | ||
<button type="button">Section B</button> | ||
</h2> | ||
<div> | ||
Details | ||
</div> | ||
</ui-disclosure> | ||
</ui-accordion> | ||
``` | ||
|
||
## Behavior | ||
|
||
- Whenever a direct child `<ui-disclosure>` element is opened, sibling `<ui-disclosure>` elements will be closed. | ||
|
||
- If the `required` attribute is present, the `<ui-disclosure>` element that is currently open will be `disabled`. | ||
|
||
## Further Reading | ||
|
||
- [WAI-ARIA Authoring Practices: Accordion](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import DisclosureElement from '../disclosure/disclosure'; | ||
|
||
export default class AccordionElement extends HTMLElement { | ||
public connectedCallback(): void { | ||
this.addEventListener('open', this.onOpen, { capture: true }); | ||
} | ||
|
||
public disconnectedCallback(): void { | ||
this.removeEventListener('open', this.onOpen, { capture: true }); | ||
} | ||
|
||
private onOpen = (e: Event) => { | ||
this.querySelectorAll<DisclosureElement>( | ||
':scope > ui-disclosure' | ||
).forEach((el) => { | ||
el.open = el === e.target; | ||
if (this.required) { | ||
el.toggleAttribute('disabled', el === e.target); | ||
} | ||
}); | ||
}; | ||
|
||
get required() { | ||
return this.hasAttribute('required'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Disclosure Widget | ||
|
||
**A custom element for building accessible disclosure widgets.** | ||
|
||
## Example | ||
|
||
```js | ||
import { DisclosureElement } from 'inclusive-elements'; | ||
|
||
window.customElements.define('ui-disclosure', DisclosureElement); | ||
``` | ||
|
||
```html | ||
<ui-disclosure> | ||
<button type="button">Summary</button> | ||
<div>Details</div> | ||
</ui-disclosure> | ||
``` | ||
|
||
## Behavior | ||
|
||
- The first descendant that is a `<button>` or has `role="button"` will be given the `aria-expanded` attribute, which will reflect the open state of the disclosure widget. | ||
|
||
- Clicking the button will toggle the disclosure widget. The `hidden` attribute will be toggled on the content element. | ||
|
||
## API | ||
|
||
```js | ||
const disclosure = document.querySelector('ui-disclosure'); | ||
|
||
// Programatically open and close the widget. | ||
disclosure.open = true; | ||
|
||
disclosure.addEventListener('open', callback); | ||
disclosure.addEventListener('close', callback); | ||
``` | ||
|
||
```css | ||
/* Transitions can be applied to the content using hello-goodbye */ | ||
@media (prefers-reduced-motion: no-preference) { | ||
ui-disclosure > .enter-active, | ||
ui-disclosure > .leave-active { | ||
transition: all 0.5s; | ||
} | ||
|
||
ui-disclosure > .enter-from, | ||
ui-disclosure > .leave-to { | ||
opacity: 0; | ||
transform: scale(0.5); | ||
} | ||
} | ||
``` | ||
|
||
## Further Reading | ||
|
||
- [WAI-ARIA Authoring Practices: Disclosure](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) | ||
- [Scott O'Hara: The details and summary elements, again](https://www.scottohara.me/blog/2022/09/12/details-summary.html) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { cancel, goodbye, hello } from 'hello-goodbye'; | ||
import { shouldOpenInNewTab } from '../utils'; | ||
|
||
export default class DisclosureElement extends HTMLElement { | ||
static get observedAttributes() { | ||
return ['open']; | ||
} | ||
|
||
public connectedCallback(): void { | ||
this.content.hidden = !this.open; | ||
|
||
this.button.setAttribute('aria-expanded', String(this.open)); | ||
|
||
this.button.addEventListener('click', this.onButtonClick); | ||
} | ||
|
||
public disconnectedCallback(): void { | ||
cancel(this.content); | ||
|
||
this.button.removeAttribute('aria-expanded'); | ||
this.button.removeEventListener('click', this.onButtonClick); | ||
} | ||
|
||
private onButtonClick = (e: MouseEvent) => { | ||
if (!shouldOpenInNewTab(e) && !this.disabled) { | ||
this.open = !this.open; | ||
e.preventDefault(); | ||
} | ||
}; | ||
|
||
get open() { | ||
return this.hasAttribute('open'); | ||
} | ||
|
||
set open(val) { | ||
if (val) { | ||
this.setAttribute('open', ''); | ||
} else { | ||
this.removeAttribute('open'); | ||
} | ||
} | ||
|
||
get disabled() { | ||
return this.hasAttribute('disabled'); | ||
} | ||
|
||
public attributeChangedCallback( | ||
name: string, | ||
oldValue: string, | ||
newValue: string | ||
): void { | ||
if (name !== 'open') return; | ||
|
||
if (newValue !== null) { | ||
this.wasOpened(); | ||
} else { | ||
this.wasClosed(); | ||
} | ||
} | ||
|
||
private wasOpened() { | ||
if (!this.content.hidden) return; | ||
|
||
this.content.hidden = false; | ||
|
||
hello(this.content); | ||
|
||
this.button.setAttribute('aria-expanded', 'true'); | ||
|
||
this.dispatchEvent(new Event('open')); | ||
} | ||
|
||
private wasClosed() { | ||
if (this.content.hidden) return; | ||
|
||
this.button.setAttribute('aria-expanded', 'false'); | ||
|
||
goodbye(this.content, { | ||
finish: () => (this.content.hidden = true), | ||
}); | ||
|
||
this.dispatchEvent(new Event('close')); | ||
} | ||
|
||
private get button(): HTMLElement { | ||
return this.querySelector('button, [role=button]')!; | ||
} | ||
|
||
private get content(): HTMLElement { | ||
return this.children[1] as HTMLElement; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters