Skip to content

Commit a889042

Browse files
committed
fix(multiple): prevent form submissions in aria directives (angular#33297)
Updates a few of the Aria directives that are likely to be placed on buttons to have `type="button"` by default. This prevents them from accidentally submitting forms. (cherry picked from commit 11e2eb3)
1 parent 3fa1f16 commit a889042

5 files changed

Lines changed: 25 additions & 0 deletions

File tree

src/aria/accordion/accordion-trigger.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ export class AccordionTrigger implements OnInit, OnDestroy {
8686
_pattern!: AccordionTriggerPattern;
8787

8888
constructor() {
89+
// Automatically prevent form submission.
90+
if (this.element.tagName === 'BUTTON' && !this.element.hasAttribute('type')) {
91+
this.element.setAttribute('type', 'button');
92+
}
93+
8994
// Check for any violations after the DOM has been updated.
9095
if (typeof ngDevMode === 'undefined' || ngDevMode) {
9196
afterRenderEffect({

src/aria/accordion/accordion.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ describe('AccordionGroup', () => {
8787
expect(getTriggerAttribute(2, 'role')).toBe('button');
8888
});
8989

90+
it('should set type="button" by default on buttons', () => {
91+
expect(getTriggerAttribute(0, 'type')).toBe('button');
92+
expect(getTriggerAttribute(1, 'type')).toBe('button');
93+
expect(getTriggerAttribute(2, 'type')).toBe('button');
94+
});
95+
9096
it('should have aria-expanded="false" when collapsed', () => {
9197
expect(getTriggerAttribute(0, 'aria-expanded')).toBe('false');
9298
expect(getTriggerAttribute(1, 'aria-expanded')).toBe('false');

src/aria/menu/menu-trigger.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ export class MenuTrigger<V> {
9090
constructor() {
9191
effect(() => this.menu()?.parent.set(this));
9292
effect(() => this._pattern.pendingFocusEffect());
93+
94+
// Automatically prevent form submission.
95+
if (this.element.tagName === 'BUTTON' && !this.element.hasAttribute('type')) {
96+
this.element.setAttribute('type', 'button');
97+
}
9398
}
9499

95100
/** Opens the menu focusing on the first menu item. */

src/aria/menu/menu.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,10 @@ describe('CDK Overlay Menu Pattern', () => {
861861
await keydown(trigger, 'Enter');
862862
expect(document.activeElement).toBe(getItem('Apple'));
863863
});
864+
865+
it('should set type="button" by default on button triggers', () => {
866+
expect(getTrigger().getAttribute('type')).toBe('button');
867+
});
864868
});
865869

866870
describe('Menu Bar Pattern', () => {

src/aria/tabs/tab.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ export class Tab implements HasElement, OnInit, OnDestroy {
9292
}
9393

9494
constructor() {
95+
// Automatically prevent form submission.
96+
if (this.element.tagName === 'BUTTON' && !this.element.hasAttribute('type')) {
97+
this.element.setAttribute('type', 'button');
98+
}
99+
95100
if (typeof ngDevMode === 'undefined' || ngDevMode) {
96101
afterRenderEffect({
97102
read: () => {

0 commit comments

Comments
 (0)