Repository: ProjectEvergreen/wcc
Pull request #63 (2022-06-11 00:40:17)

fix `HTMLTemplateElement` implementation to reflect standards behavior
<!--
## Submitting a Pull Request
We love contributions and appreciate any help you can offer!
-->

## Related Issue
resolves #51 

## Summary of Changes
1. Update _dom-shim.js_ to correctly have the `<template>` tag logic inside `HTMLTemplateElement`
1. Updated specs slightly to reflect correct `<template>` implementation
1. Update documentation site off of incorrect `<template>` implementation

## TODO
1. [x] ~~Make sure `this.mode` works from `attachShadow`~~ - https://github.com/ProjectEvergreen/wcc/issues/65
1. [x] Downstream sanity testing
    - WC@TheEdge
    - Greenwood
    
## Questions
1. [x] Cross reference with `getInnerHTML` to make sure things are / will be still compatible -#16
1. [x] do we need a `get innerHTML` for `HTMLTemplateElement`? - tracked in #66 
1. [x] Create a discussion for better way to use `<templates>` but with variables / template literal placeholders. Or just create  render functions?....  But then you lose the free `<template>` tag from using `document.createElement('template')`.  Maybe create some experimental `set` function for `template.innerHTML = '....'` that can take args and destructure them? - https://github.com/ProjectEvergreen/wcc/discussions/66

thescientist13: So one thing I'm not super clear on is how instances of [`<template>` tags (`HTMLTemplateElement`)]() and [`ShadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/) work together, specifically around the [`shadowroot="open"` attribute](https://web.dev/declarative-shadow-dom/#building) and [`mode`](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/mode).

Specifically, as per this PR, when creating a template, it returns its contents inside `<template>` tags, eg.
```html
<wcc-footer>
  <template shadowroot="open">
    <footer class="footer">
      <h4>My Blog &copy; 2022</h4>
    </footer>
  </template>
</wcc-footer>
```

And when instantiating a Shadow Root in a custom element, we can set the `mode` that way, e.g.
```js
class Footer extends HTMLElement {
  connectedCallback() {
    if (!this.shadowRoot) {
      this.attachShadow({ mode: 'open' });
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }
  }
}
```

So how does a template get access to the mode, which we would want so as to properly set the `shadowroot` attribute?  Presumably some users may want the option of open / closed, and this may also impact how we implement #16.  

They are two different things with different class hierarchies from what I understand, so not sure what option to pursue here?  I can think of a couple options at least just to get the ball rolling
1. User sets the attribute manually on a `<template>`
    ```js
    const template = document.createElement('template');

    template.innerHTML = `    
      <footer class="footer">
        <h4>My Blog &copy; ${new Date().getFullYear()}</h4>
      </footer>
    `;

    template.setAttribute('shadowroot', 'open');
    ```
1. Have the compiler check for `mode` when serializing and update it that way
    ```js
    async function renderComponentRoots(tree) {
      for (const node of tree.childNodes) {
        if (node.tagName && node.tagName.indexOf('-') > 0) {
          ...
          
          const elementHtml = elementInstance.shadowRoot
            ? elementInstance.getInnerHTML({ includeShadowRoots: true })
            : elementInstance.innerHTML;
          const elementTree = parseFragment(elementHtml);

          // if we have a shadowRoot, set the `<template>` shadowroot attribute according to shadowRoot.mode
          if(elementInstance.shadowRoot) {
            const mode = elementInstance.shadowRoot.mode;
            //  do something with the tree and set the attribute on the `<template>` accordingly
          }
    
          ...
        }
    
        ...
      }
    
      return tree;
    }
    ```
1. Just leave it hardcoded to `shadowroot="open"` (for now) 🙃 


----

I think either way, the main thing to accomplish with this PR is to get `<template>` coming from the right place and set the standards in place compared to how it was implemented before.  I can make another issue to work out how to link `mode` and `shadowroot` attribute, if they are even in fact related.  I'm thinking option #2 seems pretty feasible though.


Patch:
diff --git a/docs/components/footer.js b/docs/components/footer.js
index 4a36ad5..63aa1a6 100644
--- a/docs/components/footer.js
+++ b/docs/components/footer.js
@@ -1,36 +1,36 @@
-const template = document.createElement('template');
-
-template.innerHTML = `
-  <style>
-    footer {
-      background-color: var(--accent);
-      min-height: 30px;
-      padding: 10px 0;
-      grid-column: 1 / -1;
-      text-align: center;
-    }
+class Footer extends HTMLElement {
+  connectedCallback() {
+    this.innerHTML = this.render();
+  }
 
-    footer p {
-      margin: 0 auto;
-      font-weight: bold;
-    }
+  render() {
+    return `
+      <style>
+        footer {
+          background-color: var(--accent);
+          min-height: 30px;
+          padding: 10px 0;
+          grid-column: 1 / -1;
+          text-align: center;
+        }
 
