Fixed test case 'should upgrade a single component to an element by using its CSS classes' to reveal a bug in the current upgradeElement caused by dataUpgraded not being correctly updated. Added test case 'should upgrade a single component to an element' to reveal a bug in the current upgradeElement caused by unreliably checking of upgraded class.

Added `getUpgradedListOfElement_(element)` for getting a list of names of the upgraded classes.
Made `isElementUpgraded_(element, jsClass)` more reliable.
In `upgradeElementInternal(element, optJsClass)`:
* Added argument verification.
* Improved class search efficiency when jsClass is omitted.
* Made `dataUpgraded` update correctly. (Closes #1115)
This commit is contained in:
Xingchen Hong 2015-07-19 15:41:07 -05:00 committed by Addy Osmani
parent 62ff493131
commit 384315ca64
2 changed files with 44 additions and 20 deletions

View File

@ -50,6 +50,18 @@ var componentHandler = (function() {
return false;
}
/**
* Returns an array of the classNames of the upgraded classes on the element.
* @param {HTMLElement} element The element to fetch data from.
* @return {[string]}
* @private
*/
function getUpgradedListOfElement_(element) {
var dataUpgraded = element.getAttribute('data-upgraded');
// Use `['']` as default value to conform the `,name,name...` style.
return dataUpgraded === null ? [''] : dataUpgraded.split(',');
}
/**
* Returns true if the given element has already been upgraded for the given
* class.
@ -59,8 +71,8 @@ var componentHandler = (function() {
* @private
*/
function isElementUpgraded_(element, jsClass) {
var dataUpgraded = element.getAttribute('data-upgraded');
return dataUpgraded && dataUpgraded.indexOf(jsClass) !== -1;
var upgradedList = getUpgradedListOfElement_(element);
return upgradedList.indexOf(jsClass) !== -1;
}
/**
@ -99,36 +111,40 @@ var componentHandler = (function() {
* the element to.
*/
function upgradeElementInternal(element, optJsClass) {
// Only upgrade elements that have not already been upgraded.
var dataUpgraded = element.getAttribute('data-upgraded');
var registeredClasses = [];
// Verify argument type.
if (!(typeof element === 'object' && element instanceof Element)) {
throw new Error('Invalid argument provided to upgrade MDL element.');
}
var upgradedList = getUpgradedListOfElement_(element);
var classesToUpgrade = [];
// If jsClass is not provided scan the registered components to find the
// ones matching the element's CSS classList.
if (!optJsClass) {
registeredClasses = registeredComponents_.filter(function(component) {
return element.classList.contains(component.cssClass) &&
!isElementUpgraded_(element, component.className);
var classList = element.classList;
registeredComponents_.forEach(function (component) {
// Match CSS & Not to be upgraded & Not upgraded.
if (classList.contains(component.cssClass) &&
classesToUpgrade.indexOf(component) === -1 &&
!isElementUpgraded_(element, component.className)) {
classesToUpgrade.push(component);
}
});
} else if (!isElementUpgraded_(element, optJsClass)) {
registeredClasses.push(findRegisteredClass_(optJsClass));
classesToUpgrade.push(findRegisteredClass_(optJsClass));
}
// Upgrade the element for each classes.
for (var i = 0, l = registeredClasses.length; i < l; i++) {
var registeredClass = registeredClasses[i];
for (var i = 0, n = classesToUpgrade.length, registeredClass; i < n; i++) {
registeredClass = classesToUpgrade[i];
if (registeredClass) {
// Mark element as upgraded.
if (dataUpgraded === null) {
dataUpgraded = '';
}
element.setAttribute('data-upgraded', dataUpgraded + ',' +
registeredClass.className);
upgradedList.push(registeredClass.className);
element.setAttribute('data-upgraded', upgradedList.join(','));
var instance = new registeredClass.classConstructor(element);
instance[componentConfigProperty_] = registeredClass;
createdComponents_.push(instance);
// Call any callbacks the user has registered with this component type.
for (var j = 0, len = registeredClass.callbacks.length; j < len; j++) {
for (var j = 0, m = registeredClass.callbacks.length; j < m; j++) {
registeredClass.callbacks[j](element);
}

View File

@ -97,9 +97,9 @@ describe('componentHandler', function() {
it('should upgrade a single component to an element by using its CSS classes', function() {
var el = document.createElement('button');
el.className = 'mdl-button mdl-js-button';
el.className = 'mdl-js-button mdl-js-ripple-effect';
componentHandler.upgradeElement(el);
expect($(el)).to.have.data('upgraded', ',MaterialButton');
expect($(el)).to.have.data('upgraded', ',MaterialButton,MaterialRipple');
});
it('should upgrade the entire DOM available', function() {
@ -116,6 +116,14 @@ describe('componentHandler', function() {
document.body.removeChild(buttonTwo);
});
it('should upgrade a single component to an element', function() {
var el = document.createElement('button');
el.setAttribute('data-upgraded', ',MaterialButtonPostfix');
el.className = 'mdl-js-button';
componentHandler.upgradeElement(el);
expect($(el)).to.have.data('upgraded', ',MaterialButtonPostfix,MaterialButton');
});
it('should upgrade all elements and their children within an HTMLCollection', function() {
var container = createNestedElementsForComponentHandlerTest();
var buttons = document.querySelectorAll('.mdl-js-button');