[bugs-central] bugs-central-scaffold-sk

Bug: skia:10783
Change-Id: I6e46aea0bbb439ab0bda138d2f067be14bb46dee
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/328976
Reviewed-by: Joe Gregorio <jcgregorio@google.com>
diff --git a/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk-demo.html b/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk-demo.html
new file mode 100644
index 0000000..0fb6e0e
--- /dev/null
+++ b/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk-demo.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>bugs-central-scaffold-sk</title>
+  <meta charset="utf-8" />
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+</head>
+<body>
+  <bugs-central-scaffold-sk app_title="Skia Public" testing_offline>
+    <div>
+      Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
+      tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
+      vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
+      no sea takimata sanctus est Lorem ipsum dolor sit amet.
+    </div>
+    <div>
+      Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
+      tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
+      vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
+      no sea takimata sanctus est Lorem ipsum dolor sit amet.
+    </div>
+  </bugs-central-scaffold-sk>
+</body>
+</html>
diff --git a/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk-demo.ts b/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk-demo.ts
new file mode 100644
index 0000000..b811cfb
--- /dev/null
+++ b/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk-demo.ts
@@ -0,0 +1 @@
+import './index';
diff --git a/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk.scss b/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk.scss
new file mode 100644
index 0000000..a006f7c
--- /dev/null
+++ b/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk.scss
@@ -0,0 +1,81 @@
+@import '~elements-sk/colors';
+@import '../style.scss';
+
+bugs-central-scaffold-sk {
+  * {
+    font-family:Roboto,Helvetica,Arial,'Bitstream Vera Sans',sans-serif;
+  }
+
+  header {
+    h1 {
+      font-weight: 400;
+      font-size: 24px;
+      padding: 0 4px;
+    }
+
+    .spinner-spacer {
+      min-width: 40px;
+    }
+
+    spinner-sk {
+      width: 24px;
+      height: 24px;
+      margin: 0;
+      border: 8px solid var(--on-primary);
+      border-left: 8px solid var(--primary-variant);
+    }
+  }
+
+  main {
+    background-color: var(--background);
+  }
+
+  nav {
+    display:flex;
+    flex-direction: column;
+
+    a {
+      padding: 16px 0px;
+      color: var(--on-surface); /* Override User Agent SS*/
+      text-decoration: none;
+    }
+
+    span {
+      padding: 0 8px;
+      vertical-align: middle;
+      white-space: nowrap;
+    }
+  }
+
+  /* Overrides to elements-sk elements since they set color internally. */
+  app-sk, app-sk > aside {
+    background-color: var(--background);
+    color: var(--on-background);
+
+    aside {
+      border-right: 1px solid var(--on-background);
+    }
+    --content-horiz-padding: 32px;
+  }
+
+  app-sk > main {
+    padding: 32px 80px;
+  }
+
+  login-sk {
+    margin-left: auto;
+    margin-right: 16px;
+    display: block;
+    color: var(--on-primary);
+  }
+
+  login-sk .email, login-sk .logInOut {
+    font-size: 16px;
+    color: var(--on-primary);
+  }
+
+  error-toast-sk toast-sk {
+    background-color: var(--error);
+    color: var(--on-error);
+  }
+}
diff --git a/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk.ts b/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk.ts
new file mode 100644
index 0000000..3f28f66
--- /dev/null
+++ b/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk.ts
@@ -0,0 +1,163 @@
+/**
+ * @module module/bugs-central-scaffold-sk
+ * @description <h2><code>bugs-central-scaffold-sk</code></h2>
+ *
+ * Contains the title bar and error-toast for all the bugs central pages. The
+ * rest of pages should be a child of this element.
+ *
+ * @attr {string} app_title - The title to show in the page banner.
+ *
+ * @attr {boolean} testing_offline - If we should operate entirely in offline mode.
+
+ *
+ */
+
+import { define } from 'elements-sk/define';
+import { html } from 'lit-html';
+import { ElementSk } from '../../../infra-sk/modules/ElementSk';
+
+import 'elements-sk/error-toast-sk';
+import 'elements-sk/icon/android-icon-sk';
+import 'elements-sk/icon/bug-report-icon-sk';
+import 'elements-sk/icon/folder-icon-sk';
+import 'elements-sk/icon/gesture-icon-sk';
+import 'elements-sk/icon/help-icon-sk';
+import 'elements-sk/icon/home-icon-sk';
+import 'elements-sk/icon/person-pin-icon-sk';
+import 'elements-sk/icon/star-icon-sk';
+import 'elements-sk/nav-button-sk';
+import 'elements-sk/nav-links-sk';
+import 'elements-sk/spinner-sk';
+
+import '../../../infra-sk/modules/app-sk';
+import '../../../infra-sk/modules/login-sk';
+import '../../../infra-sk/modules/theme-chooser-sk';
+
+/**
+ * Moves the elements from a list to be the children of the target element.
+ *
+ * @param from - The list of elements we are moving.
+ * @param to - The new parent.
+ */
+function move(from: HTMLCollection | NodeList, to: HTMLElement) {
+  Array.prototype.slice.call(from).forEach((ele) => to.appendChild(ele));
+}
+
+export class BugsCentralScaffoldSk extends ElementSk {
+  private _main: HTMLElement | null = null;
+
+  constructor() {
+    super(BugsCentralScaffoldSk.template);
+  }
+
+  private static template = (el: BugsCentralScaffoldSk) => html`
+  <app-sk>
+  <header class=primary-container-themes-sk>
+    <h1>${el.appTitle}</h1>
+    <div class=spinner-spacer>
+      <spinner-sk></spinner-sk>
+    </div>
+    <div class=spacer></div>
+    <login-sk ?testing_offline=${el.testingOffline}></login-sk>
+    <theme-chooser-sk></theme-chooser-sk>
+  </header>
+
+  <aside>
+    <nav class=surface-themes-sk>
+      <a href="/" tab-index=0 >
+        <home-icon-sk></home-icon-sk><span>All Clients</span>
+      </a>
+      <a href="/?client=Android" tab-index=0 >
+        <person-pin-icon-sk></person-pin-icon-sk><span>Android Client</span>
+      </a>
+      <a href="/?client=Chromium" tab-index=0 >
+        <person-pin-icon-sk></person-pin-icon-sk><span>Chromium Client</span>
+      </a>
+      <a href="/?client=Flutter-native" tab-index=0 >
+        <person-pin-icon-sk></person-pin-icon-sk><span>Flutter-native Client</span>
+      </a>
+      <a href="/?client=Flutter-on-web" tab-index=0 >
+        <person-pin-icon-sk></person-pin-icon-sk><span>Flutter-on-web Client</span>
+      </a>
+      <a href="/?client=Skia" tab-index=0 >
+        <person-pin-icon-sk></person-pin-icon-sk><span>Skia Client</span>
+      </a>
+      <a href="http://go/skia-bugs-central" tab-index=0>
+        <help-icon-sk></help-icon-sk><span>Help</span>
+      </a>
+      <a href="https://github.com/google/skia-buildbot/tree/master/bugs-central" tab-index=0>
+        <folder-icon-sk></folder-icon-sk><span>Code</span>
+      </a>
+    </nav>
+  </aside>
+
+  <main></main>
+
+  <footer><error-toast-sk></error-toast-sk></footer>
+  </app-sk>
+`;
+
+  connectedCallback(): void {
+    super.connectedCallback();
+    // Don't call more than once.
+    if (this._main) {
+      return;
+    }
+    // We aren't using shadow dom so we need to manually move the children of
+    // perf-scaffold-sk to be children of 'main'. We have to do this for the
+    // existing elements and for all future mutations.
+
+    // Create a temporary holding spot for elements we're moving.
+    const div = document.createElement('div');
+    move(this.children, div);
+
+    // Now that we've moved all the old children out of the way we can render
+    // the template.
+    this._render();
+
+    this._main = this.querySelector('main');
+
+    // Move the old children back.
+    this.redistributeAddedNodes(div.childNodes);
+
+    // Move all future children also.
+    const observer = new MutationObserver((mutList) => {
+      mutList.forEach((mut) => {
+        this.redistributeAddedNodes(mut.addedNodes);
+      });
+    });
+    observer.observe(this, { childList: true });
+  }
+
+  disconnectedCallback(): void {
+    super.disconnectedCallback();
+  }
+
+  // Place these newly added nodes in the right place under the bugs-central-scaffold-sk
+  // element.
+  private redistributeAddedNodes(from: NodeList) {
+    Array.prototype.slice.call(from).forEach((node: Node) => {
+      this._main!.appendChild(node);
+    });
+  }
+
+
+  /** @prop appTitle {string} Reflects the app_title attribute for ease of use. */
+  get appTitle(): string { return this.getAttribute('app_title')!; }
+
+  set appTitle(val: string) { this.setAttribute('app_title', val); }
+
+  /** @prop testingOffline {boolean} Reflects the testing_offline attribute for ease of use.
+   */
+  get testingOffline(): boolean { return this.hasAttribute('testing_offline'); }
+
+  set testingOffline(val: boolean) {
+    if (val) {
+      this.setAttribute('testing_offline', '');
+    } else {
+      this.removeAttribute('testing_offline');
+    }
+  }
+}
+
+define('bugs-central-scaffold-sk', BugsCentralScaffoldSk);
diff --git a/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk_puppeteer_test.ts b/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk_puppeteer_test.ts
new file mode 100644
index 0000000..08c39f6
--- /dev/null
+++ b/bugs-central/modules/bugs-central-scaffold-sk/bugs-central-scaffold-sk_puppeteer_test.ts
@@ -0,0 +1,23 @@
+import * as path from 'path';
+import { expect } from 'chai';
+import { setUpPuppeteerAndDemoPageServer, takeScreenshot } from '../../../puppeteer-tests/util';
+
+describe('bugs-central-scaffold-sk', () => {
+  const testBed = setUpPuppeteerAndDemoPageServer(path.join(__dirname, '..', '..', 'webpack.config.ts'));
+
+  beforeEach(async () => {
+    await testBed.page.goto(`${testBed.baseUrl}/static/bugs-central-scaffold-sk.html`);
+    await testBed.page.setViewport({ width: 400, height: 500 });
+  });
+
+  it('should render the demo page', async () => {
+    // Smoke test.
+    expect(await testBed.page.$$('bugs-central-scaffold-sk')).to.have.length(1);
+  });
+
+  describe('screenshots', () => {
+    it('shows the default view', async () => {
+      await takeScreenshot(testBed.page, 'bugs-central', 'bugs-central-scaffold-sk');
+    });
+  });
+});
diff --git a/bugs-central/modules/bugs-central-scaffold-sk/index.ts b/bugs-central/modules/bugs-central-scaffold-sk/index.ts
new file mode 100644
index 0000000..e61e529
--- /dev/null
+++ b/bugs-central/modules/bugs-central-scaffold-sk/index.ts
@@ -0,0 +1,2 @@
+import './bugs-central-scaffold-sk.scss';
+import './bugs-central-scaffold-sk';