Skip to content

Commit 6d3a2af

Browse files
iterianialxhub
authored andcommitted
fix(core): Do not bubble capture events. (#57476)
These should only fire if the target is the same as the targetElement. Also, delete an out of date test since capture/non-capture tests are separately covered. PR Close #57476
1 parent 223b785 commit 6d3a2af

File tree

3 files changed

+36
-72
lines changed

3 files changed

+36
-72
lines changed

packages/core/primitives/event-dispatch/src/event_dispatcher.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import {ActionResolver} from './action_resolver';
1010
import {Dispatcher} from './dispatcher';
1111
import {EventInfo, EventInfoWrapper} from './event_info';
12+
import {isCaptureEventType} from './event_type';
1213
import {UnrenamedEventContract} from './eventcontract';
1314
import {Restriction} from './restriction';
1415

@@ -81,6 +82,13 @@ export class EventDispatcher {
8182
prepareEventForBubbling(eventInfoWrapper);
8283
while (eventInfoWrapper.getAction()) {
8384
prepareEventForDispatch(eventInfoWrapper);
85+
// If this is a capture event, ONLY dispatch if the action element is the target.
86+
if (
87+
isCaptureEventType(eventInfoWrapper.getEventType()) &&
88+
eventInfoWrapper.getAction()!.element !== eventInfoWrapper.getTargetElement()
89+
) {
90+
return;
91+
}
8492
this.dispatchDelegate(eventInfoWrapper.getEvent(), eventInfoWrapper.getAction()!.name);
8593
if (propagationStopped(eventInfoWrapper)) {
8694
return;

packages/core/test/event_dispatch/event_dispatch_spec.ts

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -109,38 +109,6 @@ describe('event dispatch', () => {
109109
inner.click();
110110
expect(outerOnClickSpy).toHaveBeenCalledBefore(innerOnClickSpy);
111111
});
112-
it('should serialize event types to be listened to and jsaction cache entry', async () => {
113-
const clickSpy = jasmine.createSpy('onClick');
114-
const focusSpy = jasmine.createSpy('onFocus');
115-
@Component({
116-
standalone: true,
117-
selector: 'app',
118-
template: `
119-
<div (click)="onClick()" id="click-element">
120-
<div id="focus-container">
121-
<div id="focus-action-element" (focus)="onFocus()">
122-
<button id="focus-target-element">Focus Button</button>
123-
</div>
124-
</div>
125-
</div>
126-
`,
127-
})
128-
class SimpleComponent {
129-
onClick = clickSpy;
130-
onFocus = focusSpy;
131-
}
132-
configureTestingModule([SimpleComponent]);
133-
fixture = TestBed.createComponent(SimpleComponent);
134-
const nativeElement = fixture.debugElement.nativeElement;
135-
const el = nativeElement.querySelector('#click-element')!;
136-
const button = nativeElement.querySelector('#focus-target-element')!;
137-
const clickEvent = new CustomEvent('click', {bubbles: true});
138-
el.dispatchEvent(clickEvent);
139-
const focusEvent = new CustomEvent('focus');
140-
button.dispatchEvent(focusEvent);
141-
expect(clickSpy).toHaveBeenCalled();
142-
expect(focusSpy).toHaveBeenCalled();
143-
});
144112

145113
describe('bubbling behavior', () => {
146114
it('should propagate events', async () => {
@@ -305,3 +273,31 @@ describe('event dispatch', () => {
305273
});
306274
});
307275
});
276+
277+
describe('capture behavior', () => {
278+
let fixture: ComponentFixture<unknown>;
279+
it('should not bubble', async () => {
280+
const onFocusSpy = jasmine.createSpy();
281+
@Component({
282+
standalone: true,
283+
selector: 'app',
284+
template: `
285+
<div id="top" (focus)="onFocus()">
286+
<div id="bottom"></div>
287+
</div>
288+
`,
289+
})
290+
class SimpleComponent {
291+
onFocus = onFocusSpy;
292+
}
293+
configureTestingModule([SimpleComponent]);
294+
fixture = TestBed.createComponent(SimpleComponent);
295+
const nativeElement = fixture.debugElement.nativeElement;
296+
const bottomEl = nativeElement.querySelector('#bottom')!;
297+
const topEl = nativeElement.querySelector('#top')!;
298+
bottomEl.dispatchEvent(new FocusEvent('focus'));
299+
expect(onFocusSpy).toHaveBeenCalledTimes(0);
300+
topEl.dispatchEvent(new FocusEvent('focus'));
301+
expect(onFocusSpy).toHaveBeenCalledTimes(1);
302+
});
303+
});

packages/platform-server/test/event_replay_spec.ts

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -208,46 +208,6 @@ describe('event replay', () => {
208208
expect(outerOnClickSpy).toHaveBeenCalledBefore(innerOnClickSpy);
209209
});
210210

211-
it('should serialize event types to be listened to and jsaction attribute', async () => {
212-
const clickSpy = jasmine.createSpy('onClick');
213-
const focusSpy = jasmine.createSpy('onFocus');
214-
@Component({
215-
standalone: true,
216-
selector: 'app',
217-
template: `
218-
<div (click)="onClick()" id="click-element">
219-
<div id="focus-container">
220-
<div id="focus-action-element" (focus)="onFocus()">
221-
<button id="focus-target-element">Focus Button</button>
222-
</div>
223-
</div>
224-
</div>
225-
`,
226-
})
227-
class SimpleComponent {
228-
onClick = clickSpy;
229-
onFocus = focusSpy;
230-
}
231-
const html = await ssr(SimpleComponent);
232-
const ssrContents = getAppContents(html);
233-
234-
render(doc, ssrContents);
235-
const el = doc.getElementById('click-element')!;
236-
const button = doc.getElementById('focus-target-element')!;
237-
const clickEvent = new CustomEvent('click', {bubbles: true});
238-
el.dispatchEvent(clickEvent);
239-
const focusEvent = new CustomEvent('focus');
240-
button.dispatchEvent(focusEvent);
241-
expect(clickSpy).not.toHaveBeenCalled();
242-
expect(focusSpy).not.toHaveBeenCalled();
243-
resetTViewsFor(SimpleComponent);
244-
await hydrate(doc, SimpleComponent, {
245-
hydrationFeatures: [withEventReplay()],
246-
});
247-
expect(clickSpy).toHaveBeenCalled();
248-
expect(focusSpy).toHaveBeenCalled();
249-
});
250-
251211
it('should remove jsaction attributes, but continue listening to events.', async () => {
252212
@Component({
253213
standalone: true,

0 commit comments

Comments
 (0)