[gold] Add triage-sk lit-html component.
Bug: skia:9525
Change-Id: I7ccecfb019a43a939c99f813f623381a6c0df0ae
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/265150
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.js
new file mode 100644
index 0000000..f787c43
--- /dev/null
+++ b/golden/modules/triage-sk/index.js
@@ -0,0 +1,2 @@
+import './triage-sk.scss'
+import './triage-sk.js'
diff --git a/golden/modules/triage-sk/triage-sk-demo.html b/golden/modules/triage-sk/triage-sk-demo.html
new file mode 100644
index 0000000..545f35d
--- /dev/null
+++ b/golden/modules/triage-sk/triage-sk-demo.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>triage-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">
+ <style>
+ #event-log-container {
+ border: 1px solid lightgrey;
+ margin: 5em 1em 1em;
+ padding: 1em;
+ }
+ #event-log {
+ font-family: monospace;
+ }
+ </style>
+</head>
+<body>
+<div id=container>
+ <!-- A triage-sk element will be inserted here dynamically. -->
+</div>
+
+<div id=event-log-container>
+ <p><strong>Event log</strong></p>
+ <textarea id=event-log readonly rows=30 cols=120></textarea>
+</div>
+</body>
+</html>
diff --git a/golden/modules/triage-sk/triage-sk-demo.js b/golden/modules/triage-sk/triage-sk-demo.js
new file mode 100644
index 0000000..fc492c4
--- /dev/null
+++ b/golden/modules/triage-sk/triage-sk-demo.js
@@ -0,0 +1,19 @@
+import './index.js';
+import { $$ } from 'common-sk/modules/dom';
+import { isPuppeteerTest } from '../demo_util';
+
+const logEventDetail = (e) => {
+ const log = $$("#event-log");
+ const entry = `${new Date().toISOString()}\t${e.detail}\n`
+ log.value = entry + log.value;
+};
+
+const triageSk = document.createElement('triage-sk');
+triageSk.addEventListener('change', logEventDetail);
+$$('#container').append(triageSk);
+
+// 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.js b/golden/modules/triage-sk/triage-sk.js
new file mode 100644
index 0000000..4b23535
--- /dev/null
+++ b/golden/modules/triage-sk/triage-sk.js
@@ -0,0 +1,75 @@
+/**
+ * @module modules/triage-sk
+ * @description <h2><code>triage-sk</code></h2>
+ *
+ * A custom element that allows labeling a digest as positive, negative or
+ * untriaged.
+ *
+ * @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 { ElementSk } from '../../../infra-sk/modules/ElementSk';
+import { html } from 'lit-html';
+import { classMap } from 'lit-html/directives/class-map.js';
+
+const POSITIVE = 'positive';
+const NEGATIVE = 'negative';
+const UNTRIAGED = 'untriaged';
+
+const template = (el) => html`
+ <button class=${classMap({'positive': true,
+ 'selected': el.value === POSITIVE})}
+ @click=${() => el._buttonClicked(POSITIVE)}>
+ <check-circle-icon-sk></check-circle-icon-sk>
+ </button>
+ <button class=${classMap({'negative': true,
+ 'selected': el.value === NEGATIVE})}
+ @click=${() => el._buttonClicked(NEGATIVE)}>
+ <cancel-icon-sk></cancel-icon-sk>
+ </button>
+ <button class=${classMap({'untriaged': true,
+ 'selected': el.value === UNTRIAGED})}
+ @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 (![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
new file mode 100644
index 0000000..b93b358
--- /dev/null
+++ b/golden/modules/triage-sk/triage-sk.scss
@@ -0,0 +1,32 @@
+triage-sk {
+ display: inline-block;
+
+ button {
+ min-width: 0;
+ padding: 0.6em;
+
+ &.selected {
+ background-color: var(--dark-white);
+ border: none;
+ box-shadow: none;
+ }
+
+ &.positive {
+ &.selected check-circle-icon-sk {
+ fill: #1B9E77;
+ }
+ }
+
+ &.negative {
+ &.selected cancel-icon-sk {
+ fill: #E7298A;
+ }
+ }
+
+ &.untriaged {
+ &.selected help-icon-sk {
+ fill: #A6761D;
+ }
+ }
+ }
+}
diff --git a/golden/modules/triage-sk/triage-sk_test.js b/golden/modules/triage-sk/triage-sk_test.js
new file mode 100644
index 0000000..c3b617e
--- /dev/null
+++ b/golden/modules/triage-sk/triage-sk_test.js
@@ -0,0 +1,85 @@
+import './index.js';
+import { eventPromise, noEventPromise } from '../test_util';
+import { $$ } from 'common-sk/modules/dom';
+
+describe('triage-sk', function() {
+ let triageSk;
+
+ beforeEach(() => {
+ triageSk = document.createElement('triage-sk');
+ document.body.appendChild(triageSk);
+ });
+
+ afterEach(() => {
+ // Remove the stale instance under test.
+ if (triageSk) {
+ document.body.removeChild(triageSk);
+ triageSk = null;
+ }
+ });
+
+ it('is untriaged by default', () => {
+ expectValueAndToggledButtonToBe(triageSk, 'untriaged');
+ });
+
+ describe('"value" property setter/getter', () => {
+ it('sets and gets value via property', () => {
+ triageSk.value = 'positive';
+ expectValueAndToggledButtonToBe(triageSk, 'positive');
+
+ triageSk.value = 'negative';
+ expectValueAndToggledButtonToBe(triageSk, 'negative');
+
+ triageSk.value = 'untriaged';
+ expectValueAndToggledButtonToBe(triageSk, 'untriaged');
+ });
+
+ it('does not emit event "change" when setting value via property',
+ async () => {
+ const noTriageEvent = noEventPromise('change');
+ triageSk.value = 'positive';
+ await noTriageEvent;
+ });
+
+ it('throws an exception upon an invalid value', () => {
+ expect(() => triageSk.value = 'hello world')
+ .to.throw(RangeError, 'Invalid triage-sk value: "hello world".');
+ });
+ });
+
+ describe('buttons', () => {
+ let changeEvent;
+ beforeEach(() => { changeEvent = eventPromise('change', 100); });
+
+ it('sets value to positive when clicking positive button', async () => {
+ $$('button.positive', triageSk).click();
+ expectValueAndToggledButtonToBe(triageSk, 'positive');
+ expect((await changeEvent).detail).to.equal('positive');
+ });
+
+ it('sets value to negative when clicking negative button', async () => {
+ $$('button.negative', triageSk).click();
+ 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();
+ expectValueAndToggledButtonToBe(triageSk, 'untriaged');
+ expect((await changeEvent).detail).to.equal('untriaged');
+ });
+
+ it('does not emit event "change" when clicking button for current value',
+ async () => {
+ const noChangeEvent = noEventPromise('change');
+ $$('button.untriaged', triageSk).click();
+ await noChangeEvent;
+ });
+ });
+});
+
+const expectValueAndToggledButtonToBe = (triageSk, value) => {
+ expect(triageSk.value).to.equal(value);
+ expect($$(`button.${value}`, triageSk).className).to.contain('selected');
+};
diff --git a/golden/puppeteer-tests/test/triage-sk_puppeteer_test.js b/golden/puppeteer-tests/test/triage-sk_puppeteer_test.js
new file mode 100644
index 0000000..3096a1a
--- /dev/null
+++ b/golden/puppeteer-tests/test/triage-sk_puppeteer_test.js
@@ -0,0 +1,42 @@
+const expect = require('chai').expect;
+const setUpPuppeteerAndDemoPageServer = require('./util').setUpPuppeteerAndDemoPageServer;
+const takeScreenshot = require('./util').takeScreenshot;
+
+describe('triage-sk', function() {
+ setUpPuppeteerAndDemoPageServer(); // Sets up this.page and this.baseUrl.
+
+ beforeEach(async function() {
+ await this.page.goto(`${this.baseUrl}/dist/triage-sk.html`);
+ });
+
+ it('should render the demo page', async function() {
+ expect(await this.page.$$('triage-sk')).to.have.length(1); // Smoke test.
+ });
+
+ describe('screenshots', async function() {
+ it('should be untriaged by default', async function() {
+ const triageSk = await this.page.$('triage-sk');
+ await takeScreenshot(triageSk, 'triage-sk_untriaged');
+ });
+
+ it('should be negative', async function() {
+ await this.page.click('triage-sk button.negative');
+ await this.page.click('body'); // Remove focus from button.
+ const triageSk = await this.page.$('triage-sk');
+ await takeScreenshot(triageSk, 'triage-sk_negative');
+ });
+
+ it('should be positive', async function() {
+ await this.page.click('triage-sk button.positive');
+ await this.page.click('body'); // Remove focus from button.
+ const triageSk = await this.page.$('triage-sk');
+ await takeScreenshot(triageSk, 'triage-sk_positive');
+ });
+
+ it('should be positive, with button focused', async function() {
+ await this.page.click('triage-sk button.positive');
+ const triageSk = await this.page.$('triage-sk');
+ await takeScreenshot(triageSk, 'triage-sk_positive-button-focused');
+ });
+ });
+});