Skip to content

Commit 73f9169

Browse files
kylebuch8mwcz
authored andcommitted
adding a mutationobserver to pfe-accordion (#401)
Adding a mutation observer so the component will continue working properly if new headers and panels are added. Fixes #332
1 parent 1014f22 commit 73f9169

File tree

5 files changed

+149
-26
lines changed

5 files changed

+149
-26
lines changed

elements/pfe-accordion/demo/index.html

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ <h3>Why do wizards need money if they could just create it?</h3>
7878
<h3>Why doesn't Harry have a portrait of his parents?</h3>
7979
</pfe-accordion-header>
8080
<pfe-accordion-panel>
81-
<p><a href="#">The characters in the portraits</a> are not actually ghosts. They mainly are there just to repeat common phrases or serve as a general a href="foobarbaz.com">representation of the individual</a> they depict. A portrait of his parents would not be of much help to Harry.</p>
81+
<p><a href="#">The characters in the portraits</a> are not actually ghosts. They mainly are there just to repeat common phrases or serve as a general <a href="foobarbaz.com">representation of the individual</a> they depict. A portrait of his parents would not be of much help to Harry.</p>
8282
</pfe-accordion-panel>
8383
<pfe-accordion-header>
8484
<h3>Why is Harry considered a half-blood if both of his parents could use magic?</h3>
@@ -100,6 +100,16 @@ <h3>Where do the main characters work as adults?</h3>
100100
<p><a href="https://www.pottermore.com/collection/characters" target="blank">Read more about the characters</a></p>
101101
</pfe-accordion-panel>
102102
</pfe-accordion>
103+
<pfe-accordion>
104+
<pfe-accordion-header>
105+
Something
106+
</pfe-accordion-header>
107+
<pfe-accordion-panel>
108+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
109+
</pfe-accordion-panel>
110+
</pfe-accordion>
111+
112+
<button id="addTabAndPanelBtn">Add Tab &amp; Panel</button>
103113
</section>
104114

105115
<section class="dark-background" >
@@ -208,5 +218,23 @@ <h3>Where do the main characters work as adults?</h3>
208218
</pfe-accordion>
209219
</section>
210220

221+
<script>
222+
var btn = document.querySelector("#addTabAndPanelBtn");
223+
var pfeAccordion = document.querySelector("pfe-accordion");
224+
225+
btn.addEventListener("click", function() {
226+
var fragment = document.createDocumentFragment();
227+
228+
var header = document.createElement("pfe-accordion-header");
229+
header.innerHTML = "<h3>New Tab</h3>";
230+
231+
var panel = document.createElement("pfe-accordion-panel");
232+
panel.innerHTML = "<p>New panel text</p>";
233+
234+
fragment.appendChild(header);
235+
fragment.appendChild(panel);
236+
pfeAccordion.appendChild(fragment);
237+
});
238+
</script>
211239
</body>
212240
</html>

elements/pfe-accordion/package-lock.json

Lines changed: 7 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

elements/pfe-accordion/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
],
1616
"repository": {
1717
"type": "git",
18-
"url" : "github:patternfly/patternfly-elements",
18+
"url": "github:patternfly/patternfly-elements",
1919
"directory": "elements/pfe-accordion"
2020
},
2121
"main": "pfe-accordion.js",

elements/pfe-accordion/src/pfe-accordion.js

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ class PfeAccordion extends PFElement {
8585

8686
constructor() {
8787
super(PfeAccordion, { type: PfeAccordion.PfeType });
88+
89+
this._linkPanels = this._linkPanels.bind(this);
90+
this._observer = new MutationObserver(this._linkPanels);
8891
}
8992

9093
connectedCallback() {
@@ -99,12 +102,19 @@ class PfeAccordion extends PFElement {
99102
Promise.all([
100103
customElements.whenDefined(PfeAccordionHeader.tag),
101104
customElements.whenDefined(PfeAccordionPanel.tag)
102-
]).then(this._linkPanels());
105+
]).then(() => {
106+
if (this.children.length) {
107+
this._linkPanels();
108+
}
109+
110+
this._observer.observe(this, { childList: true });
111+
});
103112
}
104113

105114
disconnectedCallback() {
106115
this.removeEventListener(`${PfeAccordion.tag}:change`, this._changeHandler);
107116
this.removeEventListener("keydown", this._keydownHandler);
117+
this._observer.disconnect();
108118
}
109119

