[gold] Port triage-sk to TypeScript.

Bug: skia:10246
Change-Id: I67281a5f71f621b33dfc1da2ea6c3c1d86f44a9d
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/397337
Commit-Queue: Leandro Lovisolo <lovisolo@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
diff --git a/golden/modules/triage-sk/index.js b/golden/modules/triage-sk/index.ts
similarity index 100%
rename from golden/modules/triage-sk/index.js
rename to golden/modules/triage-sk/index.ts
diff --git a/golden/modules/triage-sk/triage-sk-demo.js b/golden/modules/triage-sk/triage-sk-demo.js
deleted file mode 100644
index e94efa2..0000000
--- a/golden/modules/triage-sk/triage-sk-demo.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import './index';
-import { $$ } from 'common-sk/modules/dom';
-import { isPuppeteerTest } from '../demo_util';
-
-const log = (message) => {
-  const log = $$('#event-log');
-  const entry = `${new Date().toISOString()}\t${message}\n`;
-  log.value = entry + log.value;
-};
-
-const triageSk = document.createElement('triage-sk');
-triageSk.addEventListener('change', (e) => log(e.detail));
-
-const clearSelection = $$('#clear-selection');
-clearSelection.addEventListener('click', () => {
-  triageSk.value = '';
-  log('empty');
-});
-
-const container = $$('#container');
-container.insertBefore(triageSk, clearSelection);
-
-// Hide event log if we're within a Puppeteer test. We don't need the event log
-// to appear in any screenshots uploaded to Gold.
-if (isPuppeteerTest()) {
-  $$('#event-log-container').style.display = 'none';
-}
diff --git a/golden/modules/triage-sk/triage-sk-demo.ts b/golden/modules/triage-sk/triage-sk-demo.ts
new file mode 100644
index 0000000..7788f62
--- /dev/null
+++ b/golden/modules/triage-sk/triage-sk-demo.ts
@@ -0,0 +1,26 @@
+import './index';
+import { isPuppeteerTest } from '../demo_util';
+import {LabelOrEmpty, TriageSk} from './triage-sk';
+
+const log = (message: string) => {
+  const log = document.querySelector<HTMLTextAreaElement>('#event-log')!;
+  const entry = `${new Date().toISOString()}\t${message}\n`;
+  log.value = entry + log.value;
+};
+
+const triageSk = new TriageSk();
+triageSk.addEventListener('change', (e) => log((e as CustomEvent<LabelOrEmpty>).detail));
+
+const clearSelection = document.querySelector('#clear-selection')!;
+clearSelection.addEventListener('click', () => {
+  triageSk.value = '';
+  log('empty');
+});
+
+document.querySelector<HTMLDivElement>('#container')!.insertBefore(triageSk, clearSelection);
+
+// Hide event log if we're within a Puppeteer test. We don't need the event log
+// to appear in any screenshots uploaded to Gold.
+if (isPuppeteerTest()) {
+  document.querySelector<HTMLDivElement>('#event-log-container')!.style.display = 'none';
+}
diff --git a/golden/modules/triage-sk/triage-sk.js b/golden/modules/triage-sk/triage-sk.js
deleted file mode 100644
index ad0fd24..0000000
--- a/golden/modules/triage-sk/triage-sk.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * @module modules/triage-sk
- * @description <h2><code>triage-sk</code></h2>
- *
- * A custom element that allows labeling a digest as positive, negative or
- * untriaged.
- *
- * TODO(kjlubick) The search page and the cluster page hack this when doing bulk triage.
- *   If we can handle bulk triage in this element, that will simplify things and we can move the
- *   POST request from digest-details-sk here and all triage logic is consolidated.
- *
- * @evt change - Sent when any of the triage buttons are clicked. The new value
- *     will be contained in event.detail (possible values are "untriaged",
- *     "positive" or "negative").
- */
-
-import 'elements-sk/styles/buttons';
-import 'elements-sk/icon/check-circle-icon-sk';
-import 'elements-sk/icon/cancel-icon-sk';
-import 'elements-sk/icon/help-icon-sk';
-import { define } from 'elements-sk/define';
-import { html } from 'lit-html';
-import { ElementSk } from '../../../infra-sk/modules/ElementSk';
-
-// The "bulk triage" dialog offers more than the tree options below, so we need
-// triage-sk to support an empty state where no button is toggled.
-const NONE = '';
-const POSITIVE = 'positive';
-const NEGATIVE = 'negative';
-const UNTRIAGED = 'untriaged';
-
-const template = (el) => html`
-<button class="positive ${el.value === POSITIVE ? 'selected' : ''}"
-        @click=${() => el._buttonClicked(POSITIVE)}>
-  <check-circle-icon-sk></check-circle-icon-sk>
-</button>
-<button class="negative ${el.value === NEGATIVE ? 'selected' : ''}"
-        @click=${() => el._buttonClicked(NEGATIVE)}>
-  <cancel-icon-sk></cancel-icon-sk>
-</button>
-<button class="untriaged ${el.value === UNTRIAGED ? 'selected' : ''}"
-        @click=${() => el._buttonClicked(UNTRIAGED)}>
-  <help-icon-sk></help-icon-sk>
-</button>
-`;
-
-define('triage-sk', class extends ElementSk {
-  constructor() {
-    super(template);
-    this._value = UNTRIAGED;
-  }
-
-  connectedCallback() {
-    super.connectedCallback();
-    this._render();
-  }
-
-  /** @prop value {string} One of "untriaged", "positive" or "negative". */
-  get value() {
-    return this._value;
-  }
-
-  set value(newValue) {
-    if (![NONE, POSITIVE, NEGATIVE, UNTRIAGED].includes(newValue)) {
-      throw new RangeError(`Invalid triage-sk value: "${newValue}".`);
-    }
-    this._value = newValue;
-    this._render();
-  }
-
-  _buttonClicked(newValue) {
-    if (this.value === newValue) {
-      return;
-    }
-    this.value = newValue;
-    this.dispatchEvent(
-      new CustomEvent('change', { detail: newValue, bubbles: true }),
-    );
-  }
-});
diff --git a/golden/modules/triage-sk/triage-sk.scss b/golden/modules/triage-sk/triage-sk.scss
index 5f3734f..ae23e29 100644
--- a/golden/modules/triage-sk/triage-sk.scss
+++ b/golden/modules/triage-sk/triage-sk.scss
@@ -1,4 +1,5 @@
 @import '../colors';
