diff --git a/packages/happy-dom/src/nodes/html-details-element/HTMLDetailsElement.ts b/packages/happy-dom/src/nodes/html-details-element/HTMLDetailsElement.ts
index c5b9cc561..815c768df 100644
--- a/packages/happy-dom/src/nodes/html-details-element/HTMLDetailsElement.ts
+++ b/packages/happy-dom/src/nodes/html-details-element/HTMLDetailsElement.ts
@@ -2,6 +2,8 @@ import Event from '../../event/Event.js';
import HTMLElement from '../html-element/HTMLElement.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import Attr from '../attr/Attr.js';
+import EventPhaseEnum from '../../event/EventPhaseEnum.js';
+import MouseEvent from '../../event/events/MouseEvent.js';
/**
* HTMLDetailsElement
@@ -56,4 +58,23 @@ export default class HTMLDetailsElement extends HTMLElement {
this.dispatchEvent(new Event('toggle'));
}
}
+
+ /**
+ * @override
+ */
+ public override dispatchEvent(event: Event): boolean {
+ const returnValue = super.dispatchEvent(event);
+
+ if (
+ !event[PropertySymbol.defaultPrevented] &&
+ event[PropertySymbol.target]?.[PropertySymbol.localName] === 'summary' &&
+ event.type === 'click' &&
+ event.eventPhase === EventPhaseEnum.bubbling &&
+ event instanceof MouseEvent
+ ) {
+ this.open = !this.open;
+ }
+
+ return returnValue;
+ }
}
diff --git a/packages/happy-dom/test/nodes/html-details-element/HTMLDetailsElement.test.ts b/packages/happy-dom/test/nodes/html-details-element/HTMLDetailsElement.test.ts
index 0d1eb5925..16f2398aa 100644
--- a/packages/happy-dom/test/nodes/html-details-element/HTMLDetailsElement.test.ts
+++ b/packages/happy-dom/test/nodes/html-details-element/HTMLDetailsElement.test.ts
@@ -3,6 +3,7 @@ import Window from '../../../src/window/Window.js';
import Document from '../../../src/nodes/document/Document.js';
import { beforeEach, describe, it, expect, vi } from 'vitest';
import Event from '../../../src/event/Event.js';
+import MouseEvent from '../../../src/event/events/MouseEvent.js';
describe('HTMLDetailsElement', () => {
let window: Window;
@@ -51,5 +52,21 @@ describe('HTMLDetailsElement', () => {
element.open = false;
expect(((triggeredEvent)).type).toBe('toggle');
});
+
+ it('Should not toggle the open state when a click event is dispatched directly on the details element', () => {
+ element.dispatchEvent(new MouseEvent('click'));
+ expect(element.open).toBe(false);
+ });
+
+ it('Should toggle the "open" attribute when a click event is dispatched on a summary element, which is a child of the details element', () => {
+ const summary = document.createElement('summary');
+ element.appendChild(summary);
+
+ summary.click();
+ expect(element.open).toBe(true);
+
+ summary.click();
+ expect(element.open).toBe(false);
+ });
});
});
diff --git a/packages/jest-environment/test/testing-library/TestingLibrary.test.tsx b/packages/jest-environment/test/testing-library/TestingLibrary.test.tsx
index 1c530ad74..17fa1bd08 100644
--- a/packages/jest-environment/test/testing-library/TestingLibrary.test.tsx
+++ b/packages/jest-environment/test/testing-library/TestingLibrary.test.tsx
@@ -78,4 +78,34 @@ describe('TestingLibrary', () => {
expect(attribute).toMatch('test');
expect(attribute).not.toMatch('hello');
});
+
+ it('Removes and adds `open` attribute for `` element on click event.', async () => {
+ const user = UserEvent.setup();
+ const handleToggle = jest.fn();
+
+ render(
+
+ summary
details
+
+ );
+
+ expect(document.body.innerHTML).toBe(
+ 'summary
details
'
+ );
+ expect(handleToggle).toHaveBeenCalledTimes(0);
+
+ await user.click(screen.getByText('summary'));
+
+ expect(document.body.innerHTML).toBe(
+ 'summary
details
'
+ );
+ expect(handleToggle).toHaveBeenCalledTimes(1);
+
+ await user.click(screen.getByText('summary'));
+
+ expect(document.body.innerHTML).toBe(
+ 'summary
details
'
+ );
+ expect(handleToggle).toHaveBeenCalledTimes(2);
+ });
});