diff --git a/packages/docs/pages/components/Collapse.md b/packages/docs/pages/components/Collapse.md
index 7adebdc45..449abdd66 100644
--- a/packages/docs/pages/components/Collapse.md
+++ b/packages/docs/pages/components/Collapse.md
@@ -4,9 +4,9 @@
-The **Collapse** component is an easy way to toggle the visibility of content with show/hide functionality.
-It has two elements: a disclosure button and a section of content whose visibility is controlled by the button.
-The component implements the W3C ARIA APG [Disclosure (Show/Hide) Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) and also supports the W3C ARIA APG [Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).
+The **Collapse** component (also known as _accordion_) is a disclosure widget which is used to toggle the display of further information.
+The component is implemented based on the [HTML \ element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/details) and consists of two elements: a short disclosure label (trigger) and a section of content whose visibility it controls; the content can be 'collapsed' or 'expanded'.
+This component implements the W3C ARIA APG [Disclosure (Show/Hide) Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) and also supports the W3C ARIA APG [Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).
@@ -22,7 +22,7 @@ The component implements the W3C ARIA APG [Disclosure (Show/Hide) Pattern](https
## Collapse Component
-> An easy way to toggle what you want.
+> An easy disclosure widget to toggle content visability.
```html
@@ -30,23 +30,24 @@ The component implements the W3C ARIA APG [Disclosure (Show/Hide) Pattern](https
### Props
-| Prop name | Description | Type | Values | Default |
-| --------- | ---------------------------------------------------------------------------- | ----------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
-| animation | Custom animation (transition name) | string | - |
From config:
collapse: {
animation: "fade"
} |
-| contentId | Id property of the content container - default is an uuid | string | - | useId() |
-| expanded | Expand the trigger to fullwidth | boolean | - | false |
-| open | Whether collapse is open or not, use v-model:open to make it two-way binding | boolean | - | true |
-| override | Override existing theme classes completely | boolean | - | |
-| position | Trigger position | "bottom" \| "top" | `top`, `bottom` | From config:
collapse: {
position: "top"
} |
-| triggerId | Id property of the trigger container - default is an uuid | string | - | useId() |
+| Prop name | Description | Type | Values | Default |
+| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
+| contentId | Id property of the content container - default is an uuid | string | - | useId() |
+| expanded | Expand the trigger to fullwidth | boolean | - | false |
+| label | Some label displayed in the summary element - unnecessary when trigger slot is used | string | - | |
+| name | Setting the same name to multiple collapse elements connects them together,
with only one open at a time.
This allows to easily create UI features such as accordions. | string | - | |
+| open | Whether collapse is open or not, use v-model:open to make it two-way binding | boolean | - | false |
+| override | Override existing theme classes completely | boolean | - | |
+| position | Trigger position | "bottom" \| "top" | `top`, `bottom` | From config:
collapse: {
position: "bottom"
} |
+| triggerId | Id property of the trigger container - default is an uuid | string | - | useId() |
### Events
-| Event name | Properties | Description |
-| ----------- | --------------------------------------- | ------------------------- |
-| update:open | **value** `boolean` - updated open prop | open prop two-way binding |
-| open | | on collapse opened |
-| close | | on collapse closed |
+| Event name | Properties | Description |
+| ----------- | ------------------------------------------------- | ------------------------- |
+| update:open | **value** `boolean` - updated open prop | open prop two-way binding |
+| open | **event** `ToggleEvent` - the native toggle event | on collapse opened |
+| close | **event** `ToggleEvent` - the native toggle event | on collapse closed |
### Slots
diff --git a/packages/oruga/src/components/collapse/Collapse.vue b/packages/oruga/src/components/collapse/Collapse.vue
index fde2f38df..a45272a08 100644
--- a/packages/oruga/src/components/collapse/Collapse.vue
+++ b/packages/oruga/src/components/collapse/Collapse.vue
@@ -1,5 +1,5 @@
-
-
+
-
-
+ :aria-expanded="isOpen">
+
+ {{ label }}
+
+
-
-
-
-
-
-
+
+
+
+
diff --git a/packages/oruga/src/components/collapse/examples/accordion.vue b/packages/oruga/src/components/collapse/examples/accordion.vue
index 53f7bc71d..a420c96d6 100644
--- a/packages/oruga/src/components/collapse/examples/accordion.vue
+++ b/packages/oruga/src/components/collapse/examples/accordion.vue
@@ -1,14 +1,12 @@
### Base
-A custom trigger can be passed in the `trigger` slot.
+A custom trigger can be passed in the `trigger` slot oder by the `label` property.
+When the controlled content is hidden, the trigger is often styled as a typical push button with a right-pointing arrow or triangle, to indicate that activating the trigger will display the hidden content.
+When the content is visible, the arrow or triangle usually points down.
::: info Accessibility Note:
-The trigger container is already an interactive element with the `role="button"` attribute. For accessibility reasons, prevent adding other interactive elements such as buttons to avoid [nested-interactive](https://accessibilityinsights.io/info-examples/web/nested-interactive/) accessibility problems.
+The trigger container is already an interactive element with the `role="button"` attribute. For accessibility reasons, prevent adding other interactive elements such as buttons in the `trigger` slot to avoid [nested-interactive](https://accessibilityinsights.io/info-examples/web/nested-interactive/) accessibility problems.
:::
+### Position
+
+The collapse can be configured by the `position` property to open to top instead of bottom.
+
+
+
### Accordion
-Combine multiple collapse components to create an accordion behaviour.
+Using the [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/details#name) property multiple collapse components can be connected to create an accordion behaviour, with only one content open at a time.
diff --git a/packages/oruga/src/components/collapse/examples/inspector.vue b/packages/oruga/src/components/collapse/examples/inspector.vue
index 95456e3c1..ebb4cc782 100644
--- a/packages/oruga/src/components/collapse/examples/inspector.vue
+++ b/packages/oruga/src/components/collapse/examples/inspector.vue
@@ -36,25 +36,15 @@ const inspectData: InspectData = {
-
-
-
+
+
+
+
+ {{ open ? "Close" : "Open" }} Collapse!
+
-
+ Collapse Content
diff --git a/packages/oruga/src/components/collapse/examples/position.vue b/packages/oruga/src/components/collapse/examples/position.vue
new file mode 100644
index 000000000..f0922bf76
--- /dev/null
+++ b/packages/oruga/src/components/collapse/examples/position.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+ {{ open ? "Close" : "Open" }} Collapse!
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+ Nulla accumsan, metus ultrices eleifend gravida, nulla nunc
+ varius lectus, nec rutrum justo nibh eu lectus.
+
+ Ut vulputate semper dui. Fusce erat odio, sollicitudin vel
+ erat vel, interdum mattis neque.
+
+
+
+
+
+
+
diff --git a/packages/oruga/src/components/collapse/examples/readme.md b/packages/oruga/src/components/collapse/examples/readme.md
index 796252553..17360c7f5 100644
--- a/packages/oruga/src/components/collapse/examples/readme.md
+++ b/packages/oruga/src/components/collapse/examples/readme.md
@@ -1,3 +1,3 @@
-The **Collapse** component is an easy way to toggle the visibility of content with show/hide functionality.
-It has two elements: a disclosure button and a section of content whose visibility is controlled by the button.
-The component implements the W3C ARIA APG [Disclosure (Show/Hide) Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) and also supports the W3C ARIA APG [Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).
+The **Collapse** component (also known as _accordion_) is a disclosure widget which is used to toggle the display of further information.
+The component is implemented based on the [HTML \ element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/details) and consists of two elements: a short disclosure label (trigger) and a section of content whose visibility it controls; the content can be 'collapsed' or 'expanded'.
+This component implements the W3C ARIA APG [Disclosure (Show/Hide) Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) and also supports the W3C ARIA APG [Accordion Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).
diff --git a/packages/oruga/src/components/collapse/props.ts b/packages/oruga/src/components/collapse/props.ts
index 7461373fb..86956f7fa 100644
--- a/packages/oruga/src/components/collapse/props.ts
+++ b/packages/oruga/src/components/collapse/props.ts
@@ -5,8 +5,14 @@ export type CollapseProps = {
override?: boolean;
/** Whether collapse is open or not, use v-model:open to make it two-way binding */
open?: boolean;
- /** Custom animation (transition name) */
- animation?: string;
+ /** Some label displayed in the summary element - unnecessary when trigger slot is used */
+ label?: string;
+ /**
+ * Setting the same name to multiple collapse elements connects them together,
+ * with only one open at a time.
+ * This allows to easily create UI features such as accordions.
+ */
+ name?: string;
/**
* Trigger position
* @values top, bottom
diff --git a/packages/oruga/src/components/collapse/tests/__snapshots__/collapse.unit.test.ts.snap b/packages/oruga/src/components/collapse/tests/__snapshots__/collapse.unit.test.ts.snap
index a91254610..ecd60a99d 100644
--- a/packages/oruga/src/components/collapse/tests/__snapshots__/collapse.unit.test.ts.snap
+++ b/packages/oruga/src/components/collapse/tests/__snapshots__/collapse.unit.test.ts.snap
@@ -1,10 +1,8 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`OCollapse tests > render correctly 1`] = `
-""
+"
+ Trigger Button
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+ "
`;
diff --git a/packages/oruga/src/components/collapse/tests/collapse.axe.test.ts b/packages/oruga/src/components/collapse/tests/collapse.axe.test.ts
new file mode 100644
index 000000000..971ae1569
--- /dev/null
+++ b/packages/oruga/src/components/collapse/tests/collapse.axe.test.ts
@@ -0,0 +1,56 @@
+import { describe, test, expect, afterEach } from "vitest";
+import { enableAutoUnmount, mount } from "@vue/test-utils";
+import { axe } from "jest-axe";
+import { nextTick } from "vue";
+
+import OCollapse from "../Collapse.vue";
+import type { CollapseProps } from "../props";
+
+describe("OCollapse axe test", () => {
+ enableAutoUnmount(afterEach);
+
+ const a11yCases: { title: string; props?: CollapseProps }[] = [
+ {
+ title: "axe collapse - base case",
+ },
+ {
+ title: "axe collapse - open case",
+ props: {
+ open: true,
+ },
+ },
+ {
+ title: "axe collapse - name case",
+ props: {
+ name: "some-collapse",
+ },
+ },
+ {
+ title: "axe collapse - expanded case",
+ props: {
+ expanded: true,
+ },
+ },
+ {
+ title: "axe collapse - position case",
+ props: {
+ position: "top",
+ },
+ },
+ ];
+
+ test.each(a11yCases)("$title", async ({ props }) => {
+ const wrapper = mount(OCollapse, {
+ props: { open: false, ...props },
+ slots: {
+ trigger: "Trigger Button
",
+ default:
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+ },
+ attachTo: document.body,
+ });
+ await nextTick(); // await all content got rendered
+
+ expect(await axe(wrapper.element)).toHaveNoViolations();
+ });
+});
diff --git a/packages/oruga/src/components/collapse/tests/collapse.browser.test.ts b/packages/oruga/src/components/collapse/tests/collapse.browser.test.ts
index 1bc2eb442..c4d8b3a7a 100644
--- a/packages/oruga/src/components/collapse/tests/collapse.browser.test.ts
+++ b/packages/oruga/src/components/collapse/tests/collapse.browser.test.ts
@@ -1,54 +1,46 @@
import { describe, expect, test } from "vitest";
import { render } from "vitest-browser-vue";
-import { defineComponent, h, ref } from "vue";
+import { defineComponent, h, type VNode } from "vue";
import OCollapse from "../Collapse.vue";
-const SimpleCollapse = defineComponent({
- name: "SimpleCollapse",
- setup() {
- const isOpen = ref(0);
- const collapses = [
- {
- title: "Title 1",
- text: "Text 1",
- index: 0,
- },
- {
- title: "Title 2",
- text: "Text 2",
- index: 1,
- },
- {
- title: "Title 3",
- text: "Text 3",
- index: 2,
- },
- ];
- return { isOpen, collapses };
+const collapses = [
+ {
+ title: "Title 1",
+ text: "Text 1",
+ index: 0,
},
- render() {
- return h(
+ {
+ title: "Title 2",
+ text: "Text 2",
+ index: 1,
+ },
+ {
+ title: "Title 3",
+ text: "Text 3",
+ index: 2,
+ },
+];
+
+const SimpleCollapse = defineComponent(() => {
+ return (): VNode =>
+ h(
"section",
{},
- this.collapses.map(({ title, text, index }) =>
+ collapses.map(({ title, text, index }) =>
h(
OCollapse,
{
key: index,
"data-testid": `collapse-${index}`,
- open: this.isOpen == index,
- onOpen: () => {
- this.isOpen = index;
- },
+ name: "browser-test-collapsables",
+ open: index === 0,
},
{
trigger: ({ open }) =>
- h("div", {}, [
- h("p", {}, [
- `${title} (${open ? "Open" : "Closed"})`,
- ]),
+ h("p", { "data-testid": `trigger-${index}` }, [
+ `${title} (${open ? "Open" : "Closed"})`,
]),
default: () =>
h("div", { "data-testid": `content-${index}` }, [
@@ -58,7 +50,6 @@ const SimpleCollapse = defineComponent({
),
),
);
- },
});
describe("", () => {
@@ -69,7 +60,7 @@ describe("", () => {
const secondContent = screen.getByTestId("content-1");
await expect.element(secondContent).not.toBeVisible();
const second = screen.getByTestId("collapse-1");
- const trigger = second.getByRole("button");
+ const trigger = second.getByTestId("trigger-1");
await trigger.click();
await expect.element(firstContent).not.toBeVisible();
await expect.element(secondContent).toBeVisible();
diff --git a/packages/oruga/src/components/collapse/tests/collapse.unit.test.ts b/packages/oruga/src/components/collapse/tests/collapse.unit.test.ts
index 7c8a339c2..7ec004a54 100644
--- a/packages/oruga/src/components/collapse/tests/collapse.unit.test.ts
+++ b/packages/oruga/src/components/collapse/tests/collapse.unit.test.ts
@@ -2,15 +2,64 @@ import { describe, test, expect, afterEach } from "vitest";
import { enableAutoUnmount, mount } from "@vue/test-utils";
import OCollapse from "@/components/collapse/Collapse.vue";
+import { setTimeout } from "timers/promises";
describe("OCollapse tests", () => {
enableAutoUnmount(afterEach);
test("render correctly", () => {
- const wrapper = mount(OCollapse);
+ const wrapper = mount(OCollapse, {
+ props: { label: "Trigger Button" },
+ slots: {
+ default:
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+ },
+ });
expect(!!wrapper.vm).toBeTruthy();
expect(wrapper.exists()).toBeTruthy();
expect(wrapper.attributes("data-oruga")).toBe("collapse");
expect(wrapper.html()).toMatchSnapshot();
+
+ const summary = wrapper.find("summary");
+ expect(summary.exists()).toBeTruthy();
+ expect(summary.classes("o-collapse__trigger"));
+ expect(summary.text()).toBe("Trigger Button");
+
+ const content = wrapper.find("div");
+ expect(content.exists()).toBeTruthy();
+ expect(summary.classes("o-collapse__content"));
+ expect(content.text()).toBe(
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+ );
+ });
+
+ test("emit events when toggled", async () => {
+ const wrapper = mount(OCollapse, {
+ props: { open: false },
+ slots: {
+ trigger: "Trigger Button",
+ default:
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+ },
+ });
+
+ const summary = wrapper.find("summary");
+ expect(summary.exists()).toBeTruthy();
+
+ // open element
+ await summary.trigger("click");
+ await setTimeout(); // await dom handlers run
+
+ expect(wrapper.emitted("update:open")).toHaveLength(1);
+ expect(wrapper.emitted("open")).toHaveLength(1);
+ expect(wrapper.emitted("close")).toBeUndefined();
+
+ // click again to close element
+ await summary.trigger("click");
+ await setTimeout(); // await dom handlers run
+
+ expect(wrapper.emitted("update:open")).toHaveLength(2);
+ expect(wrapper.emitted("open")).toHaveLength(1);
+ expect(wrapper.emitted("close")).toHaveLength(1);
});
});
diff --git a/packages/oruga/src/components/dialog/tests/dialog.axe.test.ts b/packages/oruga/src/components/dialog/tests/dialog.axe.test.ts
index 7a8fcb8b1..5b4f9e685 100644
--- a/packages/oruga/src/components/dialog/tests/dialog.axe.test.ts
+++ b/packages/oruga/src/components/dialog/tests/dialog.axe.test.ts
@@ -22,7 +22,7 @@ describe("ODialog axe test", () => {
},
},
{
- title: "axe dialog - modal case case",
+ title: "axe dialog - modal case",
props: {
backdrop: true,
title: "Adcanced Title",
diff --git a/packages/oruga/src/config.d.ts b/packages/oruga/src/config.d.ts
index f196ecce0..35a3c6f1c 100644
--- a/packages/oruga/src/config.d.ts
+++ b/packages/oruga/src/config.d.ts
@@ -503,10 +503,6 @@ In addition, any CSS selector string or an actual DOM node can be used.
}>;
collapse?: ComponentConfigBase &
Partial<{
- /**
- * Custom animation (transition name)
- */
- animation: string;
/**
* Trigger position
*/