Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9b25ed0
feat(css): add support for the subtle color map
brandyscarney Mar 26, 2025
2a0fdf3
Merge branch 'next' into FW-6252
brandyscarney Mar 26, 2025
e8605e7
test(theme): skip test because all tests fail on one or more colors
brandyscarney Mar 26, 2025
5391165
chore(): add updated snapshots
Ionitron Mar 26, 2025
19c944a
style: comments
brandyscarney Mar 26, 2025
147550a
Merge branch 'next' into FW-6252
brandyscarney Mar 28, 2025
e13d85d
Merge branch 'next' into FW-6252
brandyscarney Apr 2, 2025
8fc775f
refactor(themes): add foreground color for colors when used as text
brandyscarney Apr 2, 2025
5f2e195
feat(toast): add subtle hue for the ionic theme
brandyscarney Apr 2, 2025
9bfe374
test(theme): use proper color variants
brandyscarney Apr 2, 2025
46daa85
test: backdrop
brandyscarney Apr 2, 2025
caf3c2d
style: lint
brandyscarney Apr 3, 2025
aed941f
test(themes): re-enable tests with correct checks
brandyscarney Apr 3, 2025
e86e9d5
chore(): add updated snapshots
Ionitron Apr 3, 2025
59d713f
revert back to tokens
brandyscarney Apr 3, 2025
6306fe6
style: lint
brandyscarney Apr 3, 2025
e2fbe91
Merge branch 'FW-6252' into FW-6252-toast
brandyscarney Apr 3, 2025
a84f282
Merge branch 'next' into FW-6252
brandyscarney Apr 4, 2025
60a241f
fix(themes): add tertiary, remove TODOs
brandyscarney Apr 4, 2025
465a5ef
lint again and again and again
brandyscarney Apr 4, 2025
7e8e4e8
Merge branch 'FW-6252' into FW-6252-toast
brandyscarney Apr 4, 2025
3187866
chore(): add updated snapshots
Ionitron Apr 4, 2025
75421d3
fix(themes): update other palettes to include foreground
brandyscarney Apr 4, 2025
eca3566
fix(themes): do not require the foreground variant for ios and md
brandyscarney Apr 7, 2025
67f8334
Merge branch 'next' into FW-6252
brandyscarney Apr 7, 2025
df94653
Merge branch 'FW-6252' into FW-6252-toast
brandyscarney Apr 7, 2025
22ddb4c
fix(css): remove the unused foreground vars
brandyscarney Apr 7, 2025
6d05513
style: comment cleanup
brandyscarney Apr 7, 2025
da4e069
fix(themes): fallback to base if foreground is undefined
brandyscarney Apr 7, 2025
db28e8b
Merge branch 'FW-6252' into FW-6252-toast
brandyscarney Apr 7, 2025
c19c29d
fix(toast): default hue should be subtle
brandyscarney Apr 7, 2025
be28b4f
test(toast): title
brandyscarney Apr 7, 2025
a786cc1
fix(toast): move the background and colors to the subtle and bold cla…
brandyscarney Apr 7, 2025
eee9f14
test(toast): add e2e test for subtle and improve positioning using in…
brandyscarney Apr 7, 2025
d184ad0
chore(): add updated snapshots
brandyscarney Apr 7, 2025
811e2a5
fix(toast): default to neutral colors
brandyscarney Apr 8, 2025
ad2cdd1
chore(): add updated snapshots
brandyscarney Apr 8, 2025
01a2583
fix(toast): use correct button colors
brandyscarney Apr 8, 2025
fd0ec49
chore(): add updated snapshots
brandyscarney Apr 8, 2025
670ebb2
chore: build
brandyscarney Apr 8, 2025
a3c52ff
docs
brandyscarney Apr 11, 2025
c33fec5
Merge branch 'next' into FW-6252-toast
brandyscarney Apr 11, 2025
d763ff8
Merge branch 'next' into FW-6252-toast
brandyscarney Apr 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2436,6 +2436,7 @@ ion-toast,prop,duration,number,config.getNumber('toastDuration', 0),false,false
ion-toast,prop,enterAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-toast,prop,header,string | undefined,undefined,false,false
ion-toast,prop,htmlAttributes,undefined | { [key: string]: any; },undefined,false,false
ion-toast,prop,hue,"bold" | "subtle" | undefined,'subtle',false,false
ion-toast,prop,icon,string | undefined,undefined,false,false
ion-toast,prop,isOpen,boolean,false,false,false
ion-toast,prop,keyboardClose,boolean,false,false,false
Expand Down
8 changes: 8 additions & 0 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3844,6 +3844,10 @@ export namespace Components {
* Additional attributes to pass to the toast.
*/
"htmlAttributes"?: { [key: string]: any };
/**
* Set to `"bold"` for a toast with vibrant, bold colors or to `"subtle"` for a toast with muted, subtle colors. Only applies to the `ionic` theme.
*/
"hue"?: 'bold' | 'subtle';
/**
* The name of the icon to display, or the path to a valid SVG file. See `ion-icon`. https://ionic.io/ionicons
*/
Expand Down Expand Up @@ -9384,6 +9388,10 @@ declare namespace LocalJSX {
* Additional attributes to pass to the toast.
*/
"htmlAttributes"?: { [key: string]: any };
/**
* Set to `"bold"` for a toast with vibrant, bold colors or to `"subtle"` for a toast with muted, subtle colors. Only applies to the `ionic` theme.
*/
"hue"?: 'bold' | 'subtle';
/**
* The name of the icon to display, or the path to a valid SVG file. See `ion-icon`. https://ionic.io/ionicons
*/
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
123 changes: 123 additions & 0 deletions core/src/components/toast/test/hue/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Toast - Hue</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>

<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Toast - Hue</ion-title>
</ion-toolbar>
</ion-header>

<ion-content class="ion-padding" id="content">
<p>
Toasts are presented indefinitely but can be closed by clicking the backdrop. The backdrop has been added for
demo purposes only.
</p>

<button id="show-subtle-toasts" class="expand">Show All Subtle Toasts</button>
<button id="show-bold-toasts" class="expand">Show All Bold Toasts</button>
</ion-content>

<div id="backdrop" class="backdrop"></div>

<ion-toast position="top"></ion-toast>
<ion-toast color="primary" position="top"></ion-toast>
<ion-toast color="secondary" position="top"></ion-toast>
<ion-toast color="tertiary" position="top"></ion-toast>
<ion-toast color="success" position="top"></ion-toast>
<ion-toast color="warning" position="top"></ion-toast>
<ion-toast color="danger" position="top"></ion-toast>
<ion-toast color="light" position="top"></ion-toast>
<ion-toast color="medium" position="top"></ion-toast>
<ion-toast color="dark" position="top"></ion-toast>
</ion-app>

<script type="module">
let lastToast = null;
let toastOffset = 10;

// Show all toasts when the button is clicked
function openAllToasts(isBold) {
toastOffset = 10;

const toasts = document.querySelectorAll('ion-toast');

toasts.forEach((toast, index) => {
toast.removeAttribute('hue');
toast.hue = isBold ? 'bold' : 'subtle';
const hue = toast.hue;

const color = toast.color || 'default';
toast.message = `This is a ${color} toast.`;

toast.icon = 'information-circle';

toast.buttons = [
{
text: 'Action',
},
{
icon: 'close',
role: 'cancel',
},
];

// Set dynamic position for each toast to ensure they don't overlap
toast.style.position = 'absolute';
toast.style.top = `${toastOffset}px`;
toast.style.left = '50%';
toast.style.transform = 'translateX(-50%)';

toast.present();

// Update the toastOffset for the next toast to ensure it's positioned below the previous one
toastOffset += 60;
});

document.getElementById('backdrop').style.display = 'block';
}

// Dismiss all toasts when backdrop is clicked
function dismissAllToasts() {
const toasts = document.querySelectorAll('ion-toast');
toasts.forEach((toast) => toast.dismiss());

document.getElementById('backdrop').style.display = 'none';
}

document.addEventListener('DOMContentLoaded', function () {
document.getElementById('show-subtle-toasts').addEventListener('click', () => openAllToasts(false));
document.getElementById('show-bold-toasts').addEventListener('click', () => openAllToasts(true));

document.getElementById('backdrop').addEventListener('click', dismissAllToasts);
});
</script>

<style>
.backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: none;
}
</style>
</body>
</html>
59 changes: 59 additions & 0 deletions core/src/components/toast/test/hue/toast.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { expect } from '@playwright/test';
import type { E2EPage, E2EPageOptions, ScreenshotFn, EventSpy } from '@utils/test/playwright';
import { configs, test } from '@utils/test/playwright';

class ToastFixture {
readonly page: E2EPage;

private ionToastDidPresent!: EventSpy;

constructor(page: E2EPage) {
this.page = page;
}

async goto(config: E2EPageOptions) {
const { page } = this;
await page.goto(`/src/components/toast/test/hue`, config);
this.ionToastDidPresent = await page.spyOnEvent('ionToastDidPresent');
}

async openToast(selector: string) {
const { page, ionToastDidPresent } = this;
const button = page.locator(selector);
await button.click();

await ionToastDidPresent.next();

return {
toast: page.locator('ion-toast'),
};
}

async screenshot(screenshotModifier: string, screenshot: ScreenshotFn) {
const { page } = this;

const screenshotString = screenshot(`toast-${screenshotModifier}`);

await expect(page).toHaveScreenshot(screenshotString);
}
}

configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('toast: hue'), () => {
let toastFixture: ToastFixture;
test.beforeEach(async ({ page }) => {
toastFixture = new ToastFixture(page);
await toastFixture.goto(config);
});

test('should show all subtle toasts', async () => {
await toastFixture.openToast('#show-subtle-toasts');
await toastFixture.screenshot('subtle', screenshot);
});

test('should show all bold toasts', async () => {
await toastFixture.openToast('#show-bold-toasts');
await toastFixture.screenshot('bold', screenshot);
});
});
});
41 changes: 38 additions & 3 deletions core/src/components/toast/toast.ionic.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
// --------------------------------------------------

