mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(toast): add wrapper and content parts (#30992)
Issue number: resolves #30735 --------- ## What is the current behavior? Toast's wrapper and content divs do not have part attributes. ## What is the new behavior? - Adds `wrapper` and `content` parts to Toast. ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information This PR was originally opened by @Hurubon here in #30737. It was lost in `feature-8.8` due to a bad merge, so this PR is to re-apply this feature so it can go out in 8.8.1 (it was originally supposed to go out in 8.8.0) Co-authored-by: Hurubon <58346722+Hurubon@users.noreply.github.com> Co-authored-by: Hurubon <58346722+Hurubon@users.noreply.github.com>
This commit is contained in:
@@ -2059,9 +2059,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
|
||||
|
||||
133
core/src/components/toast/test/custom/toast.e2e.ts
Normal file
133
core/src/components/toast/test/custom/toast.e2e.ts
Normal file
@@ -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(
|
||||
`
|
||||
<ion-toast is-open="true" header="Header" message="Hello World"></ion-toast>
|
||||
|
||||
<style>
|
||||
ion-toast::part(wrapper) {
|
||||
background-color: red;
|
||||
}
|
||||
ion-toast::part(container) {
|
||||
background-color: green;
|
||||
}
|
||||
ion-toast::part(content) {
|
||||
background-color: blue;
|
||||
}
|
||||
</style>
|
||||
`,
|
||||
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(
|
||||
`
|
||||
<ion-toast is-open="true" header="Header" message="Hello World"></ion-toast>
|
||||
|
||||
<style>
|
||||
ion-toast::part(header) {
|
||||
color: red;
|
||||
}
|
||||
ion-toast::part(message) {
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
`,
|
||||
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(
|
||||
`
|
||||
<ion-toast is-open="true" header="Header" message="Hello World" icon="alert"></ion-toast>
|
||||
|
||||
<style>
|
||||
ion-toast::part(icon) {
|
||||
color: red;
|
||||
}
|
||||
ion-toast::part(button) {
|
||||
color: green;
|
||||
}
|
||||
ion-toast::part(button cancel) {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const toast = document.querySelector('ion-toast');
|
||||
toast.buttons = [
|
||||
{ text: 'Cancel', role: 'cancel' },
|
||||
{ text: 'OK' }
|
||||
];
|
||||
</script>
|
||||
`,
|
||||
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)');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -47,12 +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 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.
|
||||
* @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',
|
||||
@@ -727,7 +729,7 @@ export class Toast implements ComponentInterface, OverlayInterface {
|
||||
})}
|
||||
onIonToastWillDismiss={this.dispatchCancelHandler}
|
||||
>
|
||||
<div class={wrapperClass}>
|
||||
<div class={wrapperClass} part="wrapper">
|
||||
<div class="toast-container" part="container">
|
||||
{this.renderButtons(startButtons, 'start')}
|
||||
|
||||
@@ -746,7 +748,7 @@ export class Toast implements ComponentInterface, OverlayInterface {
|
||||
not interrupt the user which is why this has
|
||||
a "status" role and a "polite" presentation.
|
||||
*/}
|
||||
<div class="toast-content" role="status" aria-atomic="true" aria-live="polite">
|
||||
<div class="toast-content" part="content" role="status" aria-atomic="true" aria-live="polite">
|
||||
{/*
|
||||
This logic below is done to improve consistency
|
||||
across platforms when showing and updating live regions.
|
||||
|
||||
Reference in New Issue
Block a user