diff --git a/angular/src/index.ts b/angular/src/index.ts
index a28a12dfefe..38d2130b7bb 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 c22df0bde25..8eb31c06047 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 f5f68679914..1164b07ad0a 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 00000000000..3562accab74
--- /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 00000000000..f5c22cd1b17
--- /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 00000000000..1a906abf687
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 00000000000..d7f8266d099
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 00000000000..1d24d41c61e
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 00000000000..1b0edc0efa8
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 00000000000..5108314254f
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 00000000000..fdeff156432
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 00000000000..1ba4b083e3f
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 00000000000..ecab85e31fe
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 00000000000..d5827d5ca99
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 00000000000..5a20ad35588
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 00000000000..379adbf2bda
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 00000000000..e592c426bd1
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 9b417e37414..364ee9f2cd0 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 7581c4b8af1..968afe913ab 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 0a44e698084..0418e86c7c2 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 cc941008e7a..50812b7a066 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 4a7bab011f0..b3af281c146 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";