QA review tooltip + comment improvements (#1779)
Resolves https://github.com/webrecorder/browsertrix/issues/1761 <!-- Fixes #issue_number --> ### Changes - Switches QA review page chip tooltip colors to dark text on light background - Shows newest comment when hovering over comment cell in QA tab page list to match tooltip in QA review page list - Validates textarea max entry for description and comments
This commit is contained in:
parent
26abbddd62
commit
e7dbf914a3
@ -98,6 +98,6 @@ export const pageDetails = (page: ArchivedItemQAPage) =>
|
|||||||
name="chat-square-text-fill"
|
name="chat-square-text-fill"
|
||||||
class="mr-2 h-4 w-4 flex-none text-blue-600"
|
class="mr-2 h-4 w-4 flex-none text-blue-600"
|
||||||
></sl-icon>
|
></sl-icon>
|
||||||
${page.notes[page.notes.length - 1]?.text}
|
${page.notes[page.notes.length - 1].text}
|
||||||
</div>`
|
</div>`
|
||||||
: nothing}`;
|
: nothing}`;
|
||||||
|
@ -138,8 +138,10 @@ export class QaPage extends TailwindElement {
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
aria-selected=${this.selected}
|
aria-selected=${this.selected}
|
||||||
>
|
>
|
||||||
<sl-tooltip placement="left" hoist>
|
<sl-tooltip class="invert-tooltip" placement="left" hoist>
|
||||||
<div slot="content" class="text-xs">${pageDetails(page)}</div>
|
<div slot="content" class="max-w-60 text-xs">
|
||||||
|
${pageDetails(page)}
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="absolute -left-4 top-[50%] flex w-8 translate-y-[-50%] flex-col place-items-center gap-1 rounded-full border border-gray-300 bg-neutral-0 p-2 leading-[14px] shadow transition-transform hover:scale-110"
|
class="absolute -left-4 top-[50%] flex w-8 translate-y-[-50%] flex-col place-items-center gap-1 rounded-full border border-gray-300 bg-neutral-0 p-2 leading-[14px] shadow transition-transform hover:scale-110"
|
||||||
>
|
>
|
||||||
|
@ -762,19 +762,31 @@ export class ArchivedItemDetailQA extends TailwindElement {
|
|||||||
<btrix-table-cell
|
<btrix-table-cell
|
||||||
>${this.renderApprovalStatus(page)}</btrix-table-cell
|
>${this.renderApprovalStatus(page)}</btrix-table-cell
|
||||||
>
|
>
|
||||||
<btrix-table-cell
|
<btrix-table-cell>
|
||||||
>${page.notes?.length
|
${page.notes?.length
|
||||||
? statusWithIcon(
|
? html`
|
||||||
|
<sl-tooltip class="invert-tooltip">
|
||||||
|
<div slot="content">
|
||||||
|
<div class="text-xs text-neutral-400">
|
||||||
|
${msg("Newest comment:")}
|
||||||
|
</div>
|
||||||
|
<div class="leading04 max-w-60 text-xs">
|
||||||
|
${page.notes[page.notes.length - 1].text}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${statusWithIcon(
|
||||||
html`<sl-icon
|
html`<sl-icon
|
||||||
name="chat-square-text-fill"
|
name="chat-square-text-fill"
|
||||||
class="text-blue-600"
|
class="text-blue-600"
|
||||||
></sl-icon>`,
|
></sl-icon>`,
|
||||||
`${page.notes.length.toLocaleString()} ${pluralOf("comments", page.notes.length)}`,
|
`${page.notes.length.toLocaleString()} ${pluralOf("comments", page.notes.length)}`,
|
||||||
)
|
)}
|
||||||
: html`<span class="text-neutral-400"
|
</sl-tooltip>
|
||||||
>${msg("None")}</span
|
`
|
||||||
>`}</btrix-table-cell
|
: html`<span class="text-neutral-400">
|
||||||
>
|
${msg("None")}
|
||||||
|
</span>`}
|
||||||
|
</btrix-table-cell>
|
||||||
</btrix-table-row>
|
</btrix-table-row>
|
||||||
`,
|
`,
|
||||||
)}
|
)}
|
||||||
|
@ -43,6 +43,7 @@ import type { ArchivedItem, ArchivedItemPageComment } from "@/types/crawler";
|
|||||||
import type { ArchivedItemQAPage, QARun } from "@/types/qa";
|
import type { ArchivedItemQAPage, QARun } from "@/types/qa";
|
||||||
import { type AuthState } from "@/utils/AuthService";
|
import { type AuthState } from "@/utils/AuthService";
|
||||||
import { finishedCrawlStates, isActive, renderName } from "@/utils/crawler";
|
import { finishedCrawlStates, isActive, renderName } from "@/utils/crawler";
|
||||||
|
import { maxLengthValidator } from "@/utils/form";
|
||||||
import { formatISODateString, getLocale } from "@/utils/localization";
|
import { formatISODateString, getLocale } from "@/utils/localization";
|
||||||
|
|
||||||
const DEFAULT_PAGE_SIZE = 100;
|
const DEFAULT_PAGE_SIZE = 100;
|
||||||
@ -158,6 +159,8 @@ export class ArchivedItemQA extends TailwindElement {
|
|||||||
private readonly notify = new NotifyController(this);
|
private readonly notify = new NotifyController(this);
|
||||||
private readonly replaySwReg =
|
private readonly replaySwReg =
|
||||||
navigator.serviceWorker.getRegistration("/replay/");
|
navigator.serviceWorker.getRegistration("/replay/");
|
||||||
|
private readonly validateItemDescriptionMax = maxLengthValidator(500);
|
||||||
|
private readonly validatePageCommentMax = maxLengthValidator(500);
|
||||||
|
|
||||||
@query("#replayframe")
|
@query("#replayframe")
|
||||||
private readonly replayFrame?: HTMLIFrameElement | null;
|
private readonly replayFrame?: HTMLIFrameElement | null;
|
||||||
@ -581,11 +584,18 @@ export class ArchivedItemQA extends TailwindElement {
|
|||||||
</sl-button>
|
</sl-button>
|
||||||
</btrix-dialog>
|
</btrix-dialog>
|
||||||
|
|
||||||
|
${this.renderReviewDialog()}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderReviewDialog() {
|
||||||
|
const { helpText, validate } = this.validateItemDescriptionMax;
|
||||||
|
return html`
|
||||||
<btrix-dialog
|
<btrix-dialog
|
||||||
class="reviewDialog [--width:60rem]"
|
class="reviewDialog [--width:60rem]"
|
||||||
label=${msg("QA Review")}
|
label=${msg("QA Review")}
|
||||||
>
|
>
|
||||||
<form class="qaReviewForm" @submit=${this.onReviewSubmit}>
|
<form class="qaReviewForm" @submit=${this.onSubmitReview}>
|
||||||
<div class="flex flex-col gap-6 md:flex-row">
|
<div class="flex flex-col gap-6 md:flex-row">
|
||||||
<div>
|
<div>
|
||||||
<sl-radio-group
|
<sl-radio-group
|
||||||
@ -642,7 +652,11 @@ export class ArchivedItemQA extends TailwindElement {
|
|||||||
label=${msg("Update archived item description?")}
|
label=${msg("Update archived item description?")}
|
||||||
name="description"
|
name="description"
|
||||||
value=${this.item?.description ?? ""}
|
value=${this.item?.description ?? ""}
|
||||||
placeholder=${msg("No description")}
|
placeholder=${msg("No description, yet")}
|
||||||
|
rows="10"
|
||||||
|
autocomplete="off"
|
||||||
|
help-text=${helpText}
|
||||||
|
@sl-input=${validate}
|
||||||
></sl-textarea>
|
></sl-textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -720,6 +734,7 @@ export class ArchivedItemQA extends TailwindElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private renderComments() {
|
private renderComments() {
|
||||||
|
const { helpText, validate } = this.validatePageCommentMax;
|
||||||
return html`
|
return html`
|
||||||
${when(
|
${when(
|
||||||
this.page?.notes?.length,
|
this.page?.notes?.length,
|
||||||
@ -770,8 +785,10 @@ export class ArchivedItemQA extends TailwindElement {
|
|||||||
name="pageComment"
|
name="pageComment"
|
||||||
label=${msg("Add a comment")}
|
label=${msg("Add a comment")}
|
||||||
placeholder=${msg("Enter page feedback")}
|
placeholder=${msg("Enter page feedback")}
|
||||||
minlength="1"
|
rows="4"
|
||||||
maxlength="500"
|
autocomplete="off"
|
||||||
|
help-text=${helpText}
|
||||||
|
@sl-input=${validate}
|
||||||
></sl-textarea>
|
></sl-textarea>
|
||||||
</form>
|
</form>
|
||||||
`;
|
`;
|
||||||
@ -941,6 +958,9 @@ export class ArchivedItemQA extends TailwindElement {
|
|||||||
|
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
|
|
||||||
|
const formEl = e.target as HTMLFormElement;
|
||||||
|
if (!(await this.checkFormValidity(formEl))) return;
|
||||||
|
|
||||||
void this.commentDialog?.hide();
|
void this.commentDialog?.hide();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -975,6 +995,11 @@ export class ArchivedItemQA extends TailwindElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async checkFormValidity(formEl: HTMLFormElement) {
|
||||||
|
await this.updateComplete;
|
||||||
|
return !formEl.querySelector("[data-invalid]");
|
||||||
|
}
|
||||||
|
|
||||||
private async deletePageComment(commentId: string): Promise<void> {
|
private async deletePageComment(commentId: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await this.api.fetch(
|
await this.api.fetch(
|
||||||
@ -1293,14 +1318,15 @@ export class ArchivedItemQA extends TailwindElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onReviewSubmit(e: SubmitEvent) {
|
private async onSubmitReview(e: SubmitEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const form = e.currentTarget as HTMLFormElement;
|
const form = e.currentTarget as HTMLFormElement;
|
||||||
const params = serialize(form);
|
const params = serialize(form);
|
||||||
|
|
||||||
if (!params.reviewStatus) {
|
if (!params.reviewStatus) return;
|
||||||
return;
|
|
||||||
}
|
const formEl = e.target as HTMLFormElement;
|
||||||
|
if (!(await this.checkFormValidity(formEl))) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await this.api.fetch<{ updated: boolean }>(
|
const data = await this.api.fetch<{ updated: boolean }>(
|
||||||
|
@ -196,6 +196,18 @@
|
|||||||
min-width: min-content;
|
min-width: min-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Style tooltip with white background */
|
||||||
|
sl-tooltip.invert-tooltip {
|
||||||
|
--sl-tooltip-arrow-size: 0;
|
||||||
|
--sl-tooltip-background-color: var(--sl-color-neutral-0);
|
||||||
|
--sl-tooltip-color: var(--sl-color-neutral-700);
|
||||||
|
}
|
||||||
|
|
||||||
|
sl-tooltip.invert-tooltip::part(body) {
|
||||||
|
outline: 1px solid var(--sl-panel-border-color);
|
||||||
|
box-shadow: var(--sl-shadow-large);
|
||||||
|
}
|
||||||
|
|
||||||
/* For single-input forms with submit button inline */
|
/* For single-input forms with submit button inline */
|
||||||
/* Requires form control and button to be direct children */
|
/* Requires form control and button to be direct children */
|
||||||
.inline-control-input,
|
.inline-control-input,
|
||||||
|
Loading…
Reference in New Issue
Block a user