Skip to content

Commit 8f74989

Browse files
fix: [#1534] Toggle open attribute on HTMLDetailsElement when dispatching a click event on a summary element which is a child of the details element (#1534)
* fix: [#1534] Fix click event not removing open attribute * chore: [#1534] Adds unit tests and adds some additional logic according to the spec --------- Co-authored-by: David Ortner <[email protected]>
1 parent 7f57469 commit 8f74989

File tree

3 files changed

+68
-0
lines changed

3 files changed

+68
-0
lines changed

packages/happy-dom/src/nodes/html-details-element/HTMLDetailsElement.ts

+21
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import Event from '../../event/Event.js';
22
import HTMLElement from '../html-element/HTMLElement.js';
33
import * as PropertySymbol from '../../PropertySymbol.js';
44
import Attr from '../attr/Attr.js';
5+
import EventPhaseEnum from '../../event/EventPhaseEnum.js';
6+
import MouseEvent from '../../event/events/MouseEvent.js';
57

68
/**
79
* HTMLDetailsElement
@@ -56,4 +58,23 @@ export default class HTMLDetailsElement extends HTMLElement {
5658
this.dispatchEvent(new Event('toggle'));
5759
}
5860
}
61+
62+
/**
63+
* @override
64+
*/
65+
public override dispatchEvent(event: Event): boolean {
66+
const returnValue = super.dispatchEvent(event);
67+
68+
if (
69+
!event[PropertySymbol.defaultPrevented] &&
70+
event[PropertySymbol.target]?.[PropertySymbol.localName] === 'summary' &&
71+
event.type === 'click' &&
72+
event.eventPhase === EventPhaseEnum.bubbling &&
73+
event instanceof MouseEvent
74+
) {
75+
this.open = !this.open;
76+
}
77+
78+
return returnValue;
79+
}
5980
}

packages/happy-dom/test/nodes/html-details-element/HTMLDetailsElement.test.ts

+17
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Window from '../../../src/window/Window.js';
33
import Document from '../../../src/nodes/document/Document.js';
44
import { beforeEach, describe, it, expect, vi } from 'vitest';
55
import Event from '../../../src/event/Event.js';
6+
import MouseEvent from '../../../src/event/events/MouseEvent.js';
67

78
describe('HTMLDetailsElement', () => {
89
let window: Window;
@@ -51,5 +52,21 @@ describe('HTMLDetailsElement', () => {
5152
element.open = false;
5253
expect((<Event>(<unknown>triggeredEvent)).type).toBe('toggle');
5354
});
55+
56+
it('Should not toggle the open state when a click event is dispatched directly on the details element', () => {
57+
element.dispatchEvent(new MouseEvent('click'));
58+
expect(element.open).toBe(false);
59+
});
60+
61+
it('Should toggle the "open" attribute when a click event is dispatched on a summary element, which is a child of the details element', () => {
62+
const summary = document.createElement('summary');
63+
element.appendChild(summary);
64+
65+
summary.click();
66+
expect(element.open).toBe(true);
67+
68+
summary.click();
69+
expect(element.open).toBe(false);
70+
});
5471
});
5572
});

packages/jest-environment/test/testing-library/TestingLibrary.test.tsx

+30
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,34 @@ describe('TestingLibrary', () => {
7878
expect(attribute).toMatch('test');
7979
expect(attribute).not.toMatch('hello');
8080
});
81+
82+
it('Removes and adds `open` attribute for `<details>` element on click event.', async () => {
83+
const user = UserEvent.setup();
84+
const handleToggle = jest.fn();
85+
86+
render(
87+
<details open={true} onToggle={handleToggle}>
88+
<summary>summary</summary>details
89+
</details>
90+
);
91+
92+
expect(document.body.innerHTML).toBe(
93+
'<div><details open=""><summary>summary</summary>details</details></div>'
94+
);
95+
expect(handleToggle).toHaveBeenCalledTimes(0);
96+
97+
await user.click(screen.getByText('summary'));
98+
99+
expect(document.body.innerHTML).toBe(
100+
'<div><details><summary>summary</summary>details</details></div>'
101+
);
102+
expect(handleToggle).toHaveBeenCalledTimes(1);
103+
104+
await user.click(screen.getByText('summary'));
105+
106+
expect(document.body.innerHTML).toBe(
107+
'<div><details open=""><summary>summary</summary>details</details></div>'
108+
);
109+
expect(handleToggle).toHaveBeenCalledTimes(2);
110+
});
81111
});

0 commit comments

Comments
 (0)