:host {
--background: #{globals.$ion-primitives-neutral-1200};
--box-shadow: #{globals.$ion-elevation-4};
--button-color: #{globals.$ion-primitives-base-white};
--color: #{globals.$ion-primitives-base-white};
--max-width: 343px;
--start: 8px;
--end: 8px;
Expand Down Expand Up @@ -132,3 +129,41 @@
.toast-button-icon {
font-size: globals.$ion-scale-600;
}

// Bold Toast
// --------------------------------------------------

:host(.toast-hue-bold) {
--background: #{globals.$ion-bg-neutral-boldest-default};
--background-activated: #{globals.$ion-bg-neutral-boldest-press};
--color: #{globals.$ion-text-inverse};
--button-color: #{globals.$ion-text-inverse};
}

:host(.toast-hue-bold.ion-color) .toast-wrapper {
background: globals.current-color(base);
color: globals.current-color(contrast);
}

:host(.toast-hue-bold.ion-color) .toast-button {
color: globals.current-color(contrast);
}

// Subtle Toast
// --------------------------------------------------

:host(.toast-hue-subtle) {
--background: #{globals.$ion-bg-neutral-subtlest-default};
--background-activated: #{globals.$ion-bg-neutral-subtlest-press};
--color: #{globals.$ion-text-default};
--button-color: #{globals.$ion-text-link-default};
}

:host(.toast-hue-subtle) .toast-button-cancel {
color: #{globals.$ion-icon-subtlest};
}

:host(.toast-hue-subtle.ion-color) .toast-wrapper {
background: globals.current-color(base, $subtle: true);
color: globals.current-color(contrast, $subtle: true);
}
11 changes: 10 additions & 1 deletion core/src/components/toast/toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ export class Toast implements ComponentInterface, OverlayInterface {
*/
@Prop() header?: string;

/**
* Set to `"bold"` for a toast with vibrant, bold colors or to `"subtle"` for
* a toast with muted, subtle colors.
*
* Only applies to the `ionic` theme.
*/
@Prop() hue?: 'bold' | 'subtle' = 'subtle';

/**
* Defines how the message and buttons are laid out in the toast.
* 'baseline': The message and the buttons will appear on the same line.
Expand Down Expand Up @@ -715,7 +723,7 @@ export class Toast implements ComponentInterface, OverlayInterface {
}

render() {
const { layout, el, revealContentToScreenReader, header, message } = this;
const { layout, el, revealContentToScreenReader, header, hue, message } = this;
const allButtons = this.getButtons();
const startButtons = allButtons.filter((b) => b.side === 'start');
const endButtons = allButtons.filter((b) => b.side !== 'start');
Expand Down Expand Up @@ -753,6 +761,7 @@ export class Toast implements ComponentInterface, OverlayInterface {
'overlay-hidden': true,
'toast-translucent': this.translucent,
[`toast-shape-${shape}`]: shape !== undefined,
[`toast-hue-${hue}`]: hue !== undefined,
})}
onIonToastWillDismiss={this.dispatchCancelHandler}
>
Expand Down
4 changes: 2 additions & 2 deletions packages/angular/src/directives/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2439,15 +2439,15 @@ export declare interface IonTitle extends Components.IonTitle {}


@ProxyCmp({
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'shape', 'swipeGesture', 'theme', 'translucent', 'trigger'],
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'hue', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'shape', 'swipeGesture', 'theme', 'translucent', 'trigger'],
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss']
})
@Component({
selector: 'ion-toast',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'shape', 'swipeGesture', 'theme', 'translucent', 'trigger'],
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'hue', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'shape', 'swipeGesture', 'theme', 'translucent', 'trigger'],
})
export class IonToast {
protected el: HTMLIonToastElement;
Expand Down
4 changes: 2 additions & 2 deletions packages/angular/standalone/src/directives/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2202,15 +2202,15 @@ export declare interface IonTitle extends Components.IonTitle {}

@ProxyCmp({
defineCustomElementFn: defineIonToast,
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'shape', 'swipeGesture', 'theme', 'translucent', 'trigger'],
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'hue', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'shape', 'swipeGesture', 'theme', 'translucent', 'trigger'],
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss']
})
@Component({
selector: 'ion-toast',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'shape', 'swipeGesture', 'theme', 'translucent', 'trigger'],
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'hue', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'shape', 'swipeGesture', 'theme', 'translucent', 'trigger'],
standalone: true
})
export class IonToast {
Expand Down
2 changes: 1 addition & 1 deletion packages/vue/src/components/Overlays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ export const IonPickerLegacy = /*@__PURE__*/ defineOverlayContainer<JSX.IonPicke

export const IonPopover = /*@__PURE__*/ defineOverlayContainer<JSX.IonPopover>('ion-popover', defineIonPopoverCustomElement, ['alignment', 'animated', 'arrow', 'backdropDismiss', 'component', 'componentProps', 'dismissOnSelect', 'enterAnimation', 'event', 'focusTrap', 'htmlAttributes', 'isOpen', 'keepContentsMounted', 'keyboardClose', 'leaveAnimation', 'mode', 'reference', 'showBackdrop', 'side', 'size', 'theme', 'translucent', 'trigger', 'triggerAction']);

export const IonToast = /*@__PURE__*/ defineOverlayContainer<JSX.IonToast>('ion-toast', defineIonToastCustomElement, ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'shape', 'swipeGesture', 'theme', 'translucent', 'trigger']);
export const IonToast = /*@__PURE__*/ defineOverlayContainer<JSX.IonToast>('ion-toast', defineIonToastCustomElement, ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'hue', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'shape', 'swipeGesture', 'theme', 'translucent', 'trigger']);

Loading