Permalink
Browse files

fix(autocomplete): fix messages not appearing. (#9909)

Fixes #9468.
  • Loading branch information...
1 parent 454b974 commit ce5f7c28770bd928afd31a33ee33000deb3311e6 @topherfangio topherfangio committed with kara Nov 15, 2016
Showing with 142 additions and 14 deletions.
  1. +90 −2 src/components/input/input-animations.spec.js
  2. +46 −12 src/components/input/input.js
  3. +6 −0 test/angular-material-mocks.js
@@ -1,5 +1,5 @@
describe('md-input-container animations', function() {
- var $rootScope, $compile, $animateCss, $material,
+ var $rootScope, $compile, $animateCss, $material, $$mdInput,
el, pageScope, invalidAnimation, messagesAnimation, messageAnimation,
cssTransitionsDisabled = false, lastAnimateCall;
@@ -98,6 +98,93 @@ describe('md-input-container animations', function() {
expect(lastAnimateCall.options.to).toEqual({"opacity": 1, "margin-top": "0"});
});
+ describe('method tests', function() {
+
+ describe('#showInputMessages', function() {
+ it('logs a warning with no messages element', inject(function($log) {
+ // Note that the element does NOT have a parent md-input-messages-animation class
+ var element = angular.element('<div><div class="md-input-message-animation"></div></div>');
+ var done = jasmine.createSpy('done');
+ var warnSpy = spyOn($log, 'warn');
+
+ $$mdInput.messages.show(element, done);
+
+ expect(done).toHaveBeenCalled();
+ expect(warnSpy).toHaveBeenCalled();
+ }));
+
+ it('logs a warning with no messages children', inject(function($log) {
+ // Note that the element does NOT have any child md-input-message-animation divs
+ var element = angular.element('<div class="md-input-messages-animation"></div>');
+ var done = jasmine.createSpy('done');
+ var warnSpy = spyOn($log, 'warn');
+
+ $$mdInput.messages.show(element, done);
+
+ expect(done).toHaveBeenCalled();
+ expect(warnSpy).toHaveBeenCalled();
+ }));
+ });
+
+ describe('#hideInputMessages', function() {
+ it('logs a warning with no messages element', inject(function($log) {
+ // Note that the element does NOT have a parent md-input-messages-animation class
+ var element = angular.element('<div><div class="md-input-message-animation"></div></div>');
+ var done = jasmine.createSpy('done');
+ var warnSpy = spyOn($log, 'warn');
+
+ $$mdInput.messages.hide(element, done);
+
+ expect(done).toHaveBeenCalled();
+ expect(warnSpy).toHaveBeenCalled();
+ }));
+
+ it('logs a warning with no messages children', inject(function($log) {
+ // Note that the element does NOT have any child md-input-message-animation divs
+ var element = angular.element('<div class="md-input-messages-animation"></div>');
+ var done = jasmine.createSpy('done');
+ var warnSpy = spyOn($log, 'warn');
+
+ $$mdInput.messages.hide(element, done);
+
+ expect(done).toHaveBeenCalled();
+ expect(warnSpy).toHaveBeenCalled();
+ }));
+ });
+
+ describe('#getMessagesElement', function() {
+
+ it('finds the messages element itself', function() {
+ var template = '<div class="md-input-messages-animation"></div>';
+ var dom = angular.element(template);
+ var messages = $$mdInput.messages.getElement(dom);
+
+ expect(dom).toEqual(messages);
+ });
+
+ it('finds a child element', function(){
+ var template = '<div><div class="md-input-messages-animation"></div></div>';
+ var dom = angular.element(template);
+ var realMessages = angular.element(dom[0].querySelector('.md-input-messages-animation'));
+ var messages = $$mdInput.messages.getElement(dom);
+
+ expect(realMessages).toEqual(messages);
+ });
+
+ it('finds the parent of a message animation element', function() {
+ var template =
+ '<div class="md-input-messages-animation">' +
+ ' <div class="md-input-message-animation"></div>' +
+ '</div>';
+ var dom = angular.element(template);
+ var message = angular.element(dom[0].querySelector('.md-input-message-animation'));
+ var messages = $$mdInput.messages.getElement(message);
+
+ expect(dom).toEqual(messages);
+ });
+ });
+ });
+
/*
* Test Helper Functions
*/
@@ -168,8 +255,9 @@ describe('md-input-container animations', function() {
$compile = $injector.get('$compile');
$animateCss = $injector.get('$animateCss');
$material = $injector.get('$material');
+ $$mdInput = $injector.get('$$mdInput');
- // Grab our input animations
+ // Grab our input animations (we MUST use the injector to setup dependencies)
invalidAnimation = $injector.get('mdInputInvalidAnimation');
messagesAnimation = $injector.get('mdInputMessagesAnimation');
messageAnimation = $injector.get('mdInputMessageAnimation');
@@ -2,7 +2,7 @@
* @ngdoc module
* @name material.components.input
*/
-angular.module('material.components.input', [
+var inputModule = angular.module('material.components.input', [
'material.core'
])
.directive('mdInputContainer', mdInputContainerDirective)
@@ -18,12 +18,26 @@ angular.module('material.components.input', [
.animation('.md-input-invalid', mdInputInvalidMessagesAnimation)
.animation('.md-input-messages-animation', ngMessagesAnimation)
- .animation('.md-input-message-animation', ngMessageAnimation)
+ .animation('.md-input-message-animation', ngMessageAnimation);
+
+// If we are running inside of tests; expose some extra services so that we can test them
+if (window._mdMocksIncluded) {
+ inputModule.service('$$mdInput', function() {
+ return {
+ // special accessor to internals... useful for testing
+ messages: {
+ show : showInputMessages,
+ hide : hideInputMessages,
+ getElement : getMessagesElement
+ }
+ }
+ })
// Register a service for each animation so that we can easily inject them into unit tests
.service('mdInputInvalidAnimation', mdInputInvalidMessagesAnimation)
.service('mdInputMessagesAnimation', ngMessagesAnimation)
.service('mdInputMessageAnimation', ngMessageAnimation);
+}
/**
* @ngdoc directive
@@ -890,10 +904,10 @@ function ngMessageDirective($mdUtil) {
}
}
-var $$AnimateRunner, $animateCss, $mdUtil;
+var $$AnimateRunner, $animateCss, $mdUtil, $log;
-function mdInputInvalidMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil) {
- saveSharedServices($$AnimateRunner, $animateCss, $mdUtil);
+function mdInputInvalidMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil, $log) {
+ saveSharedServices($$AnimateRunner, $animateCss, $mdUtil, $log);
return {
addClass: function(element, className, done) {
@@ -904,8 +918,8 @@ function mdInputInvalidMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil)
};
}
-function ngMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil) {
- saveSharedServices($$AnimateRunner, $animateCss, $mdUtil);
+function ngMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil, $log) {
+ saveSharedServices($$AnimateRunner, $animateCss, $mdUtil, $log);
return {
enter: function(element, done) {
@@ -934,8 +948,8 @@ function ngMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil) {
};
}
-function ngMessageAnimation($$AnimateRunner, $animateCss, $mdUtil) {
- saveSharedServices($$AnimateRunner, $animateCss, $mdUtil);
+function ngMessageAnimation($$AnimateRunner, $animateCss, $mdUtil, $log) {
+ saveSharedServices($$AnimateRunner, $animateCss, $mdUtil, $log);
return {
enter: function(element, done) {
@@ -955,8 +969,15 @@ function ngMessageAnimation($$AnimateRunner, $animateCss, $mdUtil) {
function showInputMessages(element, done) {
var animators = [], animator;
var messages = getMessagesElement(element);
+ var children = messages.children();
+
+ if (messages.length == 0 || children.length == 0) {
+ $log.warn('mdInput messages show animation called on invalid messages element: ', element);
+ done();
+ return;
+ }
- angular.forEach(messages.children(), function(child) {
+ angular.forEach(children, function(child) {
animator = showMessage(angular.element(child));
animators.push(animator.start());
@@ -968,8 +989,15 @@ function showInputMessages(element, done) {
function hideInputMessages(element, done) {
var animators = [], animator;
var messages = getMessagesElement(element);
+ var children = messages.children();
- angular.forEach(messages.children(), function(child) {
+ if (messages.length == 0 || children.length == 0) {
+ $log.warn('mdInput messages hide animation called on invalid messages element: ', element);
+ done();
+ return;
+ }
+
+ angular.forEach(children, function(child) {
animator = hideMessage(angular.element(child));
animators.push(animator.start());
@@ -1028,6 +1056,11 @@ function getInputElement(element) {
}
function getMessagesElement(element) {
+ // If we ARE the messages element, just return ourself
+ if (element.hasClass('md-input-messages-animation')) {
+ return element;
+ }
+
// If we are a ng-message element, we need to traverse up the DOM tree
if (element.hasClass('md-input-message-animation')) {
return angular.element($mdUtil.getClosest(element, function(node) {
@@ -1039,8 +1072,9 @@ function getMessagesElement(element) {
return angular.element(element[0].querySelector('.md-input-messages-animation'));
}
-function saveSharedServices(_$$AnimateRunner_, _$animateCss_, _$mdUtil_) {
+function saveSharedServices(_$$AnimateRunner_, _$animateCss_, _$mdUtil_, _$log_) {
$$AnimateRunner = _$$AnimateRunner_;
$animateCss = _$animateCss_;
$mdUtil = _$mdUtil_;
+ $log = _$log_;
}
@@ -16,6 +16,12 @@
'use strict';
+ // Allow our code to know when they are running inside of a test so they can expose extra services
+ // that should NOT be exposed to the public but that should be tested.
+ //
+ // As an example, see input.js which exposes some animation-related methods.
+ window._mdMocksIncluded = true;
+
/**
* @ngdoc module
* @name ngMaterial-mock

0 comments on commit ce5f7c2

Please sign in to comment.