-    footer a, footer a:visited {
-      color: #efefef;
-      text-decoration: none;
-    }
-  </style>
+        footer p {
+          margin: 0 auto;
+          font-weight: bold;
+        }
 
-  <footer>
-    <p>
-      <a href="https://projectevergreen.github.io">WCC &#9672 Project Evergreen</a>
-    </p>
-  </footer>
-`;
+        footer a, footer a:visited {
+          color: #efefef;
+          text-decoration: none;
+        }
+      </style>
 
-class Footer extends HTMLElement {
-  connectedCallback() {
-    this.innerHTML = template.content.textContent;
+      <footer>
+        <p>
+          <a href="https://projectevergreen.github.io">WCC &#9672 Project Evergreen</a>
+        </p>
+      </footer>
+    `;
   }
 }
 
diff --git a/docs/components/header.js b/docs/components/header.js
index 4c54f45..0b5495e 100644
--- a/docs/components/header.js
+++ b/docs/components/header.js
@@ -1,67 +1,67 @@
 import './navigation.js';
 
-const template = document.createElement('template');
-
-template.innerHTML = `
-  <style>
-    header {
-      background-color: var(--accent);
-      grid-column: 1 / -1;
-      min-height: 150px;
-    }
+class Header extends HTMLElement {
+  connectedCallback() {
+    this.innerHTML = this.render();
+  }
 
-    header .social {
-      text-align: right;
-      padding: 10px 10px 0 0;
-    }
+  render() {
+    return `
+      <style>
+        header {
+          background-color: var(--accent);
+          grid-column: 1 / -1;
+          min-height: 150px;
+        }
 
-    header .social img{
-      margin-top: 1%;
-    }
+        header .social {
+          text-align: right;
+          padding: 10px 10px 0 0;
+        }
 
-    header .logo {
-      width: 15%;
-      filter: drop-shadow(0 0 0.75rem white);
-    }
+        header .social img{
+          margin-top: 1%;
+        }
 
-    header img.github-badge {
-      float: right;
-      display: inline-block;
-      padding: 10px;
-      align-items: top;
-    }
+        header .logo {
+          width: 15%;
+          filter: drop-shadow(0 0 0.75rem white);
+        }
 
-    header div.container {
-      max-width: 1200px;
-      margin: auto;
-    }
-  </style>
+        header img.github-badge {
+          float: right;
+          display: inline-block;
+          padding: 10px;
+          align-items: top;
+        }
 
-  <header>
-    <div class="container">
-      <div>
-        <a href="/">
-          <img src="/assets/wcc-logo.png" alt="WCC logo" class="logo"/>
-        </a>
+        header div.container {
+          max-width: 1200px;
+          margin: auto;
+        }
+      </style>
 
-        <a href="https://github.com/ProjectEvergreen/wcc" class="social">
-          <img
-            src="https://img.shields.io/github/stars/ProjectEvergreen/wcc.svg?style=social&logo=github&label=github"
-            alt="WCC GitHub badge"
-            width="135px"
-            class="github-badge"
-          />
-        </a>
-      </div>
+      <header>
+        <div class="container">
+          <div>
+            <a href="/">
+              <img src="/assets/wcc-logo.png" alt="WCC logo" class="logo"/>
+            </a>
 
-      <wcc-navigation></wcc-navigation>
-    </div>
-  </header>
-`;
+            <a href="https://github.com/ProjectEvergreen/wcc" class="social">
+              <img
+                src="https://img.shields.io/github/stars/ProjectEvergreen/wcc.svg?style=social&logo=github&label=github"
+                alt="WCC GitHub badge"
+                width="135px"
+                class="github-badge"
+              />
+            </a>
+          </div>
 
-class Header extends HTMLElement {
-  connectedCallback() {
-    this.innerHTML = template.content.textContent;
+          <wcc-navigation></wcc-navigation>
+        </div>
+      </header>
+    `;
   }
 }
 
diff --git a/docs/components/navigation.js b/docs/components/navigation.js
index 731bc3d..a3a5c11 100644
--- a/docs/components/navigation.js
+++ b/docs/components/navigation.js
@@ -1,41 +1,40 @@
-// intentionally nested in the assets/ directory to test wcc nested dependency resolution logic
-const template = document.createElement('template');
-
-template.innerHTML = `
-  <style>
-    nav ul {
-      list-style-type: none;
-      overflow: auto;
-      grid-column: 1 / -1;
-      width: 90%;
-    }
+class Navigation extends HTMLElement {
+  connectedCallback() {
+    this.innerHTML = this.render();
+  }
 
-    nav ul li {
-      float: left;
-      width: 33.3%;
-      text-align: center;
-    }
+  render() {
+    return `
+      <style>
+        nav ul {
+          list-style-type: none;
+          overflow: auto;
+          grid-column: 1 / -1;
+          width: 90%;
+        }
 
-    nav ul li a, nav ul li a:visited {
-      display: inline-block;
-      color: #efefef;
-      min-height: 48px;
-      font-size: 2.5rem;
-    }
-  </style>
+        nav ul li {
+          float: left;
+          width: 33.3%;
+          text-align: center;
+        }
 
