/** * @license * Copyright 2021 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import {FunctionComponent} from 'preact'; import {ReportUtils} from '../../../report/renderer/report-utils.js'; import {Separator} from '../common'; import {CategoryScore} from '../wrappers/category-score'; import {useI18n, useStringFormatter, useLocalizedStrings} from '../i18n/i18n'; import {Markdown} from '../wrappers/markdown'; import type {UIStringsType} from '../i18n/ui-strings'; const MAX_TOOLTIP_AUDITS = 2; type ScoredAuditRef = LH.ReportResult.AuditRef & {result: {score: number}}; function getGatherModeLabel(gatherMode: LH.Result.GatherMode, strings: UIStringsType) { switch (gatherMode) { case 'navigation': return strings.navigationReport; case 'timespan': return strings.timespanReport; case 'snapshot': return strings.snapshotReport; } } function getCategoryRating(rating: string, strings: UIStringsType) { switch (rating) { case 'pass': return strings.ratingPass; case 'average': return strings.ratingAverage; case 'fail': return strings.ratingFail; case 'error': return strings.ratingError; } } function getScoreToBeGained(audit: ScoredAuditRef): number { return audit.weight * (1 - audit.result.score); } const SummaryTooltipAudit: FunctionComponent<{audit: LH.ReportResult.AuditRef}> = ({audit}) => { const rating = ReportUtils.calculateRating(audit.result.score, audit.result.scoreDisplayMode); return (
); }; const SummaryTooltipAudits: FunctionComponent<{category: LH.ReportResult.Category}> = ({category}) => { const strings = useLocalizedStrings(); function isRelevantAudit(audit: LH.ReportResult.AuditRef): audit is ScoredAuditRef { return audit.result.score !== null && // Metrics should not be displayed in this group. audit.group !== 'metrics' && // Audits in group "hidden" should not be counted. audit.group !== 'hidden' && // We don't want unweighted audits except for performance diagnostics. (audit.weight > 0 || audit.group === 'diagnostics') && // Passing audits should never be high impact. !ReportUtils.showAsPassed(audit.result); } const audits = category.auditRefs .filter(isRelevantAudit) .sort((a, b) => { // Remaining score should always be 0 for perf opportunities because weight is 0. // In that case, we want to sort by `overallSavingsMs`. const remainingScoreA = getScoreToBeGained(a); const remainingScoreB = getScoreToBeGained(b); if (remainingScoreA !== remainingScoreB) return remainingScoreB - remainingScoreA; if (a.result.score !== b.result.score) return a.result.score - b.result.score; // TODO: Sort using overall impact from metric savings, not just LCP const aLcpSavings = a.result.metricSavings?.LCP || 0; const bLcpSavings = b.result.metricSavings?.LCP || 0; return bLcpSavings - aLcpSavings; }) .splice(0, MAX_TOOLTIP_AUDITS); if (!audits.length) return null; return (
{strings.highestImpact}
{ audits.map(audit => ) }
); }; const SummaryTooltip: FunctionComponent<{ category: LH.ReportResult.Category, gatherMode: LH.Result.GatherMode, url: string, }> = ({category, gatherMode, url}) => { const strings = useLocalizedStrings(); const str_ = useStringFormatter(); const { numPassed, numPassableAudits, numInformative, totalWeight, } = ReportUtils.calculateCategoryFraction(category); const i18n = useI18n(); const displayAsFraction = ReportUtils.shouldDisplayAsFraction(gatherMode); const score = displayAsFraction ? numPassed / numPassableAudits : category.score; const rating = score === null ? 'error' : ReportUtils.calculateRating(score); return (
{getGatherModeLabel(gatherMode, strings)}
{url}
{category.title}
{ totalWeight !== 0 &&
{getCategoryRating(rating, strings)} { !displayAsFraction && category.score !== null && <> ยท {i18n.formatter.formatInteger(category.score * 100)} }
}
{str_(strings.passedAuditCount, {numPassed})} / {str_(strings.passableAuditCount, {numPassableAudits})}
{numInformative !== 0 &&
{str_(strings.informativeAuditCount, {numInformative})}
}
); }; const SummaryCategory: FunctionComponent<{ category: LH.ReportResult.Category|undefined, href: string, gatherMode: LH.Result.GatherMode, finalDisplayedUrl: string, }> = ({category, href, gatherMode, finalDisplayedUrl}) => { return (
{ category ?
:
}
); }; export { SummaryTooltip, SummaryCategory, };