From 188a63546af2651518e059b9b2414dd19ce76ced Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 14 Nov 2018 15:21:27 -0500 Subject: [PATCH] fix(toast): update toast design to match MD spec (#16323) Updates the Material Design Toast to closer match the spec: https://material.io/design/components/snackbars.html - Updates the animation to use opacity, not translate - Updates design with the right background, box-shadow, etc. - Fixes the broken position middle of toast and updates e2e test to include this - Allows for line breaks to be passed in the message fixes #16271 --- .../animation-interface.tsx | 2 +- .../components/toast/animations/ios.enter.ts | 15 +++--- .../components/toast/animations/ios.leave.ts | 11 +++-- .../components/toast/animations/md.enter.ts | 24 ++++++--- .../components/toast/animations/md.leave.ts | 24 ++++----- core/src/components/toast/test/basic/e2e.ts | 49 +++++++++++++++++-- .../components/toast/test/basic/index.html | 6 +-- core/src/components/toast/toast.ios.scss | 16 ++++++ core/src/components/toast/toast.md.scss | 29 ++++++----- core/src/components/toast/toast.md.vars.scss | 34 +++++++------ core/src/components/toast/toast.scss | 24 +++------ core/src/components/toast/toast.tsx | 5 +- core/src/components/toast/toast.vars.scss | 3 -- core/src/themes/test/css-variables/index.html | 28 +++++++---- core/src/utils/overlays.ts | 2 +- core/src/utils/transition.ts | 2 +- 16 files changed, 172 insertions(+), 102 deletions(-) diff --git a/core/src/components/animation-controller/animation-interface.tsx b/core/src/components/animation-controller/animation-interface.tsx index dbf1ac9c97a..eb658cf80b8 100644 --- a/core/src/components/animation-controller/animation-interface.tsx +++ b/core/src/components/animation-controller/animation-interface.tsx @@ -41,7 +41,7 @@ export interface Animation { hasCompleted: boolean; } -export type AnimationBuilder = (Animation: Animation, baseEl: HTMLElement, opts?: any) => Promise; +export type AnimationBuilder = (Animation: Animation, baseEl: any, opts?: any) => Promise; export interface PlayOptions { duration?: number; diff --git a/core/src/components/toast/animations/ios.enter.ts b/core/src/components/toast/animations/ios.enter.ts index 512fdc6f9ec..f738c219674 100644 --- a/core/src/components/toast/animations/ios.enter.ts +++ b/core/src/components/toast/animations/ios.enter.ts @@ -3,12 +3,15 @@ import { Animation } from '../../../interface'; /** * iOS Toast Enter Animation */ -export function iosEnterAnimation(AnimationC: Animation, baseEl: HTMLElement, position: string): Promise { +export function iosEnterAnimation(AnimationC: Animation, baseEl: ShadowRoot, position: string): Promise { const baseAnimation = new AnimationC(); const wrapperAnimation = new AnimationC(); - const wrapperEle = baseEl.querySelector('.toast-wrapper') as HTMLElement; - wrapperAnimation.addElement(wrapperEle); + + const hostEl = baseEl.host || baseEl; + const wrapperEl = baseEl.querySelector('.toast-wrapper') as HTMLElement; + + wrapperAnimation.addElement(wrapperEl); const bottom = `calc(-10px - var(--ion-safe-area-bottom, 0px))`; const top = `calc(10px + var(--ion-safe-area-top, 0px))`; @@ -19,9 +22,9 @@ export function iosEnterAnimation(AnimationC: Animation, baseEl: HTMLElement, po break; case 'middle': const topPosition = Math.floor( - baseEl.clientHeight / 2 - wrapperEle.clientHeight / 2 + hostEl.clientHeight / 2 - wrapperEl.clientHeight / 2 ); - wrapperEle.style.top = `${topPosition}px`; + wrapperEl.style.top = `${topPosition}px`; wrapperAnimation.fromTo('opacity', 0.01, 1); break; default: @@ -29,7 +32,7 @@ export function iosEnterAnimation(AnimationC: Animation, baseEl: HTMLElement, po break; } return Promise.resolve(baseAnimation - .addElement(baseEl) + .addElement(hostEl) .easing('cubic-bezier(.155,1.105,.295,1.12)') .duration(400) .add(wrapperAnimation)); diff --git a/core/src/components/toast/animations/ios.leave.ts b/core/src/components/toast/animations/ios.leave.ts index 2abb50b25dc..9ee06c70ab4 100644 --- a/core/src/components/toast/animations/ios.leave.ts +++ b/core/src/components/toast/animations/ios.leave.ts @@ -3,12 +3,15 @@ import { Animation } from '../../../interface'; /** * iOS Toast Leave Animation */ -export function iosLeaveAnimation(AnimationC: Animation, baseEl: HTMLElement, position: string): Promise { +export function iosLeaveAnimation(AnimationC: Animation, baseEl: ShadowRoot, position: string): Promise { const baseAnimation = new AnimationC(); const wrapperAnimation = new AnimationC(); - const wrapperEle = baseEl.querySelector('.toast-wrapper') as HTMLElement; - wrapperAnimation.addElement(wrapperEle); + + const hostEl = baseEl.host || baseEl; + const wrapperEl = baseEl.querySelector('.toast-wrapper') as HTMLElement; + + wrapperAnimation.addElement(wrapperEl); const bottom = `calc(-10px - var(--ion-safe-area-bottom, 0px))`; const top = `calc(10px + var(--ion-safe-area-top, 0px))`; @@ -25,7 +28,7 @@ export function iosLeaveAnimation(AnimationC: Animation, baseEl: HTMLElement, po break; } return Promise.resolve(baseAnimation - .addElement(baseEl) + .addElement(hostEl) .easing('cubic-bezier(.36,.66,.04,1)') .duration(300) .add(wrapperAnimation)); diff --git a/core/src/components/toast/animations/md.enter.ts b/core/src/components/toast/animations/md.enter.ts index 176c7e9900d..8d72e59b9f6 100644 --- a/core/src/components/toast/animations/md.enter.ts +++ b/core/src/components/toast/animations/md.enter.ts @@ -3,30 +3,38 @@ import { Animation } from '../../../interface'; /** * MD Toast Enter Animation */ -export function mdEnterAnimation(AnimationC: Animation, baseEl: HTMLElement, position: string): Promise { +export function mdEnterAnimation(AnimationC: Animation, baseEl: ShadowRoot, position: string): Promise { const baseAnimation = new AnimationC(); const wrapperAnimation = new AnimationC(); - const wrapperEle = baseEl.querySelector('.toast-wrapper') as HTMLElement; - wrapperAnimation.addElement(wrapperEle); + + const hostEl = baseEl.host || baseEl; + const wrapperEl = baseEl.querySelector('.toast-wrapper') as HTMLElement; + + wrapperAnimation.addElement(wrapperEl); + + const bottom = `calc(8px + var(--ion-safe-area-bottom, 0px))`; + const top = `calc(8px + var(--ion-safe-area-top, 0px))`; switch (position) { case 'top': - wrapperAnimation.fromTo('translateY', '-100%', '0%'); + wrapperEl.style.top = top; + wrapperAnimation.fromTo('opacity', 0.01, 1); break; case 'middle': const topPosition = Math.floor( - baseEl.clientHeight / 2 - wrapperEle.clientHeight / 2 + hostEl.clientHeight / 2 - wrapperEl.clientHeight / 2 ); - wrapperEle.style.top = `${topPosition}px`; + wrapperEl.style.top = `${topPosition}px`; wrapperAnimation.fromTo('opacity', 0.01, 1); break; default: - wrapperAnimation.fromTo('translateY', '100%', '0%'); + wrapperEl.style.bottom = bottom; + wrapperAnimation.fromTo('opacity', 0.01, 1); break; } return Promise.resolve(baseAnimation - .addElement(baseEl) + .addElement(hostEl) .easing('cubic-bezier(.36,.66,.04,1)') .duration(400) .add(wrapperAnimation)); diff --git a/core/src/components/toast/animations/md.leave.ts b/core/src/components/toast/animations/md.leave.ts index 321f5187483..f07b8359c3f 100644 --- a/core/src/components/toast/animations/md.leave.ts +++ b/core/src/components/toast/animations/md.leave.ts @@ -3,26 +3,20 @@ import { Animation } from '../../../interface'; /** * md Toast Leave Animation */ -export function mdLeaveAnimation(AnimationC: Animation, baseEl: HTMLElement, position: string): Promise { +export function mdLeaveAnimation(AnimationC: Animation, baseEl: ShadowRoot): Promise { const baseAnimation = new AnimationC(); const wrapperAnimation = new AnimationC(); - const wrapperEle = baseEl.querySelector('.toast-wrapper') as HTMLElement; - wrapperAnimation.addElement(wrapperEle); - switch (position) { - case 'top': - wrapperAnimation.fromTo('translateY', '0px', '-100%'); - break; - case 'middle': - wrapperAnimation.fromTo('opacity', 0.99, 0); - break; - default: - wrapperAnimation.fromTo('translateY', `0px`, '100%'); - break; - } + const hostEl = baseEl.host || baseEl; + const wrapperEl = baseEl.querySelector('.toast-wrapper') as HTMLElement; + + wrapperAnimation.addElement(wrapperEl); + + wrapperAnimation.fromTo('opacity', 0.99, 0); + return Promise.resolve(baseAnimation - .addElement(baseEl) + .addElement(hostEl) .easing('cubic-bezier(.36,.66,.04,1)') .duration(300) .add(wrapperAnimation)); diff --git a/core/src/components/toast/test/basic/e2e.ts b/core/src/components/toast/test/basic/e2e.ts index 311e34b3db7..97441d57299 100644 --- a/core/src/components/toast/test/basic/e2e.ts +++ b/core/src/components/toast/test/basic/e2e.ts @@ -5,21 +5,64 @@ test('toast: basic', async () => { url: '/src/components/toast/test/basic?ionic:_testing=true' }); - const button = await page.find('#showBottomToast'); + // Show bottom toast + let button = await page.find('#showBottomToast'); await button.click(); let toast = await page.find('ion-toast'); await toast.waitForVisible(); await page.waitFor(250); - let compare = await page.compareScreenshot(); + let compare = await page.compareScreenshot(`bottom toast`); expect(compare).toMatchScreenshot(); await toast.callMethod('dismiss'); await toast.waitForNotVisible(); await page.waitFor(250); - compare = await page.compareScreenshot('dismissed'); + compare = await page.compareScreenshot('dismissed bottom toast'); + expect(compare).toMatchScreenshot(); + + toast = await page.find('ion-toast'); + expect(toast).toBeNull(); + + // Show middle toast + button = await page.find('#showMiddleToast'); + await button.click(); + + toast = await page.find('ion-toast'); + await toast.waitForVisible(); + await page.waitFor(250); + + compare = await page.compareScreenshot(`middle toast`); + expect(compare).toMatchScreenshot(); + + await toast.callMethod('dismiss'); + await toast.waitForNotVisible(); + await page.waitFor(250); + + compare = await page.compareScreenshot('dismissed middle toast'); + expect(compare).toMatchScreenshot(); + + toast = await page.find('ion-toast'); + expect(toast).toBeNull(); + + // Show top toast + button = await page.find('#showTopToast'); + await button.click(); + + toast = await page.find('ion-toast'); + await toast.waitForVisible(); + await page.waitFor(250); + + compare = await page.compareScreenshot(`top toast`); + expect(compare).toMatchScreenshot(); + + await toast.callMethod('dismiss'); + await toast.waitForNotVisible(); + await page.waitFor(250); + + compare = await page.compareScreenshot('dismissed top toast'); expect(compare).toMatchScreenshot(); toast = await page.find('ion-toast'); diff --git a/core/src/components/toast/test/basic/index.html b/core/src/components/toast/test/basic/index.html index 71f11271b33..66a8b9dd6d2 100644 --- a/core/src/components/toast/test/basic/index.html +++ b/core/src/components/toast/test/basic/index.html @@ -21,9 +21,9 @@ Show Toast Bottom - Show Toast Top - Show Toast Middle - Show Toast with long message + Show Toast Top + Show Toast Middle + Show Toast with long message Show Toast with Close Button Show Toast with Custom Close Button Text Show Translucent Toast diff --git a/core/src/components/toast/toast.ios.scss b/core/src/components/toast/toast.ios.scss index 596b87f1c41..2babc1dd3f4 100644 --- a/core/src/components/toast/toast.ios.scss +++ b/core/src/components/toast/toast.ios.scss @@ -30,10 +30,26 @@ backdrop-filter: $toast-ios-translucent-filter; } +.toast-wrapper.toast-top { + @include transform(translate3d(0, -100%, 0)); + + top: 0; +} + .toast-wrapper.toast-middle { opacity: .01; } +.toast-wrapper.toast-bottom { + @include transform(translate3d(0, 100%, 0)); + + bottom: 0; +} + .toast-message { @include padding($toast-ios-title-padding-top, $toast-ios-title-padding-end, $toast-ios-title-padding-bottom, $toast-ios-title-padding-start); } + +.toast-button { + font-size: $toast-button-font-size; +} \ No newline at end of file diff --git a/core/src/components/toast/toast.md.scss b/core/src/components/toast/toast.md.scss index afbfa1f3ef7..5d1566dbd04 100644 --- a/core/src/components/toast/toast.md.scss +++ b/core/src/components/toast/toast.md.scss @@ -5,37 +5,36 @@ // -------------------------------------------------- :host { + --button-color: #{ion-color(primary, base)}; --background: #{$toast-md-background}; - --color: #{$toast-md-title-color}; + --color: #{$toast-md-color}; - font-size: $toast-md-title-font-size; + font-size: $toast-md-font-size; } .toast-wrapper { - @include position-horizontal(0, 0); + @include border-radius(4px); + @include position-horizontal(8px, 8px); @include margin(auto); display: block; position: absolute; - width: $toast-width; max-width: $toast-max-width; - z-index: $z-index-overlay-wrapper; -} + box-shadow: $toast-md-box-shadow; -.toast-wrapper.toast-top { - padding-top: var(--ion-safe-area-top, 0); -} + opacity: .01; -.toast-wrapper.toast-bottom { - padding-bottom: var(--ion-safe-area-bottom, 0); + z-index: $z-index-overlay-wrapper; } -.toast-wrapper.toast-middle { - opacity: .01; +.toast-message { + @include padding($toast-md-message-padding-top, $toast-md-message-padding-end, $toast-md-message-padding-bottom, $toast-md-message-padding-start); + + line-height: $toast-md-message-line-height; } -.toast-message { - @include padding($toast-md-title-padding-top, $toast-md-title-padding-end, $toast-md-title-padding-bottom, $toast-md-title-padding-start); +.toast-button { + --margin-end: 0; } diff --git a/core/src/components/toast/toast.md.vars.scss b/core/src/components/toast/toast.md.vars.scss index 19b4092351d..c4cc32a08d1 100644 --- a/core/src/components/toast/toast.md.vars.scss +++ b/core/src/components/toast/toast.md.vars.scss @@ -3,23 +3,29 @@ // Material Design Toast // -------------------------------------------------- -/// @prop - Background of the toast wrapper -$toast-md-background: $text-color-step-150 !default; +/// @prop - Background of the toast +$toast-md-background: $text-color-step-200 !default; -/// @prop - Color of the toast title -$toast-md-title-color: $background-color !default; +/// @prop - Box shadow of the toast +$toast-md-box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 6px 10px 0 rgba(0, 0, 0, .14), 0 1px 18px 0 rgba(0, 0, 0, .12) !default; -/// @prop - Font size of the toast title -$toast-md-title-font-size: 15px !default; +/// @prop - Font size of the toast +$toast-md-font-size: 14px !default; -/// @prop - Padding top of the toast title -$toast-md-title-padding-top: 19px !default; +/// @prop - Color of the toast +$toast-md-color: $background-color-step-50 !default; -/// @prop - Padding end of the toast title -$toast-md-title-padding-end: 16px !default; +/// @prop - Font size of the toast message +$toast-md-message-line-height: 20px !default; -/// @prop - Padding bottom of the toast title -$toast-md-title-padding-bottom: 17px !default; +/// @prop - Padding top of the toast message +$toast-md-message-padding-top: 14px !default; -/// @prop - Padding start of the toast title -$toast-md-title-padding-start: $toast-md-title-padding-end !default; +/// @prop - Padding end of the toast message +$toast-md-message-padding-end: 16px !default; + +/// @prop - Padding bottom of the toast message +$toast-md-message-padding-bottom: $toast-md-message-padding-top !default; + +/// @prop - Padding start of the toast message +$toast-md-message-padding-start: $toast-md-message-padding-end !default; diff --git a/core/src/components/toast/toast.scss b/core/src/components/toast/toast.scss index 76032e2f6f5..67b96404f0f 100644 --- a/core/src/components/toast/toast.scss +++ b/core/src/components/toast/toast.scss @@ -4,20 +4,20 @@ // -------------------------------------------------- :host { - --button-color: inherit; - /** * @prop --background: Background of the toast * @prop --button-color: Color of the button text * @prop --color: Color of the toast text */ + --button-color: inherit; + @include position(0, null, null, 0); display: block; position: absolute; - width: $toast-width; - height: $toast-width; + width: 100%; + height: 100%; color: var(--color); @@ -38,18 +38,6 @@ background: var(--background); } -.toast-wrapper.toast-top { - @include transform(translate3d(0, -100%, 0)); - - top: 0; -} - -.toast-wrapper.toast-bottom { - @include transform(translate3d(0, 100%, 0)); - - bottom: 0; -} - .toast-container { display: flex; @@ -61,10 +49,10 @@ .toast-button { color: var(--button-color); - - font-size: $toast-button-font-size; } .toast-message { flex: 1; + + white-space: pre; } diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index 82ba9c74d08..ffda95d8b73 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -28,9 +28,12 @@ export class Toast implements ComponentInterface, OverlayInterface { animation: Animation | undefined; @Prop({ connect: 'ion-animation-controller' }) animationCtrl!: HTMLIonAnimationControllerElement; + @Prop({ context: 'config' }) config!: Config; - /** @internal */ + /** + * @internal + */ @Prop() overlayIndex!: number; /** diff --git a/core/src/components/toast/toast.vars.scss b/core/src/components/toast/toast.vars.scss index 3b860bed7d6..fad777bcc5a 100644 --- a/core/src/components/toast/toast.vars.scss +++ b/core/src/components/toast/toast.vars.scss @@ -3,9 +3,6 @@ // Toast // -------------------------------------------------- -/// @prop - Width of the toast -$toast-width: 100% !default; - /// @prop - Max width of the toast $toast-max-width: 700px !default; diff --git a/core/src/themes/test/css-variables/index.html b/core/src/themes/test/css-variables/index.html index 3fa7a0899f6..d55c00c03f0 100644 --- a/core/src/themes/test/css-variables/index.html +++ b/core/src/themes/test/css-variables/index.html @@ -48,9 +48,24 @@ - + + + Lists + + + + Colors + + + + Other + + + + + - + Lists
@@ -280,7 +295,7 @@ - + Colors @@ -636,7 +651,7 @@ - + Other @@ -656,12 +671,7 @@
- - - - -