Skip to content

Commit 56ad59d

Browse files
mwczgithub-actions[bot]castastrophe
authored
fix: on mutation, apply cascade to new elements only (#1440)
* fix: on mutation, cascade to new elements only When a cascade is triggered by a mutation observer, copy attributes down to the newly created elements only. Previously, I had code implemented that tried to do this but it wasn't working. It was finding which cascade selectors matched the new elements, but then copied the attributes to _all_ elements that matched those selectors. * update changelog * move IE11 observer reattach to same branch as detach * update changelog * Update CHANGELOG-1.x.md * fix incorrect iteration over cascading attrNames * Update CHANGELOG-1.x.md Co-authored-by: [ Cassondra ] <[email protected]> * add jsdoc params to _cascadeAttribute * Update elements/pfelement/src/pfelement.js Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: [ Cassondra ] <[email protected]>
1 parent 24e5b8b commit 56ad59d

File tree

2 files changed

+54
-35
lines changed

2 files changed

+54
-35
lines changed

CHANGELOG-1.x.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# 1.10.1 (2021)
22

3-
- [](https://github.com/patternfly/patternfly-elements/commit/) fix: pfelement - only resetContext on a nested child element during contextUpdate if resetContext is available on the child
4-
- [fdc60ab](https://github.com/patternfly/patternfly-elements/commit/) fix: pfe-tabs issue with active border color on horizontal/wind (#1585)
3+
- [b717636](https://github.com/patternfly/patternfly-elements/commit/b717636782ebce33b4549ba9f68ffff09c036889) fix: pfelement - only resetContext on a nested child element during contextUpdate if resetContext is available on the child
4+
- [7f9c30e](https://github.com/patternfly/patternfly-elements/commit/7f9c30e0a312e538b1787a91824c6e84d1aa261a) fix: pfe-tabs issue with active border color on horizontal/wind (#1585)
5+
- [](https://github.com/patternfly/patternfly-elements/commit/) fix: on mutation, apply cascade to new elements only
56

67
# 1.10.0 (2021-07-08)
78

elements/pfelement/src/pfelement.js

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ class PFElement extends HTMLElement {
467467
// If the property/attribute pair has a cascade target, copy the attribute to the matching elements
468468
// Note: this handles the cascading of new/updated attributes
469469
if (propDef.cascade) {
470-
this._copyAttribute(attr, this._pfeClass._convertSelectorsToArray(propDef.cascade));
470+
this._cascadeAttribute(attr, this._pfeClass._convertSelectorsToArray(propDef.cascade));
471471
}
472472
}
473473
}
@@ -561,33 +561,34 @@ class PFElement extends HTMLElement {
561561

562562
let selectors = Object.keys(cascade);
563563
// Find out if anything in the nodeList matches any of the observed selectors for cacading properties
564-
if (nodeList) {
565-
selectors = [];
566-
[...nodeList].forEach((nodeItem) => {
567-
Object.keys(cascade).map((selector) => {
568-
// if this node has a match function (i.e., it's an HTMLElement, not
569-
// a text node), see if it matches the selector, otherwise drop it (like it's hot).
570-
if (nodeItem.matches && nodeItem.matches(selector)) {
571-
selectors.push(selector);
572-
}
573-
});
574-
});
575-
}
576-
577-
// If a match was found, cascade each attribute to the element
578564
if (selectors) {
579-
const components = selectors
580-
.filter((item) => item.slice(0, prefix.length + 1) === `${prefix}-`)
581-
.map((name) => customElements.whenDefined(name));
582-
583-
if (components)
584-
Promise.all(components).then(() => {
585-
this._copyAttributes(selectors, cascade);
565+
if (nodeList) {
566+
[...nodeList].forEach(nodeItem => {
567+
selectors.forEach(selector => {
568+
// if this node has a match function (i.e., it's an HTMLElement, not
569+
// a text node), see if it matches the selector, otherwise drop it (like it's hot).
570+
if (nodeItem.matches && nodeItem.matches(selector)) {
571+
let attrNames = cascade[selector];
572+
// each selector can match multiple properties/attributes, so
573+
// copy each of them
574+
attrNames.forEach(attrName => this._copyAttribute(attrName, nodeItem));
575+
}
576+
});
586577
});
587-
else this._copyAttributes(selectors, cascade);
578+
} else {
579+
// If a match was found, cascade each attribute to the element
580+
const components = selectors
581+
.filter(item => item.slice(0, prefix.length + 1) === `${prefix}-`)
582+
.map(name => customElements.whenDefined(name));
583+
584+
if (components)
585+
Promise.all(components).then(() => {
586+
this._cascadeAttributes(selectors, cascade);
587+
});
588+
else this._cascadeAttributes(selectors, cascade);
589+
}
588590
}
589591

590-
// @TODO This is here for IE11 processing; can move this after deprecation
591592
if (this._rendered && this._cascadeObserver)
592593
this._cascadeObserver.observe(this, {
593594
attributes: true,
@@ -662,7 +663,8 @@ class PFElement extends HTMLElement {
662663
for (let mutation of mutationsList) {
663664
// If a new node is added, attempt to cascade attributes to it
664665
if (mutation.type === "childList" && mutation.addedNodes.length) {
665-
this.cascadeProperties(mutation.addedNodes);
666+
const nonTextNodes = [...mutation.addedNodes].filter(n => n.nodeType !== HTMLElement.TEXT_NODE);
667+
this.cascadeProperties(nonTextNodes);
666668
}
667669
}
668670
}
@@ -954,23 +956,39 @@ class PFElement extends HTMLElement {
954956
return propName;
955957
}
956958

957-
_copyAttributes(selectors, set) {
958-
selectors.forEach((selector) => {
959-
set[selector].forEach((attr) => {
960-
this._copyAttribute(attr, selector);
959+
_cascadeAttributes(selectors, set) {
960+
selectors.forEach(selector => {
961+
set[selector].forEach(attr => {
962+
this._cascadeAttribute(attr, selector);
961963
});
962964
});
963965
}
964966

965-
_copyAttribute(name, to) {
967+
/**
968+
* Trigger a cascade of the named attribute to any child elements that match
969+
* the `to` selector. The selector can match elements in the light DOM and
970+
* shadow DOM.
971+
* @param {String} name The name of the attribute to cascade (not necessarily the same as the property name).
972+
* @param {String} to A CSS selector that matches the elements that should received the cascaded attribute. The selector will be applied within `this` element's light and shadow DOM trees.
973+
*/
974+
_cascadeAttribute(name, to) {
966975
const recipients = [...this.querySelectorAll(to), ...this.shadowRoot.querySelectorAll(to)];
967-
const value = this.getAttribute(name);
968-
const fname = value == null ? "removeAttribute" : "setAttribute";
976+
969977
for (const node of recipients) {
970-
node[fname](name, value);
978+
this._copyAttribute(name, node);
971979
}
972980
}
973981

982+
/**
983+
* Copy the named attribute to a target element.
984+
*/
985+
_copyAttribute(name, el) {
986+
this.log(`copying ${name} to ${el}`);
987+
const value = this.getAttribute(name);
988+
const fname = value == null ? "removeAttribute" : "setAttribute";
989+
el[fname](name, value);
990+
}
991+
974992
static _convertSelectorsToArray(selectors) {
975993
if (selectors) {
976994
if (typeof selectors === "string") return selectors.split(",");

0 commit comments

Comments
 (0)