Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(collapse): support w3c WAI-ARIA pattern #1186

Merged
merged 4 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/docs/components/Collapse.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

<div class="vp-doc">

The **Collapse** component is an easy way to toggle the visibility of content.
The **Collapse** component is an easy way to toggle the visibility of content with show/hide functionality.
The component supports the W3C ARIA APG [Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).

</div>

Expand Down Expand Up @@ -41,6 +42,7 @@ The **Collapse** component is an easy way to toggle the visibility of content.
| open | Whether collapse is open or not, use v-model:open to make it two-way binding | boolean | - | <code style='white-space: nowrap; padding: 0;'>true</code> |
| override | Override existing theme classes completely | boolean | - | |
| position | Trigger position | "bottom" \| "top" | `top`, `bottom` | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>collapse: {<br>&nbsp;&nbsp;position: "top"<br>}</code> |
| triggerId | Id property of the trigger container - default is an uuid | string | - | <code style='white-space: nowrap; padding: 0;'>useId()</code> |

### Events

Expand Down
44 changes: 23 additions & 21 deletions packages/oruga/src/components/collapse/Collapse.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { useId } from "vue";
import { computed, useId } from "vue";

import { getDefault } from "@/utils/config";
import { defineClasses } from "@/composables";
Expand All @@ -17,12 +17,13 @@ defineOptions({
configField: "collapse",
});

withDefaults(defineProps<CollapseProps>(), {
const props = withDefaults(defineProps<CollapseProps>(), {
override: undefined,
open: true,
animation: () => getDefault("collapse.animation", "fade"),
contentId: () => useId(),
position: () => getDefault("collapse.position", "top"),
contentId: () => useId(),
triggerId: () => useId(),
});

const emits = defineEmits<{
Expand All @@ -48,7 +49,15 @@ function toggle(): void {

// --- Computed Component Classes ---

const rootClasses = defineClasses(["rootClass", "o-clps"]);
const rootClasses = defineClasses(
["rootClass", "o-clps"],
[
"positionClass",
"o-clps--",
computed(() => props.position),
computed(() => !!props.position),
],
);

const triggerClasses = defineClasses(["triggerClass", "o-clps__trigger"]);

Expand All @@ -58,12 +67,15 @@ const contentClasses = defineClasses(["contentClass", "o-clps__content"]);
<template>
<div :class="rootClasses" data-oruga="collapse">
<div
v-if="position === 'top'"
:id="triggerId"
:class="triggerClasses"
role="button"
tabindex="0"
:aria-controls="contentId"
:aria-expanded="isOpen"
@click="toggle"
@keydown.enter="toggle">
@keydown.enter="toggle"
@keydown.space="toggle">
<!--
@slot Define the collapse trigger
@binding {boolean} open collapse open state
Expand All @@ -72,26 +84,16 @@ const contentClasses = defineClasses(["contentClass", "o-clps__content"]);
</div>

<Transition :name="animation">
<div v-show="isOpen" :id="contentId" :class="contentClasses">
<div
v-show="isOpen"
:id="contentId"
:class="contentClasses"
:aria-labelledby="triggerId">
<!--
@slot Default content
-->
<slot />
</div>
</Transition>

<div
v-if="position === 'bottom'"
:class="triggerClasses"
role="button"
tabindex="0"
@click="toggle"
@keydown.enter="toggle">
<!--
@slot Define the collapse trigger
@binding {boolean} open collapse open state
-->
<slot name="trigger" :open="isOpen" />
</div>
</div>
</template>

This file was deleted.

26 changes: 19 additions & 7 deletions packages/oruga/src/components/collapse/examples/base.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
<template>
<section>
<o-collapse :open="false" aria-id="contentIdForA11y1">
<template #trigger>
<o-button
label="Click me!"
variant="primary"
aria-controls="contentIdForA11y1" />
<o-collapse :open="false" trigger-class="trigger">
<template #trigger="{ open }">
<b>{{ open ? "Close" : "Open" }} Collapse!</b>
</template>

<div class="notification">
<h3>Subtitle</h3>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<br />
Nulla accumsan, metus ultrices eleifend gravida, nulla nunc
varius lectus, nec rutrum justo nibh eu lectus. <br />
varius lectus, nec rutrum justo nibh eu lectus.
<br />
Ut vulputate semper dui. Fusce erat odio, sollicitudin vel
erat vel, interdum mattis neque.
</p>
</div>
</o-collapse>
</section>
</template>

<style lang="css" scoped>
:deep(.trigger) {
width: 100%;
padding: 1rem;
background-color: var(--vp-c-brand-1);
color: white;
box-shadow:
0 2px 3px hsla(0, 0%, 4%, 0.1),
0 0 0 1px hsla(0, 0%, 4%, 0.1);
text-align: center;
}
</style>
22 changes: 15 additions & 7 deletions packages/oruga/src/components/collapse/examples/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,26 @@ import BaseCode from "./base.vue?raw";

import Accordion from "./accordion.vue";
import AccordionCode from "./accordion.vue?raw";

import AccordionButtons from "./accordion-buttons.vue";
import AccordionButtonsCode from "./accordion-buttons.vue?raw";
</script>

<template>
<div class="vp-doc">
<h3 id="base">Base</h3>
<p>Click the buttons below to show and hide the content.</p>
<p>
A custom trigger can be passed in the <code>trigger</code> slot. The
</p>
<div class="info custom-block">
<p class="custom-block-title">Accessibility Note:</p>
The trigger container is already an interactive element with the
<code>role="button"</code> attribute. For accessibility reasons,
prevent adding other interactive elements such as buttons to avoid
<a
target="_blank"
href="https://accessibilityinsights.io/info-examples/web/nested-interactive/">
nested-interactive
</a>
accessibility problems.
</div>
<ExampleViewer :component="Base" :code="BaseCode" />

<h3 id="accordion">Accordion</h3>
Expand All @@ -21,8 +32,5 @@ import AccordionButtonsCode from "./accordion-buttons.vue?raw";
behaviour.
</p>
<ExampleViewer :component="Accordion" :code="AccordionCode" />
<ExampleViewer
:component="AccordionButtons"
:code="AccordionButtonsCode" />
</div>
</template>
3 changes: 2 additions & 1 deletion packages/oruga/src/components/collapse/examples/readme.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
The **Collapse** component is an easy way to toggle the visibility of content.
The **Collapse** component is an easy way to toggle the visibility of content with show/hide functionality.
The component supports the W3C ARIA APG [Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).
6 changes: 4 additions & 2 deletions packages/oruga/src/components/collapse/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ export type CollapseProps = {
open?: boolean;
/** Custom animation (transition name) */
animation?: string;
/** Id property of the content container - default is an uuid */
contentId?: string;
/**
* Trigger position
* @values top, bottom
*/
position?: "top" | "bottom";
/** Id property of the content container - default is an uuid */
contentId?: string;
/** Id property of the trigger container - default is an uuid */
triggerId?: string;
} & CollapseClasses;

// class props (will not be displayed in the docs)
Expand Down
23 changes: 0 additions & 23 deletions packages/oruga/src/components/collapse/tests/Collapse.spec.cy.ts

This file was deleted.

92 changes: 0 additions & 92 deletions packages/oruga/src/components/collapse/tests/SimpleCollapse.vue

This file was deleted.

Loading