From 2ee6d8cda7b077a4fe6f6c50baadb970aac801d0 Mon Sep 17 00:00:00 2001 From: Hurubon Date: Fri, 17 Oct 2025 22:38:09 +0200 Subject: [PATCH 1/4] feat(toast): Add attribute part='content' --- core/src/components/toast/toast.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index 4fdcc90f42a..629c5a1d262 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -746,7 +746,7 @@ export class Toast implements ComponentInterface, OverlayInterface { not interrupt the user which is why this has a "status" role and a "polite" presentation. */} -
+
{/* This logic below is done to improve consistency across platforms when showing and updating live regions. From 8bdb27a8b9264677b11d3fcec20f1a13ab3c6a6d Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Thu, 30 Oct 2025 11:16:12 -0400 Subject: [PATCH 2/4] feat(toast): add wrapper and content parts --- core/api.txt | 2 ++ core/src/components/toast/toast.tsx | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/api.txt b/core/api.txt index 745d82786af..7cc6d2f0389 100644 --- a/core/api.txt +++ b/core/api.txt @@ -2016,9 +2016,11 @@ ion-toast,css-prop,--width,md ion-toast,part,button ion-toast,part,button cancel ion-toast,part,container +ion-toast,part,content ion-toast,part,header ion-toast,part,icon ion-toast,part,message +ion-toast,part,wrapper ion-toggle,shadow ion-toggle,prop,alignment,"center" | "start" | undefined,undefined,false,false diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index 629c5a1d262..62cb5010b96 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -49,7 +49,9 @@ import type { * * @part button - Any button element that is displayed inside of the toast. * @part button cancel - Any button element with role "cancel" that is displayed inside of the toast. - * @part container - The element that wraps all child elements. + * @part wrapper - The outer wrapper for the toast overlay. + * @part container - Groups the icon, content, and buttons. + * @part content - The live region that contains the header and message. * @part header - The header text of the toast. * @part message - The body text of the toast. * @part icon - The icon that appears next to the toast content. @@ -727,7 +729,7 @@ export class Toast implements ComponentInterface, OverlayInterface { })} onIonToastWillDismiss={this.dispatchCancelHandler} > -
+
{this.renderButtons(startButtons, 'start')} From 239232c9726e41ddebda461fd715d98f83647a14 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:55:08 -0400 Subject: [PATCH 3/4] test(toast): add e2e test for customizing via parts --- .../components/toast/test/custom/toast.e2e.ts | 133 ++++++++++++++++++ core/src/components/toast/toast.tsx | 4 +- 2 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 core/src/components/toast/test/custom/toast.e2e.ts diff --git a/core/src/components/toast/test/custom/toast.e2e.ts b/core/src/components/toast/test/custom/toast.e2e.ts new file mode 100644 index 00000000000..a607e3e238b --- /dev/null +++ b/core/src/components/toast/test/custom/toast.e2e.ts @@ -0,0 +1,133 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +/** + * This behavior does not vary across directions + */ +configs({ directions: ['ltr'] }).forEach(({ config, title }) => { + test.describe(title('toast: custom'), () => { + test('should be able to customize toast wrapper, container, and content using css parts', async ({ page }) => { + await page.setContent( + ` + + + + `, + config + ); + + const wrapperColor = await page.locator('ion-toast').evaluate((el: any) => { + const partEl = el.shadowRoot?.querySelector('[part="wrapper"]') as HTMLElement | null; + return partEl ? getComputedStyle(partEl).backgroundColor : null; + }); + + expect(wrapperColor).toBe('rgb(255, 0, 0)'); + + const containerColor = await page.locator('ion-toast').evaluate((el: any) => { + const partEl = el.shadowRoot?.querySelector('[part="container"]') as HTMLElement | null; + return partEl ? getComputedStyle(partEl).backgroundColor : null; + }); + + expect(containerColor).toBe('rgb(0, 128, 0)'); + + const contentColor = await page.locator('ion-toast').evaluate((el: any) => { + const partEl = el.shadowRoot?.querySelector('[part="content"]') as HTMLElement | null; + return partEl ? getComputedStyle(partEl).backgroundColor : null; + }); + + expect(contentColor).toBe('rgb(0, 0, 255)'); + }); + + test('should be able to customize toast header and message using css parts', async ({ page }) => { + await page.setContent( + ` + + + + `, + config + ); + + const headerColor = await page.locator('ion-toast').evaluate((el: any) => { + const partEl = el.shadowRoot?.querySelector('[part="header"]') as HTMLElement | null; + return partEl ? getComputedStyle(partEl).color : null; + }); + + expect(headerColor).toBe('rgb(255, 0, 0)'); + + const messageColor = await page.locator('ion-toast').evaluate((el: any) => { + const partEl = el.shadowRoot?.querySelector('[part="message"]') as HTMLElement | null; + return partEl ? getComputedStyle(partEl).color : null; + }); + + expect(messageColor).toBe('rgb(0, 128, 0)'); + }); + + test('should be able to customize toast icon, button, and button cancel using css parts', async ({ page }) => { + await page.setContent( + ` + + + + + + `, + config + ); + + const iconColor = await page.locator('ion-toast').evaluate((el: any) => { + const partEl = el.shadowRoot?.querySelector('[part="icon"]') as HTMLElement | null; + return partEl ? getComputedStyle(partEl).color : null; + }); + + expect(iconColor).toBe('rgb(255, 0, 0)'); + + const buttonColor = await page.locator('ion-toast').evaluate((el: any) => { + const partEl = el.shadowRoot?.querySelector('[part="button"]') as HTMLElement | null; + return partEl ? getComputedStyle(partEl).color : null; + }); + + expect(buttonColor).toBe('rgb(0, 128, 0)'); + + const buttonCancelColor = await page.locator('ion-toast').evaluate((el: any) => { + const partEl = el.shadowRoot?.querySelector('[part="button cancel"]') as HTMLElement | null; + return partEl ? getComputedStyle(partEl).color : null; + }); + + expect(buttonCancelColor).toBe('rgb(0, 0, 255)'); + }); + }); +}); diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index 62cb5010b96..6779fdb2702 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -47,14 +47,14 @@ import type { /** * @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use. * - * @part button - Any button element that is displayed inside of the toast. - * @part button cancel - Any button element with role "cancel" that is displayed inside of the toast. * @part wrapper - The outer wrapper for the toast overlay. * @part container - Groups the icon, content, and buttons. * @part content - The live region that contains the header and message. * @part header - The header text of the toast. * @part message - The body text of the toast. * @part icon - The icon that appears next to the toast content. + * @part button - Any button element that is displayed inside of the toast. + * @part button cancel - Any button element with role "cancel" that is displayed inside of the toast.* */ @Component({ tag: 'ion-toast', From 0a55ebb32d3242fcbe3a220fdb0114ad0b17348c Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Thu, 30 Oct 2025 16:00:56 -0400 Subject: [PATCH 4/4] bad copy paste --- core/src/components/toast/toast.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index 6779fdb2702..6f0929662af 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -54,7 +54,7 @@ import type { * @part message - The body text of the toast. * @part icon - The icon that appears next to the toast content. * @part button - Any button element that is displayed inside of the toast. - * @part button cancel - Any button element with role "cancel" that is displayed inside of the toast.* + * @part button cancel - Any button element with role "cancel" that is displayed inside of the toast. */ @Component({ tag: 'ion-toast',