-  <nav>
-    <ul>
-      <li><a href="/">Home</a></li>
-      <li><a href="/docs">Docs</a></li>
-      <li><a href="/examples">Examples</a></li>
-    </ul>
-  </nav>
-`;
+        nav ul li a, nav ul li a:visited {
+          display: inline-block;
+          color: #efefef;
+          min-height: 48px;
+          font-size: 2.5rem;
+        }
+      </style>
 
-class Navigation extends HTMLElement {
-  connectedCallback() {
-    this.innerHTML = template.content.textContent;
+      <nav>
+        <ul>
+          <li><a href="/">Home</a></li>
+          <li><a href="/docs">Docs</a></li>
+          <li><a href="/examples">Examples</a></li>
+        </ul>
+      </nav>
+    `
   }
 }
 
diff --git a/docs/layout.js b/docs/layout.js
index 08a6740..7658720 100644
--- a/docs/layout.js
+++ b/docs/layout.js
@@ -1,43 +1,43 @@
 import './components/footer.js';
 import './components/header.js';
 
-const template = document.createElement('template');
-
-template.innerHTML = `
-  <style>
-    :root {
-      --accent: #367588;
-    }
-
-    body {
-      display: flex;
-      flex-direction: column;
-    }
-
-    main {
-      max-width: 1200px;
-      margin: 20px auto;
-      width: 100%;
-      padding: 0 1rem;
-    }
-
-    a:visited {
-      color: var(--accent);
-    }
-  </style>
-
-  <wcc-header></wcc-header>
-
-  <main>
-    <slot name="content"></slot>
-  </main>
-
-  <wcc-footer></wcc-footer>
-`;
-
 class Layout extends HTMLElement {
   connectedCallback() {
-    this.innerHTML = template.content.textContent;
+    this.innerHTML = this.render();
+  }
+
+  render() {
+    return `
+      <style>
+        :root {
+          --accent: #367588;
+        }
+
+        body {
+          display: flex;
+          flex-direction: column;
+        }
+
+        main {
+          max-width: 1200px;
+          margin: 20px auto;
+          width: 100%;
+          padding: 0 1rem;
+        }
+
+        a:visited {
+          color: var(--accent);
+        }
+      </style>
+
+      <wcc-header></wcc-header>
+
+      <main>
+        <slot name="content"></slot>
+      </main>
+
+      <wcc-footer></wcc-footer>
+    `;
   }
 }
 
diff --git a/src/dom-shim.js b/src/dom-shim.js
index a41c343..71fcee0 100644
--- a/src/dom-shim.js
+++ b/src/dom-shim.js
@@ -3,6 +3,7 @@ class EventTarget { }
 
 // https://developer.mozilla.org/en-US/docs/Web/API/Node
 // EventTarget <- Node
+// TODO should be an interface?
 class Node extends EventTarget {
   constructor() {
     super();
@@ -15,7 +16,7 @@ class Node extends EventTarget {
   }
 
   appendChild(node) {
-    this.innerHTML = this.innerHTML ? this.innerHTML += node.textContent : node.textContent;
+    this.innerHTML = this.innerHTML ? this.innerHTML += node.innerHTML : node.innerHTML;
   }
 }
 
@@ -63,14 +64,9 @@ class HTMLElement extends Element {
   }
 
   // https://github.com/mfreed7/declarative-shadow-dom/blob/master/README.md#serialization
+  // eslint-disable-next-line
   getInnerHTML(options = {}) {
-    return options.includeShadowRoots
-      ? `
-        <template shadowroot="${this.shadowRoot.mode}">
-          ${this.shadowRoot.innerHTML}
-        </template>
-      `
-      : this.shadowRoot.innerHTML;
+    return this.shadowRoot.innerHTML;
   }
 }
 
@@ -101,11 +97,20 @@ class HTMLTemplateElement extends HTMLElement {
     super();
     // console.debug('HTMLTemplateElement constructor');
 
-    this.content = new DocumentFragment(this.innerHTML);
+    this.content = new DocumentFragment();
   }
 
+  // TODO open vs closed shadow root
   set innerHTML(html) {
-    this.content.textContent = html;
+    this.content.innerHTML = `
+      <template shadowroot="open">
+        ${html}
+      </template>
+    `;
+  }
+
+  get innerHTML() {
+    return this.content && this.content.innerHTML ? this.content.innerHTML : undefined;
   }
 }
 


