From a4c1005695512fb35aba9a2900a39bcfb3e5ce93 Mon Sep 17 00:00:00 2001 From: Vid Date: Fri, 3 Nov 2023 15:16:01 -0400 Subject: [PATCH] add select for view, documentation view --- .../dashboard/web/package-lock.json | 10 ++ modules/out-review/dashboard/web/package.json | 1 + .../out-review/dashboard/web/rollup.config.js | 16 ++- .../dashboard/web/src/dashboard/index.ts | 3 +- .../web/src/reviews/assets/reviews.ts | 24 ++++- .../dashboard/web/src/reviews/components.ts | 98 ++++++++++++++++--- modules/out-review/src/lib.ts | 7 +- 7 files changed, 133 insertions(+), 26 deletions(-) diff --git a/modules/out-review/dashboard/web/package-lock.json b/modules/out-review/dashboard/web/package-lock.json index a4d1c443..d994475e 100644 --- a/modules/out-review/dashboard/web/package-lock.json +++ b/modules/out-review/dashboard/web/package-lock.json @@ -12,6 +12,7 @@ "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.5", "@types/node": "^20.8.5", + "@types/typescript": "^2.0.0", "lit": "^3.0.0", "rollup": "^4.1.4", "typescript": "^5.2.2" @@ -402,6 +403,15 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.4.tgz", "integrity": "sha512-IDaobHimLQhjwsQ/NMwRVfa/yL7L/wriQPMhw1ZJall0KX6E1oxk29XMDeilW5qTIg5aoiqf5Udy8U/51aNoQQ==" }, + "node_modules/@types/typescript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/typescript/-/typescript-2.0.0.tgz", + "integrity": "sha512-WMEWfMISiJ2QKyk5/dSdgL0ZwP//PZj0jmDU0hMh51FmLq4WIYzjlngsUQZXejQL+QtkXJUOGjb3G3UCvgZuSQ==", + "deprecated": "This is a stub types definition for TypeScript (https://github.com/Microsoft/TypeScript). TypeScript provides its own type definitions, so you don't need @types/typescript installed!", + "dependencies": { + "typescript": "*" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", diff --git a/modules/out-review/dashboard/web/package.json b/modules/out-review/dashboard/web/package.json index 9e55a35e..4001cb97 100644 --- a/modules/out-review/dashboard/web/package.json +++ b/modules/out-review/dashboard/web/package.json @@ -12,6 +12,7 @@ "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "^11.1.5", "@types/node": "^20.8.5", + "@types/typescript": "^2.0.0", "lit": "^3.0.0", "rollup": "^4.1.4", "typescript": "^5.2.2" diff --git a/modules/out-review/dashboard/web/rollup.config.js b/modules/out-review/dashboard/web/rollup.config.js index bcbd3e48..414d6b29 100644 --- a/modules/out-review/dashboard/web/rollup.config.js +++ b/modules/out-review/dashboard/web/rollup.config.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ import typescript from '@rollup/plugin-typescript'; import resolve from '@rollup/plugin-node-resolve'; import litcss from 'rollup-plugin-lit-css'; @@ -10,10 +11,15 @@ const dashboard = { plugins: [litcss(), resolve(), typescript({ outDir: './built/dashboard' })], }; -const reviewer = { +const reviewer = (dir) => ({ input: `./src/reviews/index.ts`, - output: { dir: `built/reviewer/` }, - plugins: [litcss(), resolve(), typescript({ outDir: './built/reviewer' })], -}; + output: { dir }, + plugins: [litcss(), resolve(), typescript({ outDir: dir })], +}); -export default [dashboard, reviewer]; +const builds = [dashboard, reviewer('built/reviewer')]; +// export DASHBOARD_PREVIEW="$HOME/D/withhaibun/haibun-e2e-tests/files/published/built/reviewer" +if (process.env.DASHBOARD_PREVIEW) { + builds.push(reviewer(process.env.DASHBOARD_PREVIEW)); +} +export default builds; diff --git a/modules/out-review/dashboard/web/src/dashboard/index.ts b/modules/out-review/dashboard/web/src/dashboard/index.ts index 97af8f2b..1401af60 100644 --- a/modules/out-review/dashboard/web/src/dashboard/index.ts +++ b/modules/out-review/dashboard/web/src/dashboard/index.ts @@ -20,6 +20,7 @@ export class ReviewOverview extends HTMLElement { render(prData: TPRData | null, reviewData: TTraceHistorySummary[]) { // const prLink = prData ? `${prData.title} (${prData.date})` : 'No latest PR found.'; + const openFrame = window !== top ? 'Open links in a new window to escape this frame.' : ''; const reviewLinks = reviewData.length > 0 ? reviewData.map(review => { const titles = review.features; return `${titles} (${review.date}) ✅ ${review.results?.success} ❌ ${review.results?.fail}`; @@ -29,7 +30,7 @@ export class ReviewOverview extends HTMLElement {