110120
attributeChangedCallback(attr, oldVal, newVal) {
@@ -179,6 +189,10 @@ class PfeAccordion extends PFElement {
179189
headers.forEach(header => {
180190
const panel = this._panelForHeader(header);
181191

192+
if (!panel) {
193+
return;
194+
}
195+
182196
header.setAttribute("aria-controls", panel.pfeId);
183197
panel.setAttribute("aria-labelledby", header.pfeId);
184198
});
@@ -208,6 +222,11 @@ class PfeAccordion extends PFElement {
208222
}
209223

210224
_expandPanel(panel) {
225+
if (!panel) {
226+
console.error(`${PfeAccordion.tag}: Trying to expand a panel that doesn't exist`);
227+
return;
228+
}
229+
211230
if (panel.expanded) {
212231
return;
213232
}
@@ -223,6 +242,11 @@ class PfeAccordion extends PFElement {
223242
}
224243

225244
_collapsePanel(panel) {
245+
if (!panel) {
246+
console.error(`${PfeAccordion.tag}: Trying to collapse a panel that doesn't exist`);
247+
return;
248+
}
249+
226250
if (!panel.expanded) {
227251
return;
228252
}
@@ -298,6 +322,10 @@ class PfeAccordion extends PFElement {
298322
_panelForHeader(header) {
299323
const next = header.nextElementSibling;
300324

325+
if (!next) {
326+
return;
327+
}
328+
301329
if (next.tagName.toLowerCase() !== PfeAccordionPanel.tag) {
302330
console.error(
303331
`${PfeAccordion.tag}: Sibling element to a header needs to be a panel`
@@ -368,12 +396,49 @@ class PfeAccordionHeader extends PFElement {
368396

369397
constructor() {
370398
super(PfeAccordionHeader);
399+
400+
this._init = this._init.bind(this);
371401
this._clickHandler = this._clickHandler.bind(this);
402+
this._observer = new MutationObserver(this._init);
372403
}
373404

374405
connectedCallback() {
375406
super.connectedCallback();
376407

408+
if (this.children.length || this.textContent.trim().length) {
409+
this._init();
410+
}
411+
412+
this.addEventListener("click", this._clickHandler);
413+
this._observer.observe(this, { childList: true });
414+
}
415+
416+
disconnectedCallback() {
417+
this.removeEventListener("click", this._clickHandler);
418+
this._observer.disconnect();
419+
}
420+
421+
get expanded() {
422+
return this.hasAttribute("aria-expanded");
423+
}
424+
425+
set expanded(val) {
426+
val = Boolean(val);
427+
428+
if (val) {
429+
this.setAttribute("aria-expanded", true);
430+
this.button.setAttribute("aria-expanded", true);
431+
} else {
432+
this.removeAttribute("aria-expanded");
433+
this.button.setAttribute("aria-expanded", false);
434+
}
435+
}
436+
437+
_init() {
438+
if (window.ShadyCSS) {
439+
this._observer.disconnect();
440+
}
441+
377442
if (!this.hasAttribute("role")) {
378443
this.setAttribute("role", "header");
379444
}
@@ -416,26 +481,8 @@ class PfeAccordionHeader extends PFElement {
416481
);
417482
}
418483

419-
this.addEventListener("click", this._clickHandler);
420-
}
421-
422-
disconnectedCallback() {
423-
this.removeEventListener("click", this._clickHandler);
424-
}
425-
426-
get expanded() {
427-
return this.hasAttribute("aria-expanded");
428-
}
429-
430-
set expanded(val) {
431-
val = Boolean(val);
432-
433-
if (val) {
434-
this.setAttribute("aria-expanded", true);
435-
this.button.setAttribute("aria-expanded", true);
436-
} else {
437-
this.removeAttribute("aria-expanded");
438-
this.button.setAttribute("aria-expanded", false);
484+
if (window.ShadyCSS) {
485+
this._observer.observe(this, { childList: true });
439486
}
440487
}
441488

elements/pfe-accordion/test/pfe-accordion_test.html

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ <h2>Where do the main characters work as adults?</h2>
4343
</pfe-accordion-panel>
4444
</pfe-accordion>
4545

46+
<pfe-accordion id="dynamic">
47+
<pfe-accordion-header>
48+
<h2>Header</h2>
49+
</pfe-accordion-header>
50+
<pfe-accordion-panel>
51+
Panel
52+
</pfe-accordion-panel>
53+
</pfe-accordion>
54+
4655
<script>
4756
suite('<pfe-accordion>', () => {
4857
test('it should upgrade pfe-accordion', () => {
@@ -237,6 +246,41 @@ <h2>Where do the main characters work as adults?</h2>
237246

238247
sinon.assert.calledWith(spy, 'pfe-accordion-header: The first child in the light DOM must be a Header level tag (h1, h2, h3, h4, h5, or h6)');
239248
});
249+
250+
test('it should properly initialize any dynamically added headers and panels', done => {
251+
const pfeAccordion = document.querySelector('#dynamic');
252+
const documentFragment = document.createDocumentFragment();
253+
254+
const newHeader = document.createElement("pfe-accordion-header");
255+
newHeader.id = "newHeader";
256+
newHeader.innerHTML = `<h2>New Header</h2>`;
257+
258+
const newPanel = document.createElement("pfe-accordion-panel");
259+
newPanel.id = "newPanel";
260+
newPanel.innerHTML = `New Panel`;
261+
262+
documentFragment.appendChild(newHeader);
263+
documentFragment.appendChild(newPanel);
264+
265+
pfeAccordion.appendChild(documentFragment);
266+
267+
flush(() => {
268+
const newHeaderElement = document.querySelector("#newHeader");
269+
const newPanelElement = document.querySelector("#newPanel");
270+
271+
assert.equal(newHeaderElement.getAttribute("role"), "header");
272+
assert.isTrue(newHeaderElement.hasAttribute("pfe-id"));
273+
assert.isTrue(newHeaderElement.hasAttribute("aria-controls"));
274+
assert.equal(newHeaderElement.getAttribute("aria-controls"), newPanelElement.getAttribute("pfe-id"));
275+
276+
assert.equal(newPanelElement.getAttribute("role"), "region");
277+
assert.isTrue(newPanelElement.hasAttribute("pfe-id"));
278+
assert.isTrue(newPanelElement.hasAttribute("aria-labelledby"));
279+
assert.equal(newPanelElement.getAttribute("aria-labelledby"), newHeaderElement.getAttribute("pfe-id"));
280+
281+
done();
282+
});
283+
});
240284
});
241285
</script>
242286
</body>

0 commit comments

Comments
 (0)