Repository: ProjectEvergreen/wcc (js)

Issue:
Support Constructable Stylesheets (CSS Modules) in DOM Shim
## Summary
Now that Import Attributes are a thing, including CSS Modules as [Constructable Stylesheets](https://web.dev/articles/constructable-stylesheets), it would be good to get some basic support for this into the DOM Shim at least in some capacity.

Something at minimum like, that can shim part of the [`CSSStyleSheet`](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet) class
```js
class CSSStyleSheet {
  constructor() { }
  replace() { }
  replaceSync() { }
}
```


## Details

This would help support projects like Greenwood in supporting features like being able to author using Import Attributes - https://github.com/ProjectEvergreen/wcc/issues/153

Pull request description:
feature/issue 153 `CSSStyleSheet` in DOM Shim
<!--
## Submitting a Pull Request
We love contributions and appreciate any help you can offer!
-->

## Related Issue
resolves #152 

## Summary of Changes
1. Adds a shim for `CSSStyleSheet`
1. Add test case
1. Document support (just as no-ops for now, but can certainly flesh this out more as needed by the community and / or use cases)

## TODO
1. [x] Do / should we need to polyfill anything else?
1. [x] Update documentation
1. [x] Add test case (or maybe wait as part of #152 ?)

Patch:
diff --git a/docs/pages/index.md b/docs/pages/index.md
index 37350ed..fffc4cf 100644
--- a/docs/pages/index.md
+++ b/docs/pages/index.md
@@ -20,6 +20,7 @@ $ npm install wc-compiler --save-dev
     - `[get|set|has]Attribute`
 1. `<template>` / `DocumentFragment`
 1. `addEventListener` (as a no-op)
+1. Supports `CSSStyleSheet` (all methods act as no-ops)
 1. Recursive rendering of nested custom elements
 1. Metadata and runtime hints to support various progressive hydration and lazy loading strategies
 
diff --git a/src/dom-shim.js b/src/dom-shim.js
index ee621ea..be289a3 100644
--- a/src/dom-shim.js
+++ b/src/dom-shim.js
@@ -1,5 +1,13 @@
 function noop() { }
 
+// https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet
+class CSSStyleSheet {
+  insertRule() { }
+  deleteRule() { }
+  replace() { }
+  replaceSync() { }
+}
+
 // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
 class EventTarget {
   constructor() {
@@ -93,6 +101,7 @@ class ShadowRoot extends DocumentFragment {
   constructor(options) {
     super();
     this.mode = options.mode || 'closed';
+    this.adoptedStyleSheets = [];
   }
 }
 
@@ -146,4 +155,5 @@ class CustomElementsRegistry {
 globalThis.addEventListener = globalThis.addEventListener ?? noop;
 globalThis.document = globalThis.document ?? new Document();
 globalThis.customElements = globalThis.customElements ?? new CustomElementsRegistry();
-globalThis.HTMLElement = globalThis.HTMLElement ?? HTMLElement;
\ No newline at end of file
+globalThis.HTMLElement = globalThis.HTMLElement ?? HTMLElement;
+globalThis.CSSStyleSheet = globalThis.CSSStyleSheet ?? CSSStyleSheet;
\ No newline at end of file


Test patch:
diff --git a/test/cases/constructable-stylesheet/constructabe-stylesheet.spec.js b/test/cases/constructable-stylesheet/constructabe-stylesheet.spec.js
new file mode 100644
index 0000000..fd433d9
--- /dev/null
+++ b/test/cases/constructable-stylesheet/constructabe-stylesheet.spec.js
@@ -0,0 +1,38 @@
+/*
+ * Use Case
+ * Run wcc against a custom element using constructible stylesheets.
+ *
+ * User Result
+ * Should return the expected HTML and no error instatiating a CSSStyleSheet..
+ *
+ * User Workspace
+ * src/
+ *   components/
+ *     header/
+ *       header.js
+ *   pages/
+ *     index.js
+ */
+import chai from 'chai';
+import { JSDOM } from 'jsdom';
+import { renderToString } from '../../../src/wcc.js';
+
+const expect = chai.expect;
+
+describe('Run WCC For ', function() {
+  const LABEL = 'Constructible Stylesheets usage';
+  let dom;
+
+  before(async function() {
+    const { html } = await renderToString(new URL('./src/pages/index.js', import.meta.url));
+
+    dom = new JSDOM(html);
+  });
+
+  describe(LABEL, function() {
+    it('should have one top level <wcc-header> element with a <template> with an open shadowroot', function() {
+      expect(dom.window.document.querySelectorAll('wcc-header template[shadowrootmode="open"]').length).to.equal(1);
+      expect(dom.window.document.querySelectorAll('template').length).to.equal(1);
+    });
+  });
+});
\ No newline at end of file
diff --git a/test/cases/constructable-stylesheet/src/components/header/header.js b/test/cases/constructable-stylesheet/src/components/header/header.js
new file mode 100644
index 0000000..24ad6fa
--- /dev/null
+++ b/test/cases/constructable-stylesheet/src/components/header/header.js
@@ -0,0 +1,23 @@
+const template = document.createElement('template');
+
+template.innerHTML = `
+  <header>
+    <h1>Welcome to my website!</h1>
+  </header>
+`;
+
+const sheet = new CSSStyleSheet();
+sheet.replaceSync('li{color:red;}');
+
+export default class Header extends HTMLElement {
+  connectedCallback() {
+    if (!this.shadowRoot) {
+      this.attachShadow({ mode: 'open' });
+      this.shadowRoot.appendChild(template.content.cloneNode(true));
+    }
+
+    this.shadowRoot.adoptedStyleSheets = [sheet];
+  }
+}
+
+customElements.define('wcc-header', Header);
\ No newline at end of file
diff --git a/test/cases/constructable-stylesheet/src/pages/index.js b/test/cases/constructable-stylesheet/src/pages/index.js
new file mode 100644
index 0000000..e0f385d
--- /dev/null
+++ b/test/cases/constructable-stylesheet/src/pages/index.js
@@ -0,0 +1,10 @@
+import '../components/header/header.js';
+
+export default class HomePage extends HTMLElement {
+  connectedCallback() {
+    this.innerHTML = `
+      <wcc-header></wcc-header>
+      <h1>Home Page</h1>
+    `;
+  }
+}
\ No newline at end of file
diff --git a/test/cases/import-attributes/import-attributes.spec.js b/test/cases/import-attributes/import-attributes.spec.js
index a5f9612..7dfdb26 100644
--- a/test/cases/import-attributes/import-attributes.spec.js
+++ b/test/cases/import-attributes/import-attributes.spec.js
@@ -31,10 +31,8 @@ describe('Run WCC For ', function() {
   });
 
   describe(LABEL, function() {
-    describe('static page content', function() {
-      it('should have the expected static content for the page', function() {
-        expect(dom.window.document.querySelector('h1').textContent).to.equal('Home Page');
-      });
+    it('should have the expected static content for the page', function() {
+      expect(dom.window.document.querySelector('h1').textContent).to.equal('Home Page');
     });
 
     it('should have a <header> tag within the document', function() {