Reviews

${reviewLinks}
- Open links in a new window to escape this frame. + ${openFrame}
`; } diff --git a/modules/out-review/dashboard/web/src/reviews/assets/reviews.ts b/modules/out-review/dashboard/web/src/reviews/assets/reviews.ts index 8c5cc226..fbdf0cc2 100644 --- a/modules/out-review/dashboard/web/src/reviews/assets/reviews.ts +++ b/modules/out-review/dashboard/web/src/reviews/assets/reviews.ts @@ -1,20 +1,33 @@ import { css } from "lit"; -export const controls = css`ul { +export const controls = css`ul { list-style: none; } +.artifact::before, .ok-true::before, .ok-false::before { position: absolute; left: -5px; /* Adjust this value to move it further or closer */ } + +.artifact::before { + content: '📦 '; + position: absolute; + padding-left: 33px; +} + +.artifact-button { + background-color: #FAD575; + border-radius: 4px; +} + .ok-false::before { content: '✕ '; color: red; position: absolute; padding-left: 33px; } -.ok-true::before { +.ok-true:not(.stepper-prose):not(.stepper-feature)::before { content: '✓ '; color: green; position: absolute; @@ -46,6 +59,11 @@ h2 { .code { font-family: monospace; white-space: pre-wrap; -} +}`; +export const documentation = css` + ::part(review-step) { + line-height: 1.5em; + margin-top: .5em; + } `; \ No newline at end of file diff --git a/modules/out-review/dashboard/web/src/reviews/components.ts b/modules/out-review/dashboard/web/src/reviews/components.ts index bc5d50bb..8460de8f 100644 --- a/modules/out-review/dashboard/web/src/reviews/components.ts +++ b/modules/out-review/dashboard/web/src/reviews/components.ts @@ -2,9 +2,9 @@ import { LitElement, html, css, TemplateResult, nothing } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; -import { controls } from './assets/reviews.js'; +import { controls, documentation } from './assets/reviews.js'; -import { TFoundHistories, THistoryWithMeta, findArtifacts, asArtifact, asActionResult } from '@haibun/out-review/build/lib.js'; +import { TFoundHistories, THistoryWithMeta, findArtifacts, asArtifact, asActionResult, actionName } from '@haibun/out-review/build/lib.js'; import { TWindowRouter } from './router.js'; import { TLogHistoryWithArtifact, TLogHistory, TArtifactMessageContext, TArtifact } from '@haibun/core/build/lib/interfaces/logger.js'; @@ -29,12 +29,13 @@ export class ReviewsGroups extends LitElement { return html``; } } - +const views = ['results', 'everything', 'documentation'] as const; +type TView = typeof views[number]; @customElement('a-review') export class AReview extends LitElement { @property({ type: Object }) reviewLD?: THistoryWithMeta; @property({ type: Object }) detail?: object; - @property({ type: Boolean }) showDetails = false; + @property({ type: String }) view: TView = 'results'; static styles = [controls, css`.review-body { display: flex; @@ -53,26 +54,55 @@ export class AReview extends LitElement { this.artifacts = (findArtifacts(this.reviewLD) || []); this.videoOverview = this.artifacts.find(a => a.messageContext.artifact.type === 'video' && a.messageContext.topic.event === 'summary'); this.videoDetail(); + this.initializeFromCookie(); await super.connectedCallback(); } + currentFilter = (h: TLogHistory) => { + if (this.view === 'everything') { + return true; + } + if (this.view === 'results') { + return (asActionResult(h) || (asArtifact(h) && asArtifact(h)?.messageContext?.topic?.event !== 'debug')); + } + const action = asActionResult(h); + if (action) { + const { actionName, stepperName } = action.messageContext.topic.step.actions[0]; + // FIXME this should be mapped to something like log level + if (!['set', 'setAll'].includes(actionName) && !['WebServerStepper'].includes(stepperName)) { + return true; + } + return false; + } + return (asArtifact(h) && asArtifact(h)?.messageContext?.topic?.event !== 'debug'); + }; render() { - const currentFilter = (h: TLogHistory) => this.showDetails ? h : (asActionResult(h) || (asArtifact(h) && asArtifact(h)?.messageContext?.topic?.event !== 'debug')); + const viewStyle = this.view === 'documentation' ? html`` : nothing; if (!this.reviewLD) { return html`

No data

`; } - const checkbox = html` this.showDetails = (e.target).checked} />`; + const chooseView = html` + +`; + return this.reviewLD && html` + ${viewStyle}

${this.reviewLD.meta.feature}

- ${(this.reviewLD.logHistory).filter(currentFilter).map(h => { - return html`>` + ${(this.reviewLD.logHistory).filter(this.currentFilter).map(h => { + return html` .view=${this.view}>` })}
- ${checkbox} + View ${chooseView} ${this.detail}
@@ -87,12 +117,44 @@ export class AReview extends LitElement { const content = getDetailContent(this.videoOverview?.messageContext.artifact); this.detail = html`${content}`; } + + updated(changedProperties: Map) { + if (changedProperties.has('view')) { + this.saveToCookie(); + } + } + + initializeFromCookie() { + const cookieValue = this.getCookie('view'); + console.log('cookie ', cookieValue) + if (cookieValue !== null) { + this.view = cookieValue; + } + } + + saveToCookie() { + console.log('saving cookie', this.view) + document.cookie = `view=${this.view};path=/;max-age=31536000`; // Expires in 1 year + } + + getCookie(name: string): string | null { + const nameEQ = name + "="; + const ca = document.cookie.split(';'); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); + } + return null; + } } @customElement('review-step') export class ReviewStep extends LitElement { - static styles = [controls]; @property({ type: Array }) logHistory?: TLogHistory; + @property({ type: Boolean }) showLogLevel = true; + + static styles = [controls]; render() { const { logHistory } = this; @@ -102,19 +164,25 @@ export class ReviewStep extends LitElement { if (logHistory === undefined) { return html`
No history
`; } - let okResult: string | symbol = nothing; - okResult = executorResult ? `ok-${executorResult?.messageContext.topic.result.ok}` : nothing; + const okClasses = [`stepper-${actionName(logHistory)}`]; + const result = executorResult?.messageContext.topic.result.ok; + if (result !== undefined) { + okClasses.push(`ok-${result}`); + } else if (logArtifact !== undefined) { + okClasses.push('artifact'); + } const message = executorResult ? executorResult.messageContext.topic.step.in : logHistory.message; - const loggerDisplay = executorResult ? nothing : this.loggerButton(logHistory.level); + const loggerDisplay = (!this.showLogLevel || executorResult) ? nothing : this.loggerButton(logHistory.level); const detailButton = logArtifact && this.reportDetail(logArtifact.messageContext); - return html`
${loggerDisplay} ${message} ${detailButton}
` + const actionClass = 'stepper-' + (actionName(logHistory) || 'unknown'); + return html`
${loggerDisplay} ${message} ${detailButton}
` } selectMessage() { this.showDetail(html`
${JSON.stringify(this.logHistory, null, 2)}
`) } reportDetail(artifactContext: TArtifactMessageContext) { const content = getDetailContent(artifactContext.artifact); - return html``; + return html``; } showDetail(html: TemplateResult) { this.dispatchEvent(new CustomEvent('show-detail', { detail: html })); diff --git a/modules/out-review/src/lib.ts b/modules/out-review/src/lib.ts index 773cdb28..ab87aa13 100644 --- a/modules/out-review/src/lib.ts +++ b/modules/out-review/src/lib.ts @@ -45,7 +45,10 @@ export function asArtifact(logHistory: TLogHistory | undefined): TLogHistoryWith } export function asStepperActionType(logHistory: TLogHistory | undefined, stepperType: string): TLogHistoryWithExecutorTopic | undefined { - const asTopic = logHistory; - const ret = (asTopic.messageContext?.topic?.step?.actions[0].actionName === stepperType) ? logHistory : undefined; + const ret = (actionName(logHistory) === stepperType) ? logHistory : undefined; return ret; } + +export const actionName = (logHistory: TLogHistory | undefined) => { + return (logHistory).messageContext?.topic?.step?.actions[0].actionName; +} \ No newline at end of file