From 1d4c704dcbb80e9bb80ea3003486097078c05926 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Wed, 17 Jun 2026 09:14:51 -0500 Subject: [PATCH 1/5] fix(ion-button): sync disabled state in ion-button renderHiddenButton Closes #30968 --- core/src/components/button/button.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index 0e4741f4acd..b4aa82ec138 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -198,6 +198,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf * then do not append a new one again. */ if (formButtonEl !== null && formEl.contains(formButtonEl)) { + formButtonEl.disabled = this.disabled; return; } From 2964bc6a15552d19577bf0d92ff6f8558cd02272 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Wed, 17 Jun 2026 09:18:24 -0500 Subject: [PATCH 2/5] test(ion-button): add e2e test for async disabled state change (issue #30968) --- .../button/test/form-reference/button.e2e.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/core/src/components/button/test/form-reference/button.e2e.ts b/core/src/components/button/test/form-reference/button.e2e.ts index 5710f52da67..c8a0fe2039b 100644 --- a/core/src/components/button/test/form-reference/button.e2e.ts +++ b/core/src/components/button/test/form-reference/button.e2e.ts @@ -153,6 +153,42 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(submitEvent).toHaveReceivedEvent(); }); + + test('should submit the form when disabled state changes asynchronously', async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/30968', + }); + + await page.setContent( + ` +
+ + Submit +
+ `, + config + ); + + const submitEvent = await page.spyOnEvent('submit'); + const button = page.locator('ion-button'); + + // Simulate async disabled state change — e.g. async validator resolving + await button.evaluate((el: HTMLIonButtonElement) => { + el.disabled = true; + setTimeout(() => { + el.disabled = false; + }, 0); + }); + + // Wait for the async change to settle + await page.waitForTimeout(100); + + await page.click('ion-button'); + + expect(submitEvent).toHaveReceivedEvent(); + }); + }); test.describe(title('should throw a warning if the form cannot be found'), () => { From 31aff3187b2951ab19514f35a454e4af876d641f Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Wed, 17 Jun 2026 12:52:21 -0500 Subject: [PATCH 3/5] npm run lint.fix --- core/src/components/button/test/form-reference/button.e2e.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/components/button/test/form-reference/button.e2e.ts b/core/src/components/button/test/form-reference/button.e2e.ts index c8a0fe2039b..c803dd2c226 100644 --- a/core/src/components/button/test/form-reference/button.e2e.ts +++ b/core/src/components/button/test/form-reference/button.e2e.ts @@ -188,7 +188,6 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(submitEvent).toHaveReceivedEvent(); }); - }); test.describe(title('should throw a warning if the form cannot be found'), () => { From c235b7954e2775ae36629152cc84154c9ad30f56 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Thu, 18 Jun 2026 14:27:51 -0500 Subject: [PATCH 4/5] revert: fix(ion-button): sync disabled state in ion-button renderHiddenButton This reverts commit 1d4c704dcbb80e9bb80ea3003486097078c05926. Will retest changes without the fix and new test location --- core/src/components/button/button.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index b4aa82ec138..f8258af2239 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -195,10 +195,13 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf /** * If the form already has a rendered form button - * then do not append a new one again. + * then do not append a new one again. Sync the + * disabled state and type in it changes after button + * creation (e.g., runtime property updates). */ if (formButtonEl !== null && formEl.contains(formButtonEl)) { - formButtonEl.disabled = this.disabled; + // formButtonEl.disabled = this.disabled; + // formButtonEl.type = this.type; return; } From 5fb20dd4f9474ef979dd2fb4ebfa59bbcd52ed04 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Thu, 18 Jun 2026 14:43:25 -0500 Subject: [PATCH 5/5] revert: test(ion-button): add e2e test for async disabled state change (issue #30968) This reverts commit 2964bc6a15552d19577bf0d92ff6f8558cd02272.