+@import '~elements-sk/colors';
 
 triage-sk {
   display: inline-block;
@@ -9,7 +10,7 @@
 
     &.selected {
       background-color: var(--dark-white);
-      border: none;
+      border-color: var(--dark-white);
       box-shadow: none;
     }
 
diff --git a/golden/modules/triage-sk/triage-sk.ts b/golden/modules/triage-sk/triage-sk.ts
new file mode 100644
index 0000000..de15346
--- /dev/null
+++ b/golden/modules/triage-sk/triage-sk.ts
@@ -0,0 +1,83 @@
+/**
+ * @module modules/triage-sk
+ * @description <h2><code>triage-sk</code></h2>
+ *
+ * A custom element that allows labeling a digest as positive, negative or
+ * untriaged.
+ *
+ * TODO(kjlubick) The search page and the cluster page hack this when doing bulk triage.
+ *   If we can handle bulk triage in this element, that will simplify things and we can move the
+ *   POST request from digest-details-sk here and all triage logic is consolidated.
+ *
+ * @evt change - Sent when any of the triage buttons are clicked. The new value
+ *     will be contained in event.detail (possible values are "untriaged",
+ *     "positive" or "negative").
+ */
+
+import 'elements-sk/styles/buttons';
+import 'elements-sk/icon/check-circle-icon-sk';
+import 'elements-sk/icon/cancel-icon-sk';
+import 'elements-sk/icon/help-icon-sk';
+import { define } from 'elements-sk/define';
+import { html } from 'lit-html';
+import { ElementSk } from '../../../infra-sk/modules/ElementSk';
+import { Label } from '../rpc_types';
+
+/**
+ * The "bulk triage" dialog offers more than the tree options below, so we need triage-sk to
+ * support an empty state where no button is toggled.
+ */
+export type LabelOrEmpty = Label | '';
+
+export class TriageSk extends ElementSk {
+  private static template = (el: TriageSk) => html`
+    <button class="positive ${el.value === 'positive' ? 'selected' : ''}"
+            @click=${() => el.buttonClicked('positive')}>
+      <check-circle-icon-sk></check-circle-icon-sk>
+    </button>
+    <button class="negative ${el.value === 'negative' ? 'selected' : ''}"
+            @click=${() => el.buttonClicked('negative')}>
+      <cancel-icon-sk></cancel-icon-sk>
+    </button>
+    <button class="untriaged ${el.value === 'untriaged' ? 'selected' : ''}"
+            @click=${() => el.buttonClicked('untriaged')}>
+      <help-icon-sk></help-icon-sk>
+    </button>
+  `;
+
+  private _value: LabelOrEmpty = 'untriaged';
+
+  constructor() {
+    super(TriageSk.template);
+  }
+
+  connectedCallback() {
+    super.connectedCallback();
+    this._render();
+  }
+
+  get value(): LabelOrEmpty {
+    return this._value;
+  }
+
+  set value(newValue: LabelOrEmpty) {
+    const validValues: LabelOrEmpty[] = ['', 'untriaged', 'negative', 'positive'];
+    if (!validValues.includes(newValue)) {
+      throw new RangeError(`Invalid triage-sk value: "${newValue}".`);
+    }
+    this._value = newValue;
+    this._render();
+  }
+
+  private buttonClicked(newValue: LabelOrEmpty) {
+    if (this.value === newValue) {
+      return;
+    }
+    this.value = newValue;
+    this.dispatchEvent(
+      new CustomEvent<LabelOrEmpty>('change', {detail: newValue, bubbles: true}),
+    );
+  }
+}
+
+define('triage-sk', TriageSk);
diff --git a/golden/modules/triage-sk/triage-sk_test.js b/golden/modules/triage-sk/triage-sk_test.ts
similarity index 68%
rename from golden/modules/triage-sk/triage-sk_test.js
rename to golden/modules/triage-sk/triage-sk_test.ts
index bd856be..87cfd92 100644
--- a/golden/modules/triage-sk/triage-sk_test.js
+++ b/golden/modules/triage-sk/triage-sk_test.ts
@@ -1,15 +1,17 @@
 import './index';
-import { $, $$ } from 'common-sk/modules/dom';
 import {
   eventPromise,
   noEventPromise,
   setUpElementUnderTest,
 } from '../../../infra-sk/modules/test_util';
+import { expect } from 'chai';
+import { LabelOrEmpty, TriageSk } from './triage-sk';
+import { Label } from '../rpc_types';
 
 describe('triage-sk', () => {
-  const newInstance = setUpElementUnderTest('triage-sk');
+  const newInstance = setUpElementUnderTest<TriageSk>('triage-sk');
 
-  let triageSk;
+  let triageSk: TriageSk;
   beforeEach(() => triageSk = newInstance());
 
   it('is untriaged by default', () => {
@@ -39,30 +41,32 @@
       });
 
     it('throws an exception upon an invalid value', () => {
-      expect(() => triageSk.value = 'hello world')
+      expect(() => triageSk.value = 'hello world' as LabelOrEmpty)
         .to.throw(RangeError, 'Invalid triage-sk value: "hello world".');
     });
   });
 
   describe('buttons', () => {
-    let changeEvent;
-    beforeEach(() => { changeEvent = eventPromise('change', 100); });
+    let changeEvent: Promise<CustomEvent<LabelOrEmpty>>;
+    beforeEach(() => {
+      changeEvent = eventPromise<CustomEvent<LabelOrEmpty>>('change', 100);
+    });
 
     it('sets value to positive when clicking positive button', async () => {
-      $$('button.positive', triageSk).click();
+      clickButton(triageSk,'positive');
       expectValueAndToggledButtonToBe(triageSk, 'positive');
       expect((await changeEvent).detail).to.equal('positive');
     });
 
     it('sets value to negative when clicking negative button', async () => {
-      $$('button.negative', triageSk).click();
+      clickButton(triageSk,'negative');
       expectValueAndToggledButtonToBe(triageSk, 'negative');
       expect((await changeEvent).detail).to.equal('negative');
     });
 
     it('sets value to untriaged when clicking untriaged button', async () => {
       triageSk.value = 'positive'; // Untriaged by default; change value first.
-      $$('button.untriaged', triageSk).click();
+      clickButton(triageSk,'untriaged');
       expectValueAndToggledButtonToBe(triageSk, 'untriaged');
       expect((await changeEvent).detail).to.equal('untriaged');
     });
@@ -70,17 +74,20 @@
     it('does not emit event "change" when clicking button for current value',
       async () => {
         const noChangeEvent = noEventPromise('change');
-        $$('button.untriaged', triageSk).click();
+        clickButton(triageSk,'untriaged');
         await noChangeEvent;
       });
   });
 });
 
-const expectValueAndToggledButtonToBe = (triageSk, value) => {
+const clickButton = (triageSk: TriageSk, value: Label) =>
+    triageSk.querySelector<HTMLButtonElement>(`button.${value}`)!.click();
+
+const expectValueAndToggledButtonToBe = (triageSk: TriageSk, value: LabelOrEmpty) => {
   expect(triageSk.value).to.equal(value);
   if (value === '') {
-    expect($('button.selected', triageSk)).to.have.length(0);
+    expect(triageSk.querySelectorAll('button.selected')).to.have.length(0);
   } else {
-    expect($$(`button.${value}`, triageSk).className).to.contain('selected');
+    expect(triageSk.querySelector(`button.${value}`)!.className).to.contain('selected');
   }
 };