diff --git a/angular/src/index.ts b/angular/src/index.ts
index a28a12dfef..38d2130b7b 100644
--- a/angular/src/index.ts
+++ b/angular/src/index.ts
@@ -130,6 +130,7 @@ export {
TextareaCustomEvent,
ToastOptions,
ToastButton,
+ ToastLayout,
ToggleChangeEventDetail,
ToggleCustomEvent,
} from '@ionic/core';
diff --git a/core/api.txt b/core/api.txt
index c22df0bde2..8eb31c0604 100644
--- a/core/api.txt
+++ b/core/api.txt
@@ -1386,6 +1386,7 @@ ion-toast,prop,header,string | undefined,undefined,false,false
ion-toast,prop,htmlAttributes,undefined | { [key: string]: any; },undefined,false,false
ion-toast,prop,icon,string | undefined,undefined,false,false
ion-toast,prop,keyboardClose,boolean,false,false,false
+ion-toast,prop,layout,"baseline" | "stacked",'baseline',false,false
ion-toast,prop,leaveAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-toast,prop,message,IonicSafeString | string | undefined,undefined,false,false
ion-toast,prop,mode,"ios" | "md",undefined,false,false
diff --git a/core/src/components.d.ts b/core/src/components.d.ts
index f5f6867991..1164b07ad0 100644
--- a/core/src/components.d.ts
+++ b/core/src/components.d.ts
@@ -14,7 +14,7 @@ import { PickerInternalChangeEventDetail } from "./components/picker-internal/pi
import { PinFormatter } from "./components/range/range-interface";
import { NavigationHookCallback } from "./components/route/route-interface";
import { SelectCompareFn } from "./components/select/select-interface";
-import { ToastAttributes, ToastPosition } from "./components/toast/toast-interface";
+import { ToastAttributes, ToastLayout, ToastPosition } from "./components/toast/toast-interface";
export namespace Components {
interface IonAccordion {
/**
@@ -2976,6 +2976,10 @@ export namespace Components {
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
*/
"keyboardClose": boolean;
+ /**
+ * Defines how the message and buttons are laid out in the toast. 'baseline': The message and the buttons will appear on the same line. Message text may wrap within the message container. 'stacked': The buttons containers and message will stack on top of each other. Use this if you have long text in your buttons.
+ */
+ "layout": ToastLayout;
/**
* Animation to use when the toast is dismissed.
*/
@@ -6977,6 +6981,10 @@ declare namespace LocalJSX {
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
*/
"keyboardClose"?: boolean;
+ /**
+ * Defines how the message and buttons are laid out in the toast. 'baseline': The message and the buttons will appear on the same line. Message text may wrap within the message container. 'stacked': The buttons containers and message will stack on top of each other. Use this if you have long text in your buttons.
+ */
+ "layout"?: ToastLayout;
/**
* Animation to use when the toast is dismissed.
*/
diff --git a/core/src/components/toast/test/layout/index.html b/core/src/components/toast/test/layout/index.html
new file mode 100644
index 0000000000..3562accab7
--- /dev/null
+++ b/core/src/components/toast/test/layout/index.html
@@ -0,0 +1,57 @@
+
+
+
+
+ Toast - Layout
+
+
+
+
+
+
+
+
+
+
+
+
+ Toast - Layout
+
+
+
+
+ Open Baseline Layout Toast
+ Open Stacked Layout Toast
+
+
+
+
+
+
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts b/core/src/components/toast/test/layout/toast.e2e.ts
new file mode 100644
index 0000000000..f5c22cd1b1
--- /dev/null
+++ b/core/src/components/toast/test/layout/toast.e2e.ts
@@ -0,0 +1,15 @@
+import { expect } from '@playwright/test';
+import { test } from '@utils/test/playwright';
+
+test.describe('toast: stacked layout', () => {
+ test('should render stacked buttons', async ({ page }) => {
+ await page.goto('/src/components/toast/test/layout');
+ const ionToastDidPresent = await page.spyOnEvent('ionToastDidPresent');
+
+ await page.click('#stacked');
+ await ionToastDidPresent.next();
+
+ const toastWrapper = page.locator('ion-toast .toast-wrapper');
+ expect(await toastWrapper.screenshot()).toMatchSnapshot(`toast-stacked-${page.getSnapshotSettings()}.png`);
+ });
+});
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-ltr-Mobile-Chrome-linux.png
new file mode 100644
index 0000000000..1a906abf68
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-ltr-Mobile-Chrome-linux.png differ
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-ltr-Mobile-Firefox-linux.png
new file mode 100644
index 0000000000..d7f8266d09
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-ltr-Mobile-Firefox-linux.png differ
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-ltr-Mobile-Safari-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-ltr-Mobile-Safari-linux.png
new file mode 100644
index 0000000000..1d24d41c61
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-ltr-Mobile-Safari-linux.png differ
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-rtl-Mobile-Chrome-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-rtl-Mobile-Chrome-linux.png
new file mode 100644
index 0000000000..1b0edc0efa
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-rtl-Mobile-Chrome-linux.png differ
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-rtl-Mobile-Firefox-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-rtl-Mobile-Firefox-linux.png
new file mode 100644
index 0000000000..5108314254
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-rtl-Mobile-Firefox-linux.png differ
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-rtl-Mobile-Safari-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-rtl-Mobile-Safari-linux.png
new file mode 100644
index 0000000000..fdeff15643
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-ios-rtl-Mobile-Safari-linux.png differ
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-ltr-Mobile-Chrome-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-ltr-Mobile-Chrome-linux.png
new file mode 100644
index 0000000000..1ba4b083e3
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-ltr-Mobile-Chrome-linux.png differ
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-ltr-Mobile-Firefox-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-ltr-Mobile-Firefox-linux.png
new file mode 100644
index 0000000000..ecab85e31f
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-ltr-Mobile-Firefox-linux.png differ
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-ltr-Mobile-Safari-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-ltr-Mobile-Safari-linux.png
new file mode 100644
index 0000000000..d5827d5ca9
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-ltr-Mobile-Safari-linux.png differ
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-rtl-Mobile-Chrome-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-rtl-Mobile-Chrome-linux.png
new file mode 100644
index 0000000000..5a20ad3558
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-rtl-Mobile-Chrome-linux.png differ
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-rtl-Mobile-Firefox-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-rtl-Mobile-Firefox-linux.png
new file mode 100644
index 0000000000..379adbf2bd
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-rtl-Mobile-Firefox-linux.png differ
diff --git a/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-rtl-Mobile-Safari-linux.png b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-rtl-Mobile-Safari-linux.png
new file mode 100644
index 0000000000..e592c426bd
Binary files /dev/null and b/core/src/components/toast/test/layout/toast.e2e.ts-snapshots/toast-stacked-md-rtl-Mobile-Safari-linux.png differ
diff --git a/core/src/components/toast/toast-interface.ts b/core/src/components/toast/toast-interface.ts
index 9b417e3741..364ee9f2cd 100644
--- a/core/src/components/toast/toast-interface.ts
+++ b/core/src/components/toast/toast-interface.ts
@@ -12,6 +12,7 @@ export interface ToastOptions {
animated?: boolean;
icon?: string;
htmlAttributes?: ToastAttributes;
+ layout?: ToastLayout;
color?: Color;
mode?: Mode;
@@ -27,6 +28,8 @@ export interface ToastOptions {
*/
export type ToastAttributes = { [key: string]: any };
+export type ToastLayout = 'baseline' | 'stacked';
+
export interface ToastButton {
text?: string;
icon?: string;
diff --git a/core/src/components/toast/toast.md.scss b/core/src/components/toast/toast.md.scss
index 7581c4b8af..968afe913a 100644
--- a/core/src/components/toast/toast.md.scss
+++ b/core/src/components/toast/toast.md.scss
@@ -49,14 +49,22 @@
// --------------------------------------------------
-.toast-button-group-start {
+.toast-layout-baseline .toast-button-group-start {
@include margin(null, null, null, 8px);
}
-.toast-button-group-end {
+.toast-layout-stacked .toast-button-group-start {
+ @include margin(8px, 8px, null, null);
+}
+
+.toast-layout-baseline .toast-button-group-end {
@include margin(null, 8px, null, null);
}
+.toast-layout-stacked .toast-button-group-end {
+ @include margin(null, 8px, 8px, null);
+}
+
.toast-button {
@include padding($toast-md-button-padding-top, $toast-md-button-padding-end, $toast-md-button-padding-bottom, $toast-md-button-padding-start);
diff --git a/core/src/components/toast/toast.scss b/core/src/components/toast/toast.scss
index 0a44e69808..0418e86c7c 100644
--- a/core/src/components/toast/toast.scss
+++ b/core/src/components/toast/toast.scss
@@ -112,7 +112,11 @@
contain: content;
}
-.toast-content {
+.toast-layout-stacked .toast-container {
+ flex-wrap: wrap;
+}
+
+.toast-layout-baseline .toast-content {
display: flex;
flex: 1;
@@ -134,6 +138,12 @@
display: flex;
}
+.toast-layout-stacked .toast-button-group {
+ justify-content: end;
+
+ width: 100%;
+}
+
.toast-button {
border: 0;
diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx
index cc941008e7..50812b7a06 100644
--- a/core/src/components/toast/toast.tsx
+++ b/core/src/components/toast/toast.tsx
@@ -11,6 +11,7 @@ import type {
OverlayInterface,
ToastButton,
} from '../../interface';
+import { printIonWarning } from '../../utils/logging';
import { dismiss, eventMethod, isCancel, prepareOverlay, present, safeCall } from '../../utils/overlays';
import type { IonicSafeString } from '../../utils/sanitization';
import { sanitizeDOMString } from '../../utils/sanitization';
@@ -20,7 +21,7 @@ import { iosEnterAnimation } from './animations/ios.enter';
import { iosLeaveAnimation } from './animations/ios.leave';
import { mdEnterAnimation } from './animations/md.enter';
import { mdLeaveAnimation } from './animations/md.leave';
-import type { ToastAttributes, ToastPosition } from './toast-interface';
+import type { ToastAttributes, ToastPosition, ToastLayout } from './toast-interface';
// TODO(FW-2832): types
@@ -87,6 +88,15 @@ export class Toast implements ComponentInterface, OverlayInterface {
*/
@Prop() header?: string;
+ /**
+ * Defines how the message and buttons are laid out in the toast.
+ * 'baseline': The message and the buttons will appear on the same line.
+ * Message text may wrap within the message container.
+ * 'stacked': The buttons containers and message will stack on top
+ * of each other. Use this if you have long text in your buttons.
+ */
+ @Prop() layout: ToastLayout = 'baseline';
+
/**
* Message to be shown in the toast.
*/
@@ -290,6 +300,7 @@ export class Toast implements ComponentInterface, OverlayInterface {
}
render() {
+ const { layout, el } = this;
const allButtons = this.getButtons();
const startButtons = allButtons.filter((b) => b.side === 'start');
const endButtons = allButtons.filter((b) => b.side !== 'start');
@@ -297,9 +308,21 @@ export class Toast implements ComponentInterface, OverlayInterface {
const wrapperClass = {
'toast-wrapper': true,
[`toast-${this.position}`]: true,
+ [`toast-layout-${layout}`]: true,
};
const role = allButtons.length > 0 ? 'dialog' : 'status';
+ /**
+ * Stacked buttons are only meant to be
+ * used with one type of button.
+ */
+ if (layout === 'stacked' && startButtons.length > 0 && endButtons.length > 0) {
+ printIonWarning(
+ 'This toast is using start and end buttons with the stacked toast layout. We recommend following the best practice of using either start or end buttons with the stacked toast layout.',
+ el
+ );
+ }
+
return (
('
export const IonPicker = /*@__PURE__*/ defineOverlayContainer('ion-picker', defineIonPickerCustomElement, ['animated', 'backdropDismiss', 'buttons', 'columns', 'cssClass', 'duration', 'enterAnimation', 'htmlAttributes', 'keyboardClose', 'leaveAnimation', 'mode', 'showBackdrop'], pickerController);
-export const IonToast = /*@__PURE__*/ defineOverlayContainer('ion-toast', defineIonToastCustomElement, ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'keyboardClose', 'leaveAnimation', 'message', 'mode', 'position', 'translucent'], toastController);
+export const IonToast = /*@__PURE__*/ defineOverlayContainer('ion-toast', defineIonToastCustomElement, ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'translucent'], toastController);
export const IonModal = /*@__PURE__*/ defineOverlayContainer('ion-modal', defineIonModalCustomElement, ['animated', 'backdropBreakpoint', 'backdropDismiss', 'breakpoints', 'canDismiss', 'enterAnimation', 'handle', 'handleBehavior', 'htmlAttributes', 'initialBreakpoint', 'isOpen', 'keepContentsMounted', 'keyboardClose', 'leaveAnimation', 'mode', 'presentingElement', 'showBackdrop', 'swipeToClose', 'trigger']);
diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts
index 4a7bab011f..b3af281c14 100644
--- a/packages/vue/src/index.ts
+++ b/packages/vue/src/index.ts
@@ -121,6 +121,7 @@ export {
TextareaCustomEvent,
ToastOptions,
ToastButton,
+ ToastLayout,
ToggleChangeEventDetail,
ToggleCustomEvent,
} from "@ionic/core/components";