From cae51a65f883595cb73fed4dba9e60c7c068facc Mon Sep 17 00:00:00 2001 From: Topher Fangio Date: Tue, 1 Dec 2015 16:22:31 -0600 Subject: [PATCH] fix(toast): Hide scrollbars during animation. During animation, certain browsers would show scroll bars if the toast was positioned at the bottom of the parent container. Fix by wrapping the animated portions in a div with hidden overflow. Also, update documentation with notes about positioning and recommendations. BREAKING CHANGE md-toast now applies the enter/leave animations to an inner div with a class `md-toast-content`. If you have customized the animations or other styles of your toast, you will need to update your CSS to account for the new wrapper. Fixes #2936. Closes #6029 --- src/components/toast/toast-theme.scss | 24 ++--- src/components/toast/toast.js | 34 +++---- src/components/toast/toast.scss | 124 +++++++++++++++++--------- src/components/toast/toast.spec.js | 18 ++-- 4 files changed, 115 insertions(+), 85 deletions(-) diff --git a/src/components/toast/toast-theme.scss b/src/components/toast/toast-theme.scss index c2f1c248540..486c70f334d 100644 --- a/src/components/toast/toast-theme.scss +++ b/src/components/toast/toast-theme.scss @@ -1,16 +1,18 @@ md-toast.md-THEME_NAME-theme { - background-color: #323232; - color: '{{background-50}}'; - - .md-button { + .md-toast-content { + background-color: #323232; color: '{{background-50}}'; - &.md-highlight { - color: '{{primary-A200}}'; - &.md-accent { - color: '{{accent-A200}}'; - } - &.md-warn { - color: '{{warn-A200}}'; + + .md-button { + color: '{{background-50}}'; + &.md-highlight { + color: '{{primary-A200}}'; + &.md-accent { + color: '{{accent-A200}}'; + } + &.md-warn { + color: '{{warn-A200}}'; + } } } } diff --git a/src/components/toast/toast.js b/src/components/toast/toast.js index 97952cb4424..10e347e892a 100644 --- a/src/components/toast/toast.js +++ b/src/components/toast/toast.js @@ -62,19 +62,6 @@ function MdToastDirective($mdToast) { * * * - * Additionally, during animation, we will add the `md-toast-animating` class to the parent - * container. This defines a simple rule of `overflow: hidden !important;` to ensure that - * scrollbars are not visible on the parent during animation if you use a different overflow style. - * - * If you need to override this, you can use the following CSS, but be aware that it may cause - * scrollbars to intermittently appear. - * - * - * .md-toast-animating { - * overflow: auto !important; - * } - * - * * @usage * *
@@ -246,16 +233,17 @@ function MdToastProvider($$interimElementProvider) { methods: ['textContent', 'content', 'action', 'highlightAction', 'theme', 'parent'], options: /* @ngInject */ function($mdToast, $mdTheming) { var opts = { - template: [ - '', - '' + - '{{ toast.content }}' + - '', - '', - '{{ toast.action }}', - '', - '' - ].join(''), + template: + '' + + '
' + + ' ' + + ' {{ toast.content }}' + + ' ' + + ' ' + + ' {{ toast.action }}' + + ' ' + + '
' + + '
', controller: /* @ngInject */ function mdToastCtrl($scope) { var self = this; $scope.$watch(function() { return activeToastContent; }, function() { diff --git a/src/components/toast/toast.scss b/src/components/toast/toast.scss index ae12de6590a..f47157b4bcc 100644 --- a/src/components/toast/toast.scss +++ b/src/components/toast/toast.scss @@ -1,39 +1,54 @@ // See height set globally, depended on by buttons - md-toast { - display: flex; - position:absolute; + position: absolute; z-index: $z-index-toast; box-sizing: border-box; - align-items: center; + cursor: default; + overflow: hidden; - min-height: 48px; - padding-left: 24px; - padding-right: 24px; + // Add some padding to the outer toast container so that the wrapper's box shadow is visible + padding: $toast-margin; - box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26); - border-radius: 2px; - font-size: 14px; - cursor: default; + // Setup opacity transition on whole toast + opacity: 1; + transition: $swift-ease-out; - height: 0px; - max-height: 7*$toast-height; - max-width: 100%; + .md-toast-content { + display: flex; + align-items: center; - overflow:hidden; + height: 0; + max-height: 7 * $toast-height; + max-width: 100%; + min-height: 48px; + padding-left: 24px; + padding-right: 24px; + + box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26); + border-radius: 2px; + font-size: 14px; + + overflow: hidden; + + // Setup for transform transitions on inner content + transform: translate3d(0, 0, 0) rotateZ(0deg); + transition: $swift-ease-out; + } &.md-capsule { border-radius: 24px; - } - opacity: 1; - transform: translate3d(0,0,0) rotateZ(0deg); - transition: $swift-ease-out; + .md-toast-content { + border-radius: 24px; + } + } &.ng-leave-active { - transition: $swift-ease-in; + .md-toast-content { + transition: $swift-ease-in; + } } /* Transition differently when swiping */ @@ -41,18 +56,26 @@ md-toast { &.md-swiperight, &.md-swipeup, &.md-swipedown { - transition: $swift-ease-out; + .md-toast-content { + transition: $swift-ease-out; + } } &.ng-enter { - transform: translate3d(0, 100%, 0); + opacity: 0; + .md-toast-content { + transform: translate3d(0, 100%, 0); + } &.md-top { - transform: translate3d(0, -100%, 0); + .md-toast-content { + transform: translate3d(0, -100%, 0); + } } - opacity: 0; &.ng-enter-active { - transform: translate3d(0, 0, 0); opacity: 1; + .md-toast-content { + transform: translate3d(0, 0, 0); + } } } /* @@ -60,17 +83,25 @@ md-toast { * make it rotate when the user swipes it away */ &.ng-leave.ng-leave-active { - opacity: 0; - transform: translate3d(0, 100%, 0); + .md-toast-content { + opacity: 0; + transform: translate3d(0, 100%, 0); + } &.md-swipeup { - transform: translate3d(0, -50%, 0); + .md-toast-content { + transform: translate3d(0, -50%, 0); + } } &.md-swipedown { - transform: translate3d(0, 50%, 0); + .md-toast-content { + transform: translate3d(0, 50%, 0); + } } &.md-top { - transform: translate3d(0, -100%, 0); + .md-toast-content { + transform: translate3d(0, -100%, 0); + } } } @@ -100,28 +131,33 @@ md-toast { &.ng-leave.ng-leave-active { &.md-swipeup { - transform: translate3d(0, -50%, 0); + .md-toast-content { + transform: translate3d(0, -50%, 0); + } } &.md-swipedown { - transform: translate3d(0, 50%, 0); + .md-toast-content { + transform: translate3d(0, 50%, 0); + } } } } } + @media (min-width: $layout-breakpoint-sm) { md-toast { - min-width: 288px; + min-width: 288px + $toast-margin * 2; &.md-bottom { - bottom: $toast-margin; + bottom: 0; } &.md-left { - left: $toast-margin; + left: 0; } &.md-right { - right: $toast-margin; + right: 0; } &.md-top { - top: $toast-margin; + top: 0; } /* @@ -130,10 +166,14 @@ md-toast { */ &.ng-leave.ng-leave-active { &.md-swipeleft { - transform: translate3d(-50%, 0, 0); + .md-toast-content { + transform: translate3d(-50%, 0, 0); + } } &.md-swiperight { - transform: translate3d(50%, 0, 0); + .md-toast-content { + transform: translate3d(50%, 0, 0); + } } } } @@ -141,12 +181,12 @@ md-toast { @media (min-width: $layout-breakpoint-lg) { md-toast { - max-width: $baseline-grid * 71; + .md-toast-content { + max-width: $baseline-grid * 71; + } } } - - @media screen and (-ms-high-contrast: active) { md-toast { border: 1px solid #fff; diff --git a/src/components/toast/toast.spec.js b/src/components/toast/toast.spec.js index afe31a00711..6369ae326ab 100644 --- a/src/components/toast/toast.spec.js +++ b/src/components/toast/toast.spec.js @@ -43,7 +43,7 @@ describe('$mdToast service', function() { $material.flushOutstandingAnimations(); - expect(parent.find('span').text()).toBe('Do something'); + expect(parent.find('span').text().trim()).toBe('Do something'); expect(parent.find('md-toast')).toHaveClass('md-capsule'); expect(parent.find('md-toast').attr('md-theme')).toBe('some-theme'); @@ -58,7 +58,7 @@ describe('$mdToast service', function() { $rootScope.$digest(); $mdToast.updateContent('Goodbye world'); $rootScope.$digest(); - expect($rootElement.find('span').text()).toBe('Goodbye world'); + expect($rootElement.find('span').text().trim()).toBe('Goodbye world'); })); it('supports an action toast', inject(function($mdToast, $rootScope, $material) { @@ -76,7 +76,7 @@ describe('$mdToast service', function() { }); $material.flushOutstandingAnimations(); var button = parent.find('button'); - expect(button.text()).toBe('Click me'); + expect(button.text().trim()).toBe('Click me'); button.triggerHandler('click'); $material.flushInterimElement(); expect(resolved).toBe(true); @@ -100,8 +100,8 @@ describe('$mdToast service', function() { var content = parent.find('span').eq(0); var button = parent.find('button'); - expect(content.text()).toBe('Do something'); - expect(button.text()).toBe('Click me'); + expect(content.text().trim()).toBe('Do something'); + expect(button.text().trim()).toBe('Click me'); })); @@ -119,8 +119,8 @@ describe('$mdToast service', function() { var content = parent.find('span').eq(0); var button = parent.find('button'); - expect(content.text()).toBe('Do something'); - expect(button.text()).toBe('Click me'); + expect(content.text().trim()).toBe('Do something'); + expect(button.text().trim()).toBe('Click me'); })); }); @@ -145,7 +145,7 @@ describe('$mdToast service', function() { }); var toast = $rootElement.find('md-toast'); $timeout.flush(); - expect(toast.text()).toBe('1234'); + expect(toast.text().trim()).toBe('1234'); })); it('should have templateUrl', inject(function($timeout, $rootScope, $templateCache, $rootElement) { @@ -154,7 +154,7 @@ describe('$mdToast service', function() { templateUrl: 'template.html', }); var toast = $rootElement.find('md-toast'); - expect(toast.text()).toBe('hello, 1'); + expect(toast.text().trim()).toBe('hello, 1'); })); it('should add position class to toast', inject(function($rootElement, $timeout) {