Test patch:
diff --git a/test/cases/attributes/attributes.spec.js b/test/cases/attributes/attributes.spec.js
index fad814f..f35c070 100644
--- a/test/cases/attributes/attributes.spec.js
+++ b/test/cases/attributes/attributes.spec.js
@@ -21,24 +21,22 @@ const expect = chai.expect;
 describe('Run WCC For ', function() {
   const LABEL = 'Custom Element w/ Attributes and Shadow DOM';
   let dom;
-  let pageContentsDom;
 
   before(async function() {
     const { html } = await renderToString(new URL('./src/index.js', import.meta.url));
 
     dom = new JSDOM(html);
-    pageContentsDom = new JSDOM(dom.window.document.querySelectorAll('template[shadowroot="open"]')[0].innerHTML);
   });
 
   describe(LABEL, function() {
-    it('should have one top level <template> with an open shadowroot', function() {
-      expect(dom.window.document.querySelectorAll('template[shadowroot="open"]').length).to.equal(1);
-      expect(dom.window.document.querySelectorAll('template').length).to.equal(1);
+    it('should have one top level <wcc-counter> custom element with a <template> with an open shadowroot', function() {
+      expect(dom.window.document.querySelectorAll('wcc-counter template[shadowroot="open"]').length).to.equal(1);
+      expect(dom.window.document.querySelectorAll('wcc-counter  template').length).to.equal(1);
     });
 
     describe('static page content', function() {
       it('should have the expected static content for the page', function() {
-        expect(pageContentsDom.window.document.querySelector('h1').textContent).to.equal('Counter');
+        expect(dom.window.document.querySelector('h1').textContent).to.equal('Counter');
       });
     });
 
@@ -46,7 +44,7 @@ describe('Run WCC For ', function() {
       let counterContentsDom;
 
       before(function() {
-        counterContentsDom = new JSDOM(pageContentsDom.window.document.querySelectorAll('wcc-counter template[shadowroot="open"]')[0].innerHTML);
+        counterContentsDom = new JSDOM(dom.window.document.querySelectorAll('wcc-counter template[shadowroot="open"]')[0].innerHTML);
       });
 
       it('should have two <button> tags within the <wcc-counter> shadowroot', function() {
@@ -55,7 +53,7 @@ describe('Run WCC For ', function() {
 
       it('should have a <span> with the value of the attribute as its text content', function() {
         const count = counterContentsDom.window.document.querySelector('span#count').textContent;
-  
+
         expect(count).to.equal('5');
       });
     });
diff --git a/test/cases/attributes/src/components/counter.js b/test/cases/attributes/src/components/counter.js
index 61875f7..cf113ee 100644
--- a/test/cases/attributes/src/components/counter.js
+++ b/test/cases/attributes/src/components/counter.js
@@ -51,11 +51,13 @@ class Counter extends HTMLElement {
 
   render() {
     return `
-      <div>
-        <button id="inc">Increment</button>
-        <span>Current Count: <span id="count">${this.count}</span></span>
-        <button id="dec">Decrement</button>
-      </div>
+      <template shadowroot="open">
+        <div>
+          <button id="inc">Increment</button>
+          <span>Current Count: <span id="count">${this.count}</span></span>
+          <button id="dec">Decrement</button>
+        </div>
+      </template>
     `;
   }
 }
diff --git a/test/cases/attributes/src/index.js b/test/cases/attributes/src/index.js
index 7f758fd..9984027 100644
--- a/test/cases/attributes/src/index.js
+++ b/test/cases/attributes/src/index.js
@@ -1,25 +1,19 @@
 import './components/counter.js';
 
-export default class HomePage extends HTMLElement {
-  constructor() {
-    super();
+const template = document.createElement('template');
 
-    if (this.shadowRoot) {
-      // console.debug('HomePage => shadowRoot detected!');
-    } else {
-      this.attachShadow({ mode: 'open' });
-    }
-  }
+template.innerHTML = `
+  <h1>Counter</h1>
 
-  connectedCallback() {
-    this.shadowRoot.innerHTML = this.getTemplate();
-  }
+  <wcc-counter count="5"></wcc-counter>
+`;
 
-  getTemplate() {
-    return `
-      <h1>Counter</h1>
+export default class HomePage extends HTMLElement {
+  connectedCallback() {
+    this.innerHTML = `
+    <h1>Counter</h1>
 
-      <wcc-counter count="5"></wcc-counter>
-    `;
+    <wcc-counter count="5"></wcc-counter>
+  `;
   }
 }
\ No newline at end of file
diff --git a/test/cases/children-and-slots/children-and-slots.spec.js b/test/cases/children-and-slots/children-and-slots.spec.js
index 6c22562..a3dcd97 100644
--- a/test/cases/children-and-slots/children-and-slots.spec.js
+++ b/test/cases/children-and-slots/children-and-slots.spec.js
@@ -22,27 +22,24 @@ const expect = chai.expect;
 describe('Run WCC For ', function() {
   const LABEL = 'Custom Element w/ Declarative Shadow DOM and using children and <slot> content';
   let dom;
-  let pageContentsDom;
 
   before(async function() {
     const { html } = await renderToString(new URL('./src/pages/index.js', import.meta.url));
 
     dom = new JSDOM(html);
-
-    pageContentsDom = new JSDOM(dom.window.document.querySelectorAll('template[shadowroot="open"]')[0].innerHTML);
   });
 
   describe(LABEL, function() {
-    it('should have one top level <template> with an open shadowroot', function() {
-      expect(dom.window.document.querySelectorAll('template[shadowroot="open"]').length).to.equal(1);
-      expect(dom.window.document.querySelectorAll('template').length).to.equal(1);
+    it('should have one two top level <wcc-paragraph> custom elements with a <template> with an open shadowroot', function() {
+      expect(dom.window.document.querySelectorAll('wcc-paragraph template[shadowroot="open"]').length).to.equal(2);
+      expect(dom.window.document.querySelectorAll('wcc-paragraph template').length).to.equal(2);
     });
 
     describe('<my-paragraph> with default <slot> content', function() {
       let paragraphContentsDom;
 
       before(function() {
-        paragraphContentsDom = new JSDOM(pageContentsDom.window.document.querySelectorAll('wcc-paragraph.default template[shadowroot="open"]')[0].innerHTML);
+        paragraphContentsDom = new JSDOM(dom.window.document.querySelectorAll('wcc-paragraph.default template[shadowroot="open"]')[0].innerHTML);
       });
 
       it('should have one <my-paragraph> tag for the default content', function() {
@@ -57,16 +54,16 @@ describe('Run WCC For ', function() {
     describe('<my-paragraph> with custom <slot> content', function() {
       let paragraphContentsDom;
       let paragraphContentsLightDom;
-  
+
       before(function() {
-        paragraphContentsDom = new JSDOM(pageContentsDom.window.document.querySelectorAll('wcc-paragraph.custom template[shadowroot="open"]')[0].innerHTML);
-        paragraphContentsLightDom = new JSDOM(pageContentsDom.window.document.querySelectorAll('wcc-paragraph.custom')[0].innerHTML);
+        paragraphContentsDom = new JSDOM(dom.window.document.querySelectorAll('wcc-paragraph.custom template[shadowroot="open"]')[0].innerHTML);
+        paragraphContentsLightDom = new JSDOM(dom.window.document.querySelectorAll('wcc-paragraph.custom')[0].innerHTML);
       });
-  
+
       it('should have one <my-paragraph> tag for the default content', function() {
         expect(paragraphContentsDom.window.document.querySelectorAll('p').length).to.equal(1);
       });
-  
+
       it('should have one <span> tag with the custom content in the light DOM', function() {
         expect(paragraphContentsLightDom.window.document.querySelector('span').textContent).to.equal('Let\'s have some different text!');
       });
diff --git a/test/cases/children-and-slots/src/pages/index.js b/test/cases/children-and-slots/src/pages/index.js
index 7f5d534..ed1b9aa 100644
--- a/test/cases/children-and-slots/src/pages/index.js
+++ b/test/cases/children-and-slots/src/pages/index.js
@@ -2,10 +2,7 @@ import '../components/paragraph.js';
 
 export default class HomePage extends HTMLElement {
   connectedCallback() {
-    if (!this.shadowRoot) {
-      this.attachShadow({ mode: 'open' });
-      this.shadowRoot.innerHTML = this.getTemplate();
-    }
+    this.innerHTML = this.getTemplate();
   }
 
   getTemplate() {
diff --git a/test/cases/get-data/get-data.spec.js b/test/cases/get-data/get-data.spec.js
index b6d0aba..551f916 100644
--- a/test/cases/get-data/get-data.spec.js
+++ b/test/cases/get-data/get-data.spec.js
@@ -21,24 +21,22 @@ const expect = chai.expect;
 describe('Run WCC For ', function() {
   const LABEL = 'Custom Element w/ getData and Shadow DOM';
   let dom;
-  let pageContentsDom;
 
   before(async function() {
     const { html } = await renderToString(new URL('./src/index.js', import.meta.url));
 
     dom = new JSDOM(html);
-    pageContentsDom = new JSDOM(dom.window.document.querySelectorAll('template[shadowroot="open"]')[0].innerHTML);
   });
 
   describe(LABEL, function() {
-    it('should have one top level <template> with an open shadowroot', function() {
-      expect(dom.window.document.querySelectorAll('template[shadowroot="open"]').length).to.equal(1);
-      expect(dom.window.document.querySelectorAll('template').length).to.equal(1);
+    it('should have one top level <wcc-counter> custom element with a <template> with an open shadowroot', function() {
+      expect(dom.window.document.querySelectorAll('wcc-counter template[shadowroot="open"]').length).to.equal(1);
+      expect(dom.window.document.querySelectorAll('wcc-counter template').length).to.equal(1);
     });
 
     describe('static page content', function() {
       it('should have the expected static content for the page', function() {
-        expect(pageContentsDom.window.document.querySelector('h1').textContent).to.equal('Counter');
+        expect(dom.window.document.querySelector('h1').textContent).to.equal('Counter');
       });
     });
 
@@ -47,7 +45,7 @@ describe('Run WCC For ', function() {
       let count;
 
       before(function() {
-        counterContentsDom = new JSDOM(pageContentsDom.window.document.querySelectorAll('wcc-counter template[shadowroot="open"]')[0].innerHTML);
+        counterContentsDom = new JSDOM(dom.window.document.querySelectorAll('wcc-counter template[shadowroot="open"]')[0].innerHTML);
         count = JSON.parse(counterContentsDom.window.document.querySelector('script[type="application/json"]').textContent).count;
       });
 
@@ -57,7 +55,7 @@ describe('Run WCC For ', function() {
 
       it('should have a <span> with the value of the attribute as its text content', function() {
         const innerCount = counterContentsDom.window.document.querySelector('span#count').textContent;
-  
+
         expect(count).to.equal(parseInt(innerCount, 10));
       });
     });
diff --git a/test/cases/get-data/src/components/counter.js b/test/cases/get-data/src/components/counter.js
index e488fba..af70c90 100644
--- a/test/cases/get-data/src/components/counter.js
+++ b/test/cases/get-data/src/components/counter.js
@@ -52,15 +52,17 @@ class Counter extends HTMLElement {
 
   render() {
     return `
-      <script type="application/json">
-        ${JSON.stringify({ count: this.count })}
-      </script>
+      <template shadowroot="open">
+        <script type="application/json">
+          ${JSON.stringify({ count: this.count })}
+        </script>
 
-      <div>
-        <button id="inc">Increment</button>
-        <span>Current Count: <span id="count">${this.count}</span></span>
-        <button id="dec">Decrement</button>
-      </div>
+        <div>
+          <button id="inc">Increment</button>
+          <span>Current Count: <span id="count">${this.count}</span></span>
+          <button id="dec">Decrement</button>
+        </div>
+      </template>
     `;
   }
 }
diff --git a/test/cases/get-data/src/index.js b/test/cases/get-data/src/index.js
index 67c6d6c..0a5e4b9 100644
--- a/test/cases/get-data/src/index.js
+++ b/test/cases/get-data/src/index.js
@@ -1,18 +1,9 @@
 import './components/counter.js';
 
 export default class HomePage extends HTMLElement {
-  constructor() {
-    super();
-
-    if (this.shadowRoot) {
-      // console.debug('HomePage => shadowRoot detected!');
-    } else {
-      this.attachShadow({ mode: 'open' });
-    }
-  }
 
   connectedCallback() {
-    this.shadowRoot.innerHTML = this.getTemplate();
+    this.innerHTML = this.getTemplate();
   }
 
   getTemplate() {
diff --git a/test/cases/light-dom/light-dom.spec.js b/test/cases/light-dom/light-dom.spec.js
index 7f0a9cf..de03978 100644
--- a/test/cases/light-dom/light-dom.spec.js
+++ b/test/cases/light-dom/light-dom.spec.js
@@ -53,24 +53,24 @@ describe('Run WCC For ', function() {
 
       it('should have expected content within the <header> tag', function() {
         const content = headerContentsDom.window.document.querySelector('a h4').textContent;
-  
+
         expect(content).to.contain('My Personal Blog');
       });
 
       describe('nested navigation element', function() {
         let navigationContentsDom;
-  
+
         before(function() {
           navigationContentsDom = new JSDOM(dom.window.document.querySelectorAll('wcc-navigation')[0].innerHTML);
         });
-  
+
         it('should have a <nav> tag within the <template> shadowroot', function() {
           expect(navigationContentsDom.window.document.querySelectorAll('nav').length).to.equal(1);
         });
-  
+
         it('should have three links within the <nav> element', function() {
           const links = navigationContentsDom.window.document.querySelectorAll('nav ul li a');
-    
+
           expect(links.length).to.equal(3);
         });
       });
diff --git a/test/cases/light-dom/src/components/navigation.js b/test/cases/light-dom/src/components/navigation.js
index 2ff6aa4..f2256e0 100644
--- a/test/cases/light-dom/src/components/navigation.js
+++ b/test/cases/light-dom/src/components/navigation.js
@@ -1,18 +1,14 @@
-const template = document.createElement('template');
-
-template.innerHTML = `
-  <nav>
-    <ul>
-      <li><a href="/">Home</a></li>
-      <li><a href="/about">About</a></li>
-      <li><a href="/artists">Artists</a></li>
-    <ul>
-  </nav>
-`;
-
 class Navigation extends HTMLElement {
   connectedCallback() {
-    this.appendChild(template.content.cloneNode(true));
+    this.innerHTML = `
+      <nav>
+        <ul>
+          <li><a href="/">Home</a></li>
+          <li><a href="/about">About</a></li>
+          <li><a href="/artists">Artists</a></li>
+        <ul>
+      </nav>
+    `;
   }
 }
 
diff --git a/test/cases/nested-elements/nested-elements.spec.js b/test/cases/nested-elements/nested-elements.spec.js
index ee99dc4..4c274a2 100644
--- a/test/cases/nested-elements/nested-elements.spec.js
+++ b/test/cases/nested-elements/nested-elements.spec.js
@@ -53,7 +53,7 @@ describe('Run WCC For ', function() {
 
       it('should have expected content within the <footer> tag', function() {
         const content = footerContentsDom.window.document.querySelector('footer h4 a').textContent;
-  
+
         expect(content).to.contain('My Blog');
       });
     });
@@ -77,24 +77,24 @@ describe('Run WCC For ', function() {
 
       it('should have expected content within the <header> tag', function() {
         const content = headerContentsDom.window.document.querySelector('header a h4').textContent;
-  
+
         expect(content).to.contain('My Personal Blog');
       });
 
       describe('nested navigation element', function() {
         let navigationContentsDom;
-  
+
         before(function() {
           navigationContentsDom = new JSDOM(headerContentsDom.window.document.querySelectorAll('wcc-navigation template[shadowroot="open"]')[0].innerHTML);
         });
-  
+
         it('should have a <nav> tag within the <template> shadowroot', function() {
           expect(navigationContentsDom.window.document.querySelectorAll('nav').length).to.equal(1);
         });
-  
+
         it('should have three links within the <nav> element', function() {
           const links = navigationContentsDom.window.document.querySelectorAll('nav ul li a');
-    
+
           expect(links.length).to.equal(3);
         });
       });
diff --git a/test/cases/nested-elements/src/components/header.js b/test/cases/nested-elements/src/components/header.js
index 927b583..757cafc 100644
--- a/test/cases/nested-elements/src/components/header.js
+++ b/test/cases/nested-elements/src/components/header.js
@@ -11,27 +11,29 @@ class Header extends HTMLElement {
 
   render() {
     return `
-      <header class="header">
-        <div class="head-wrap">
-          <div class="brand">
-            <a href="/">
-              <img src="/www/assets/greenwood-logo.jpg" alt="Greenwood logo"/>
-              <h4>My Personal Blog</h4>
-            </a>
-          </div>
+      <template shadowroot="open">
+        <header class="header">
+          <div class="head-wrap">
+            <div class="brand">
+              <a href="/">
+                <img src="/www/assets/greenwood-logo.jpg" alt="Greenwood logo"/>
+                <h4>My Personal Blog</h4>
+              </a>
+            </div>
 
-          <wcc-navigation></wcc-navigation>
+            <wcc-navigation></wcc-navigation>
 
-          <div class="social">
-            <a href="https://github.com/ProjectEvergreen/greenwood">
-              <img
-                src="https://img.shields.io/github/stars/ProjectEvergreen/greenwood.svg?style=social&logo=github&label=github"
-                alt="Greenwood GitHub badge"
-                class="github-badge"/>
-            </a>
+            <div class="social">
+              <a href="https://github.com/ProjectEvergreen/greenwood">
+                <img
+                  src="https://img.shields.io/github/stars/ProjectEvergreen/greenwood.svg?style=social&logo=github&label=github"
+                  alt="Greenwood GitHub badge"
+                  class="github-badge"/>
+              </a>
+            </div>
           </div>
-        </div>
-      </header>
+        </header>
+      </template>
     `;
   }
 }
diff --git a/test/cases/nested-elements/src/pages/index.js b/test/cases/nested-elements/src/pages/index.js
index 3981190..2aa2bbc 100644
--- a/test/cases/nested-elements/src/pages/index.js
+++ b/test/cases/nested-elements/src/pages/index.js
@@ -1,6 +1,16 @@
 import '../components/footer.js';
 import '../components/header.js';
 
+const template = document.createElement('template');
+
+template.innerHTML = `
+  <wcc-header></wcc-header>
+
+  <h1>Home Page</h1>
+
+  <wcc-footer></wcc-footer>
+`;
+
 export default class HomePage extends HTMLElement {
   constructor() {
     super();
@@ -13,17 +23,7 @@ export default class HomePage extends HTMLElement {
   }
 
   connectedCallback() {
-    this.shadowRoot.innerHTML = this.getTemplate();
-  }
-
-  getTemplate() {
-    return `
-      <wcc-header></wcc-header>
-
-      <h1>Home Page</h1>
-
-      <wcc-footer></wcc-footer>
-    `;
+    this.shadowRoot.innerHTML = template.innerHTML;
   }
 }
 
diff --git a/test/cases/render-from-html/render-from-html.spec.js b/test/cases/render-from-html/render-from-html.spec.js
index 7933596..8bacb56 100644
--- a/test/cases/render-from-html/render-from-html.spec.js
+++ b/test/cases/render-from-html/render-from-html.spec.js
@@ -80,24 +80,24 @@ describe('Run WCC ', function() {
 
       it('should have expected content within the <header> tag', function() {
         const content = headerContentsDom.window.document.querySelector('header a h4').textContent;
-  
+
         expect(content).to.contain('My Personal Blog');
       });
 
       describe('nested navigation element', function() {
         let navigationContentsDom;
-  
+
         before(function() {
           navigationContentsDom = new JSDOM(headerContentsDom.window.document.querySelectorAll('wcc-navigation template[shadowroot="open"]')[0].innerHTML);
         });
-  
+
         it('should have a <nav> tag within the <template> shadowroot', function() {
           expect(navigationContentsDom.window.document.querySelectorAll('nav').length).to.equal(1);
         });
-  
+
         it('should have three links within the <nav> element', function() {
           const links = navigationContentsDom.window.document.querySelectorAll('nav ul li a');
-    
+
           expect(links.length).to.equal(3);
         });
       });
@@ -107,7 +107,7 @@ describe('Run WCC ', function() {
       it('should have two custom elements in the asset graph', function() {
         expect(Object.keys(assetMetadata).length).to.equal(2);
       });
-  
+
       it('should have the correct attributes for each asset', function() {
         Object.entries(assetMetadata).forEach((asset) => {
           expect(asset[0]).to.not.be.undefined;
diff --git a/test/cases/render-from-html/src/components/header.js b/test/cases/render-from-html/src/components/header.js
index 2e16851..f38b3cc 100644
--- a/test/cases/render-from-html/src/components/header.js
+++ b/test/cases/render-from-html/src/components/header.js
@@ -1,38 +1,38 @@
 import './navigation.js';
 
+const template = document.createElement('template');
+
+template.innerHTML = `
+  <header class="header">
+    <div class="head-wrap">
+      <div class="brand">
+        <a href="/">
+          <img src="/www/assets/greenwood-logo.jpg" alt="Greenwood logo"/>
+          <h4>My Personal Blog</h4>
+        </a>
+      </div>
+
+      <wcc-navigation></wcc-navigation>
+
+      <div class="social">
+        <a href="https://github.com/ProjectEvergreen/greenwood">
+          <img
+            src="https://img.shields.io/github/stars/ProjectEvergreen/greenwood.svg?style=social&logo=github&label=github"
+            alt="Greenwood GitHub badge"
+            class="github-badge"/>
+        </a>
+      </div>
+    </div>
+  </header>
+`;
+
 class Header extends HTMLElement {
   connectedCallback() {
     if (!this.shadowRoot) {
       this.attachShadow({ mode: 'open' });
-      this.shadowRoot.innerHTML = this.render();
+      this.shadowRoot.appendChild(template.content.cloneNode(true));
     }
   }
-
-  render() {
-    return `
-      <header class="header">
-        <div class="head-wrap">
-          <div class="brand">
-            <a href="/">
-              <img src="/www/assets/greenwood-logo.jpg" alt="Greenwood logo"/>
-              <h4>My Personal Blog</h4>
-            </a>
-          </div>
-
-          <wcc-navigation></wcc-navigation>
-
-          <div class="social">
-            <a href="https://github.com/ProjectEvergreen/greenwood">
-              <img
-                src="https://img.shields.io/github/stars/ProjectEvergreen/greenwood.svg?style=social&logo=github&label=github"
-                alt="Greenwood GitHub badge"
-                class="github-badge"/>
-            </a>
-          </div>
-        </div>
-      </header>
-    `;
-  }
 }
 
 export default Header;
diff --git a/test/cases/single-element/single-element.spec.js b/test/cases/single-element/single-element.spec.js
index 600a993..9fff355 100644
--- a/test/cases/single-element/single-element.spec.js
+++ b/test/cases/single-element/single-element.spec.js
@@ -23,13 +23,13 @@ describe('Run WCC For ', function() {
 
   before(async function() {
     const { html } = await renderToString(new URL('./src/footer.js', import.meta.url));
-    
+
     rawHtml = html;
     dom = new JSDOM(html);
   });
 
   describe(LABEL, function() {
-      
+
     it('should NOT have a <head> tag in the content of the page', function() {
       expect(rawHtml.indexOf('<head>') >= 0).to.equal(false);
     });
@@ -42,7 +42,7 @@ describe('Run WCC For ', function() {
       expect(rawHtml.indexOf('<body>') >= 0).to.equal(false);
     });
 
-    it('should have one top level <template> with an open shadowroot', function() {
+    it('should have one top level <wcc-footer> element with a <template> with an open shadowroot', function() {
       expect(dom.window.document.querySelectorAll('wcc-footer template[shadowroot="open"]').length).to.equal(1);
       expect(dom.window.document.querySelectorAll('template').length).to.equal(1);
     });
@@ -51,13 +51,13 @@ describe('Run WCC For ', function() {
       let footer;
 
       before(async function() {
-        footer = new JSDOM(dom.window.document.querySelectorAll('template[shadowroot="open"]')[0].innerHTML);
+        footer = new JSDOM(dom.window.document.querySelectorAll('wcc-footer template[shadowroot="open"]')[0].innerHTML);
       });
 
       it('should have one <footer> tag within the <template> shadowroot', function() {
         expect(footer.window.document.querySelectorAll('footer').length).to.equal(1);
       });
-  
+
       it('should have the expected content for the <footer> tag', function() {
         expect(footer.window.document.querySelectorAll('h4 a').textContent).to.contain(/My Blog/);
       });