Skip to content
This repository has been archived by the owner on Aug 29, 2023. It is now read-only.

Commit

Permalink
fix(toast): Hide scrollbars during animation.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
topherfangio authored and jelbourn committed Dec 7, 2015
1 parent 7edda11 commit cae51a6
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 85 deletions.
24 changes: 13 additions & 11 deletions src/components/toast/toast-theme.scss
Original file line number Diff line number Diff line change
@@ -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}}';
}
}
}
}
Expand Down
34 changes: 11 additions & 23 deletions src/components/toast/toast.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,6 @@ function MdToastDirective($mdToast) {
* </div>
* </hljs>
*
* 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.
*
* <hljs lang="css">
* .md-toast-animating {
* overflow: auto !important;
* }
* </hljs>
*
* @usage
* <hljs lang="html">
* <div ng-controller="MyController">
Expand Down Expand Up @@ -246,16 +233,17 @@ function MdToastProvider($$interimElementProvider) {
methods: ['textContent', 'content', 'action', 'highlightAction', 'theme', 'parent'],
options: /* @ngInject */ function($mdToast, $mdTheming) {
var opts = {
template: [
'<md-toast md-theme="{{ toast.theme }}" ng-class="{\'md-capsule\': toast.capsule}">',
'<span flex role="alert" aria-relevant="all" aria-atomic="true">' +
'{{ toast.content }}' +
'</span>',
'<md-button class="md-action" ng-if="toast.action" ng-click="toast.resolve()" ng-class="{\'md-highlight\': toast.highlightAction}">',
'{{ toast.action }}',
'</md-button>',
'</md-toast>'
].join(''),
template:
'<md-toast md-theme="{{ toast.theme }}" ng-class="{\'md-capsule\': toast.capsule}">' +
' <div class="md-toast-content">' +
' <span flex role="alert" aria-relevant="all" aria-atomic="true">' +
' {{ toast.content }}' +
' </span>' +
' <md-button class="md-action" ng-if="toast.action" ng-click="toast.resolve()" ng-class="{\'md-highlight\': toast.highlightAction}">' +
' {{ toast.action }}' +
' </md-button>' +
' </div>' +
'</md-toast>',
controller: /* @ngInject */ function mdToastCtrl($scope) {
var self = this;
$scope.$watch(function() { return activeToastContent; }, function() {
Expand Down
124 changes: 82 additions & 42 deletions src/components/toast/toast.scss
Original file line number Diff line number Diff line change
@@ -1,76 +1,107 @@
// 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 */
&.md-swipeleft,
&.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);
}
}
}
/*
* When the toast doesn't take up the whole screen,
* 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);
}
}
}

Expand Down Expand Up @@ -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;
}

/*
Expand All @@ -130,23 +166,27 @@ 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);
}
}
}
}
}

@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;
Expand Down
18 changes: 9 additions & 9 deletions src/components/toast/toast.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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');
}));


Expand All @@ -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');
}));
});

Expand All @@ -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) {
Expand All @@ -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) {
Expand Down

0 comments on commit cae51a6

Please sign in to comment.