diff --git a/src/server/report/app.js b/src/server/report/app.js index cae405c7..cc43e9d7 100644 --- a/src/server/report/app.js +++ b/src/server/report/app.js @@ -1,11 +1,11 @@ import './button.js'; import { COMMON_STYLE, FILTER_STATUS, FULL_MODE, LAYOUTS, renderEmpty, renderTestStatus } from './common.js'; import { css, html, LitElement, nothing } from 'lit'; +import { ICON_NEXT, ICON_PREV } from './icons.js'; import { RADIO_STYLE, renderRadio } from './radio.js'; import { renderBrowserResults, RESULT_STYLE } from './result.js'; import { renderTabButtons, renderTabPanel, TAB_STYLE } from './tabs.js'; import data from './data.js'; -import { ICON_HOME } from './icons.js'; import page from 'page'; class App extends LitElement { @@ -15,33 +15,30 @@ class App extends LitElement { _filterTest: { state: true }, _fullMode: { state: true }, _layout: { state: true }, - _overlay: { state: true }, - _selectedBrowserIndex: { state: true } + _overlay: { state: true } }; static styles = [COMMON_STYLE, RADIO_STYLE, RESULT_STYLE, TAB_STYLE, css` - .container { + :host { display: grid; - grid-auto-flow: row; - grid-template-areas: "sidebar content"; - grid-template-columns: 300px auto; + grid-template-columns: 275px auto; + height: 100vh; + overflow: hidden; } aside { background-color: #fff; border-right: 1px solid #e6e6e6; box-shadow: 0 0 6px rgba(0,0,0,.07); - box-sizing: border-box; - grid-area: sidebar; - height: 100vh; - position: sticky; - top: 0; - z-index: 10; - } - aside > div { - padding: 10px; + padding: 20px; } main { background-color: #fafafa; - grid-area: content; + overflow-y: scroll; + } + main > .item-container:first-child { + padding-top: 20px; + } + .list-file-title { + padding-bottom: 20px; } table { background-color: #ffffff; @@ -49,7 +46,7 @@ class App extends LitElement { width: 100%; } td, th { - border: 1px solid #dfe6ef; + border: 1px solid #cdd5dc; padding: 10px; text-align: center; } @@ -68,8 +65,8 @@ class App extends LitElement { } fieldset { border: none; - margin-inline: 0; - padding-inline: 0; + margin: 20px 0; + padding: 0; } fieldset > legend { font-weight: bold; @@ -77,40 +74,37 @@ class App extends LitElement { .test-results { display: grid; grid-template-rows: auto 1fr auto; - grid-template-areas: - 'header' - 'content'; height: 100vh; } .header { background-color: #f0f0f0; border-bottom: 1px solid #cdd5dc; box-shadow: 0 0 6px rgba(0,0,0,.07); - grid-area: header; } .tab-panels { - grid-area: content; overflow: auto; } .title { align-items: center; display: flex; + font-size: 1.5rem; + gap: 5px; padding: 20px 20px 0 20px; } .title h2 { - margin: 0; - } - .title-info { - flex: 1 0 auto; - } - .title-navigation { - flex: 0 0 auto; + font-size: inherit; } .settings { align-items: center; display: flex; padding: 20px; - gap: 20px; + gap: 10px; + } + .settings > * { + flex: 0 0 auto; + } + .settings > .spacer { + flex: 1 1 auto; } .settings-box { background-color: #ffffff; @@ -145,6 +139,7 @@ class App extends LitElement { } else { this._filterFile = undefined; this._filterTest = undefined; + this._selectedBrowserIndex = -1; } if (searchParams.has('status')) { let filterStatus = searchParams.get('status'); @@ -160,20 +155,15 @@ class App extends LitElement { } render() { return html` -
- -
${this._renderMainView()}
-
+ +
${this._renderMainView()}
`; } - _handleBackClick() { - this._updateSearchParams({ file: undefined, test: undefined }); - } _handleFilterBrowserChange(e) { const browsers = data.browsers.map(b => b.name).filter(b => { if (b === e.target.value) { @@ -187,13 +177,15 @@ class App extends LitElement { _handleFilterStatusChange(e) { this._updateSearchParams({ status: e.target.value }); } - _handleListTestClick(e) { - this._updateSearchParams({ file: e.target.dataset.file, test: e.target.dataset.test }); - return false; + _handleNextClick() { + this._updateSearchParams(this._next); } _handleOverlayChange(e) { this._overlay = e.target.checked; } + _handlePrevClick() { + this._updateSearchParams(this._prev); + } _renderError(message, source) { return html`

${message}: ${source}.

`; } @@ -248,18 +240,22 @@ class App extends LitElement { searchParams.set('file', file.name); searchParams.delete('test'); return html` -

${file.name}

- - - - - ${data.browsers.map(b => renderBrowserCell(b))} - - - - ${file.tests.map(t => this._renderListFileTest(file, t))} - -
Test
+
+
+

${file.name}

+
+ + + + + ${data.browsers.map(b => renderBrowserCell(b))} + + + + ${file.tests.map(t => this._renderListFileTest(file, t))} + +
Test
+
`; } _renderListFileTest(file, test) { @@ -275,7 +271,7 @@ class App extends LitElement { searchParams.set('test', test.name); return html` - ${test.name} + ${test.name} ${results} `; @@ -283,13 +279,11 @@ class App extends LitElement { _renderMainView() { if (this._filterFile === undefined) { - let list; if (this._files.length === 0) { return renderEmpty(); } else { - list = this._files.map(f => this._renderListFile(f)); + return this._files.map(f => this._renderListFile(f)); } - return html`
${list}
`; } const fileData = this._files.find(f => f.name === this._filterFile); @@ -312,6 +306,7 @@ class App extends LitElement { return renderTabButtons('browser results', tabs, index => { this._selectedBrowserIndex = index; this.shadowRoot.querySelector('.tab-panels').scrollTo(0, 0); + this.requestUpdate(); }); } _renderTabPanels(tabs) { @@ -349,9 +344,13 @@ class App extends LitElement { }); }); - const selectedBrowser = browsers[this._selectedBrowserIndex] || - browsers.find(b => browserResults.get(b.name) < tests.length) || - browsers[0]; + if (!browsers[this._selectedBrowserIndex]) { + this._selectedBrowserIndex = Math.max( + browsers.findIndex((b) => browserResults.get(b.name) < tests.length), + 0 + ); + } + const selectedBrowser = browsers[this._selectedBrowserIndex]; const tabs = browsers.map((b) => { const numPassed = browserResults.get(b.name); @@ -364,21 +363,25 @@ class App extends LitElement { }; }); + const homeSearchParams = new URLSearchParams(window.location.search); + homeSearchParams.delete('file'); + homeSearchParams.delete('test'); + return html`
-
-

${fileData.name}

-
-
- ${ICON_HOME} -
+ Home +  >  +

${fileData.name}

${renderRadio('layout', this._layout, (val) => this._layout = val, [LAYOUTS.FULL, LAYOUTS.SPLIT])} ${fullMode} +
+ ${ICON_PREV} + ${ICON_NEXT}
${this._renderTabButtons(tabs)}
@@ -391,6 +394,12 @@ class App extends LitElement { const files = []; let foundFilterTest = false; + let lookingForNextFile = false; + let lookingForNextTest = false; + let prevFile, prevTest; + + this._next = undefined; + this._prev = undefined; data.files.forEach(f => { const tests = []; @@ -403,13 +412,31 @@ class App extends LitElement { !r.passed && this._filterStatus === FILTER_STATUS.FAILED)) numStatusMatch++; }); if (numStatusMatch > 0) { - tests.push(t); - if (t.name === this._filterTest) { + if (lookingForNextTest) { + lookingForNextTest = false; + this._next = { file: f.name, test: t.name }; + } + if (t.name === this._filterTest && f.name === this._filterFile) { foundFilterTest = true; + lookingForNextTest = true; + this._prev = prevTest; } + prevTest = { file: f.name, test: t.name }; + tests.push(t); } }); if (tests.length > 0) { + if (this._filterTest === undefined) { + if (lookingForNextFile) { + lookingForNextFile = false; + this._next = { file: f.name }; + } + if (f.name === this._filterFile) { + lookingForNextFile = true; + this._prev = prevFile; + } + } + prevFile = { file: f.name }; files.push({ ...f, tests }); } }); diff --git a/src/server/report/button.js b/src/server/report/button.js index 3df083c3..0cc4707c 100644 --- a/src/server/report/button.js +++ b/src/server/report/button.js @@ -2,6 +2,7 @@ import { css, html, LitElement } from 'lit'; class Button extends LitElement { static properties = { + disabled: { type: Boolean }, text: { type: String } }; static styles = [css` @@ -10,7 +11,6 @@ class Button extends LitElement { } button { align-items: center; - background-color: #ffffff; border: 1px solid #cdd5dc; border-radius: 5px; cursor: pointer; @@ -22,6 +22,11 @@ class Button extends LitElement { padding: 10px; user-select: none; } + button, + button[disabled]:hover { + background-color: #ffffff; + color: #6e7477; + } button:hover, button:focus { background-color: #007bff; @@ -31,9 +36,17 @@ class Button extends LitElement { border-color: #007bff; box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px #007bff; } + button[disabled] { + cursor: default; + opacity: 0.5; + } `]; + constructor() { + super(); + this.disabled = false; + } render() { - return html``; + return html``; } } diff --git a/src/server/report/common.js b/src/server/report/common.js index c3c2677f..abf12ec9 100644 --- a/src/server/report/common.js +++ b/src/server/report/common.js @@ -3,6 +3,18 @@ import { ICON_EMPTY, ICON_FULL, ICON_SPLIT } from './icons.js'; import { classMap } from 'lit/directives/class-map.js'; export const COMMON_STYLE = css` + a, a:link, a:visited { + color: #007bff; + } + a:focus, a:hover { + color: #004489; + text-decoration: underline; + } + a:focus-visible { + border-radius: 3px; + outline: 2px solid #007bff; + outline-offset: 1px; + } .empty { align-items: center; display: flex; @@ -20,6 +32,16 @@ export const COMMON_STYLE = css` font-weight: bold; margin: 0; } + h1, h2 { + margin: 0; + } + .item-container { + border-bottom: 4px solid #e3e9f1; + padding: 40px 20px; + } + .item-container:last-of-type { + border-bottom: none; + } .pass { color: #46a661; } diff --git a/src/server/report/icons.js b/src/server/report/icons.js index c42f932a..b2a0563b 100644 --- a/src/server/report/icons.js +++ b/src/server/report/icons.js @@ -6,18 +6,18 @@ export const ICON_EMPTY = svg` `; -export const ICON_HOME = svg` - - - - `; - export const ICON_FULL = svg` `; +export const ICON_NEXT = svg` + + + +`; + export const ICON_NO_GOLDEN = svg` @@ -31,6 +31,12 @@ export const ICON_NO_GOLDEN = svg` `; +export const ICON_PREV = svg` + + + +`; + export const ICON_SPLIT = svg` diff --git a/src/server/report/result.js b/src/server/report/result.js index aad2e5c5..aa91dd72 100644 --- a/src/server/report/result.js +++ b/src/server/report/result.js @@ -18,10 +18,6 @@ export const RESULT_STYLE = css` font-size: 1.2rem; font-weight: bold; } - .result-container { - border-bottom: 4px solid #e3e9f1; - padding: 40px 20px; - } .result-split { flex-direction: row; flex-wrap: nowrap; @@ -173,7 +169,7 @@ export function renderBrowserResults(browser, tests, options) { } return acc.push(html` -
+

${t.name}

${renderStatusText(`${resultData.duration}ms`, status)}