report: add PWA category badge gauge (#6526)

This commit is contained in:
Brendan Kenny 2018-11-16 17:08:37 -08:00 коммит произвёл GitHub
Родитель 130c5e0126
Коммит 7f55977b2a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 256 добавлений и 13 удалений

Просмотреть файл

@ -46,14 +46,53 @@ class PwaCategoryRenderer extends CategoryRenderer {
return categoryElem;
}
/**
* @param {LH.ReportResult.Category} category
* @return {DocumentFragment}
*/
renderScoreGauge(category) {
// Defer to parent-gauge style if category error.
if (category.score === null) {
return super.renderScoreGauge(category);
}
const tmpl = this.dom.cloneTemplate('#tmpl-lh-gauge--pwa', this.templateContext);
const wrapper = /** @type {HTMLAnchorElement} */ (this.dom.find('.lh-gauge--pwa__wrapper',
tmpl));
wrapper.href = `#${category.id}`;
const allGroups = this._getGroupIds(category.auditRefs);
const passingGroupIds = this._getPassingGroupIds(category.auditRefs);
if (passingGroupIds.size === allGroups.size) {
wrapper.classList.add('lh-badged--all');
} else {
for (const passingGroupId of passingGroupIds) {
wrapper.classList.add(`lh-badged--${passingGroupId}`);
}
}
this.dom.find('.lh-gauge__label', tmpl).textContent = category.title;
return tmpl;
}
/**
* Returns the group IDs found in auditRefs.
* @param {Array<LH.ReportResult.AuditRef>} auditRefs
* @return {Set<string>}
*/
_getGroupIds(auditRefs) {
const groupIds = auditRefs.map(ref => ref.group).filter(/** @return {g is string} */ g => !!g);
return new Set(groupIds);
}
/**
* Returns the group IDs whose audits are all considered passing.
* @param {Array<LH.ReportResult.AuditRef>} auditRefs
* @return {Set<string>}
*/
_getPassingGroupIds(auditRefs) {
const groupIds = auditRefs.map(ref => ref.group).filter(/** @return {g is string} */ g => !!g);
const uniqueGroupIds = new Set(groupIds);
const uniqueGroupIds = this._getGroupIds(auditRefs);
// Remove any that have a failing audit.
for (const auditRef of auditRefs) {

Просмотреть файл

@ -201,15 +201,26 @@ class ReportRenderer {
const categories = reportSection.appendChild(this._dom.createElement('div', 'lh-categories'));
for (const category of report.reportCategories) {
if (scoreHeader) {
scoreHeader.appendChild(categoryRenderer.renderScoreGauge(category));
}
const renderer = specificCategoryRenderers[category.id] || categoryRenderer;
categories.appendChild(renderer.render(category, report.categoryGroups));
}
if (scoreHeader) {
const defaultGauges = [];
const customGauges = [];
for (const category of report.reportCategories) {
const renderer = specificCategoryRenderers[category.id] || categoryRenderer;
const categoryGauge = renderer.renderScoreGauge(category);
// Group gauges that aren't default at the end of the header
if (renderer.renderScoreGauge === categoryRenderer.renderScoreGauge) {
defaultGauges.push(categoryGauge);
} else {
customGauges.push(categoryGauge);
}
}
scoreHeader.append(...defaultGauges, ...customGauges);
const scoreScale = this._dom.cloneTemplate('#tmpl-lh-scorescale', this._templateContext);
this._dom.find('.lh-scorescale-label', scoreScale).textContent =
Util.UIStrings.scorescaleLabel;

Просмотреть файл

@ -79,6 +79,7 @@
--lh-audit-group-vpadding: 8px;
--lh-section-vpadding: 12px;
--chevron-size: 12px;
--circle-size: calc(3 * var(--header-font-size));
--pass-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><title>check</title><path fill="%23178239" d="M24 4C12.95 4 4 12.95 4 24c0 11.04 8.95 20 20 20 11.04 0 20-8.96 20-20 0-11.05-8.96-20-20-20zm-4 30L10 24l2.83-2.83L20 28.34l15.17-15.17L38 16 20 34z"/></svg>');
--average-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><title>info</title><path fill="%23E67700" d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm2 30h-4V22h4v12zm0-16h-4v-4h4v4z"/></svg>');
@ -785,10 +786,15 @@ details, summary {
border: 0;
}
.lh-scores-header .lh-gauge__wrapper {
.lh-scores-header .lh-gauge__wrapper,
.lh-scores-header .lh-gauge--pwa__wrapper, {
margin: 0 4px;
}
.lh-scores-header .lh-gauge--pwa__wrapper {
border-left: 1px solid var(--report-secondary-border-color)
}
.lh-scorescale {
color: var(--medium-75-gray);
padding: 0 calc(var(--section-indent) * 1.5) 0;
@ -835,7 +841,6 @@ details, summary {
}
.lh-category {
--circle-size: calc(2.5 * var(--header-font-size));
padding: var(--section-padding);
}

Просмотреть файл

@ -477,7 +477,6 @@ limitations under the License.
<template id="tmpl-lh-gauge">
<style>
.lh-gauge__wrapper {
--circle-size: calc(3 * var(--header-font-size));
--circle-size-half: calc(var(--circle-size) / 2);
--circle-background: hsl(216, 12%, 92%);
--circle-border-width: 9;
@ -573,6 +572,143 @@ limitations under the License.
</a>
</template>
<!-- Lighthouse PWA badge gauge -->
<template id="tmpl-lh-gauge--pwa">
<style>
.lh-gauge--pwa__wrapper {
display: inline-flex;
align-items: center;
flex-direction: column;
text-decoration: none;
flex: 1;
min-width: auto;
position: relative;
}
.lh-gauge--pwa {
width: var(--circle-size);
height: var(--circle-size);
}
.lh-gauge--pwa .lh-gauge--pwa__component {
display: none;
}
.lh-gauge--pwa__wrapper:not(.lh-badged--all) .lh-gauge--pwa__logo > path {
/* Gray logo unless everything is passing. */
fill: #B0B0B0;
}
/* No passing groups. */
.lh-gauge--pwa__wrapper:not([class*='lh-badged--']) .lh-gauge--pwa__na-line {
display: inline;
}
/* Just fast and reliable. */
.lh-gauge--pwa__wrapper.lh-badged--pwa-fast-reliable:not(.lh-badged--pwa-installable) .lh-gauge--pwa__fast-reliable-badge {
display: inline;
}
/* Just installable. */
.lh-gauge--pwa__wrapper.lh-badged--pwa-installable:not(.lh-badged--pwa-fast-reliable) .lh-gauge--pwa__installable-badge {
display: inline;
}
/* Fast and reliable and installable. */
.lh-gauge--pwa__wrapper.lh-badged--pwa-fast-reliable.lh-badged--pwa-installable .lh-gauge--pwa__fast-reliable-installable-badges {
display: inline;
}
/* All passing groups. */
.lh-gauge--pwa__wrapper.lh-badged--all .lh-gauge--pwa__check-circle {
display: inline;
}
.lh-gauge__label {
font-size: var(--body-font-size);
line-height: var(--body-line-height);
margin-top: calc(0.5 * var(--body-line-height));
text-align: center;
color: black;
}
</style>
<a href="#" class="lh-gauge--pwa__wrapper">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" class="lh-gauge--pwa">
<defs>
<linearGradient id="lh-gauge--pwa__bg-disk__gradient" x1="50%" y1="2.15%" x2="50%" y2="97.645%">
<stop stop-color="#F1F3F4" offset="0%"></stop>
<stop stop-color="#DEE6EA" offset="100%"></stop>
</linearGradient>
<linearGradient id="lh-gauge--pwa__check-circle__gradient" x1="50%" y1="0%" x2="50%" y2="100%">
<stop stop-color="#00C852" offset="0%"></stop>
<stop stop-color="#009688" offset="100%"></stop>
</linearGradient>
<linearGradient id="lh-gauge--pwa__installable__shadow-gradient" x1="76.056%" x2="24.111%" y1="82.995%" y2="24.735%">
<stop stop-color="#A5D6A7" offset="0%"></stop>
<stop stop-color="#80CBC4" offset="100%"></stop>
</linearGradient>
<linearGradient id="lh-gauge--pwa__fast-reliable__shadow-gradient" x1="76.056%" y1="82.995%" x2="25.678%" y2="26.493%">
<stop stop-color="#64B5F6" offset="0%"></stop>
<stop stop-color="#2979FF" offset="100%"></stop>
</linearGradient>
<g id="lh-gauge--pwa__fast-reliable-badge">
<circle fill="#FFFFFF" cx="10" cy="10" r="10"></circle>
<path fill="#304FFE" d="M10 3.58l5.25 2.34v3.5c0 3.23-2.24 6.26-5.25 7-3.01-.74-5.25-3.77-5.25-7v-3.5L10 3.58zm-.47 10.74l2.76-4.83.03-.07c.04-.08 0-.24-.22-.24h-1.64l.47-3.26h-.47l-2.7 4.77c-.02.01.05-.1-.04.05-.09.16-.1.31.18.31h1.63l-.47 3.27h.47z"/>
</g>
<g id="lh-gauge--pwa__installable-badge">
<circle fill="#FFFFFF" cx="10" cy="10" r="10"></circle>
<path fill="#009688" d="M10 4.167A5.835 5.835 0 0 0 4.167 10 5.835 5.835 0 0 0 10 15.833 5.835 5.835 0 0 0 15.833 10 5.835 5.835 0 0 0 10 4.167zm2.917 6.416h-2.334v2.334H9.417v-2.334H7.083V9.417h2.334V7.083h1.166v2.334h2.334v1.166z"/>
</g>
</defs>
<g stroke="none" fill-rule="nonzero">
<!-- Background and PWA logo (color by default) -->
<circle fill="url(#lh-gauge--pwa__bg-disk__gradient)" cx="30" cy="30" r="30"></circle>
<g class="lh-gauge--pwa__logo">
<path fill="#3D3D3D" d="M35.66 19.39l.7-1.75h2L37.4 15 38.6 12l3.4 9h-2.51l-.58-1.61z"/>
<path fill="#304FFE" d="M33.52 21l3.65-9h-2.42l-2.5 5.82L30.5 12h-1.86l-1.9 5.82-1.35-2.65-1.21 3.72L25.4 21h2.38l1.72-5.2 1.64 5.2z"/>
<path fill="#3D3D3D" fill-rule="nonzero" d="M20.3 17.91h1.48c.45 0 .85-.05 1.2-.15l.39-1.18 1.07-3.3a2.64 2.64 0 0 0-.28-.37c-.55-.6-1.36-.91-2.42-.91H18v9h2.3V17.9zm1.96-3.84c.22.22.33.5.33.87 0 .36-.1.65-.29.87-.2.23-.59.35-1.15.35h-.86v-2.41h.87c.52 0 .89.1 1.1.32z"/>
</g>
<!-- No badges. -->
<rect class="lh-gauge--pwa__component lh-gauge--pwa__na-line" fill="#FFFFFF" x="20" y="32" width="20" height="4" rx="2"></rect>
<!-- Just fast and reliable. -->
<g class="lh-gauge--pwa__component lh-gauge--pwa__fast-reliable-badge" transform="translate(20, 29)">
<path fill="url(#lh-gauge--pwa__fast-reliable__shadow-gradient)" d="M33.63 19.49A30 30 0 0 1 16.2 30.36L3 17.14 17.14 3l16.49 16.49z"/>
<use xlink:href="#lh-gauge--pwa__fast-reliable-badge" />
</g>
<!-- Just installable. -->
<g class="lh-gauge--pwa__component lh-gauge--pwa__installable-badge" transform="translate(20, 29)">
<path fill="url(#lh-gauge--pwa__installable__shadow-gradient)" d="M33.629 19.487c-4.272 5.453-10.391 9.39-17.415 10.869L3 17.142 17.142 3 33.63 19.487z"/>
<use xlink:href="#lh-gauge--pwa__installable-badge" />
</g>
<!-- Fast and reliable and installable. -->
<g class="lh-gauge--pwa__component lh-gauge--pwa__fast-reliable-installable-badges">
<g transform="translate(8, 29)"> <!-- fast and reliable -->
<path fill="url(#lh-gauge--pwa__fast-reliable__shadow-gradient)" d="M16.321 30.463L3 17.143 17.142 3l22.365 22.365A29.864 29.864 0 0 1 22 31c-1.942 0-3.84-.184-5.679-.537z"/>
<use xlink:href="#lh-gauge--pwa__fast-reliable-badge" />
</g>
<g transform="translate(32, 29)"> <!-- installable -->
<path fill="url(#lh-gauge--pwa__installable__shadow-gradient)" d="M25.982 11.84a30.107 30.107 0 0 1-13.08 15.203L3 17.143 17.142 3l8.84 8.84z"/>
<use xlink:href="#lh-gauge--pwa__installable-badge" />
</g>
</g>
<!-- Full PWA. -->
<g class="lh-gauge--pwa__component lh-gauge--pwa__check-circle" transform="translate(18, 28)">
<circle fill="#FFFFFF" cx="12" cy="12" r="12"></circle>
<path fill="url(#lh-gauge--pwa__check-circle__gradient)" d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"></path>
</g>
</g>
</svg>
<div class="lh-gauge__label"></div>
</a>
</template>
<!-- Lighthouse crtiical request chains component -->
<template id="tmpl-lh-crc">
<div class="lh-crc-container">

Просмотреть файл

@ -131,10 +131,17 @@ describe('PwaCategoryRenderer', () => {
const categoryElem = pwaRenderer.render(category, sampleResults.categoryGroups);
const badgedElems = categoryElem.querySelectorAll(`.lh-badged`);
const badgedScoreGauge =
categoryElem.querySelector('.lh-gauge--pwa__wrapper[class*="lh-badged--"]');
// Only expect a badge on last permutation (all bits are set).
const expectedBadgeCount = i === totalPermutations - 1 ? 1 : 0;
assert.strictEqual(badgedElems.length, expectedBadgeCount);
// Only expect a badge (and badged gauge) on last permutation (all bits are set).
if (i !== totalPermutations - 1) {
assert.strictEqual(badgedElems.length, 0);
assert.strictEqual(badgedScoreGauge, null);
} else {
assert.strictEqual(badgedElems.length, 1);
assert.ok(badgedScoreGauge.classList.contains(`lh-badged--${targetGroupId}`));
}
}
});
@ -145,6 +152,7 @@ describe('PwaCategoryRenderer', () => {
const categoryElem = pwaRenderer.render(category, sampleResults.categoryGroups);
assert.strictEqual(categoryElem.querySelectorAll('.lh-badged').length, groupIds.length);
assert.ok(categoryElem.querySelector('.lh-gauge--pwa__wrapper.lh-badged--all'));
});
it('renders no badges when no audit groups are passing', () => {
@ -154,6 +162,8 @@ describe('PwaCategoryRenderer', () => {
const categoryElem = pwaRenderer.render(category, sampleResults.categoryGroups);
assert.strictEqual(categoryElem.querySelectorAll('.lh-badged').length, 0);
assert.strictEqual(categoryElem.querySelector('.lh-gauge--pwa__wrapper[class*="lh-badged-"]'),
null);
});
it('renders all but one badge when all groups but one are passing', () => {
@ -164,13 +174,30 @@ describe('PwaCategoryRenderer', () => {
const failingGroupId = auditRefs[0].group;
const categoryElem = pwaRenderer.render(category, sampleResults.categoryGroups);
const gaugeElem = categoryElem.querySelector('.lh-gauge--pwa__wrapper');
for (const groupId of groupIds) {
const expectedCount = groupId === failingGroupId ? 0 : 1;
const groupElems = categoryElem.querySelectorAll(`.lh-audit-group--${groupId}.lh-badged`);
assert.strictEqual(groupElems.length, expectedCount);
gaugeElem.classList.contains(`lh-badged--${groupId}`);
}
});
});
describe('#renderScoreGauge', () => {
it('renders an error score gauge in case of category error', () => {
category.score = null;
const badgeGauge = pwaRenderer.renderScoreGauge(category);
// Not a PWA gauge.
assert.strictEqual(badgeGauge.querySelector('.lh-gauge--pwa__wrapper'), null);
const percentageElem = badgeGauge.querySelector('.lh-gauge__percentage');
assert.strictEqual(percentageElem.textContent, '?');
assert.strictEqual(percentageElem.title, Util.UIStrings.errorLabel);
});
});
});

Просмотреть файл

@ -79,7 +79,7 @@ describe('ReportRenderer', () => {
const output = renderer.renderReport(sampleResults, container);
assert.ok(output.querySelector('.lh-header-sticky'), 'has a header');
assert.ok(output.querySelector('.lh-report'), 'has report body');
assert.equal(output.querySelectorAll('.lh-gauge').length,
assert.equal(output.querySelectorAll('.lh-gauge__wrapper, .lh-gauge--pwa__wrapper').length,
sampleResults.reportCategories.length * 2, 'renders category gauges');
});
@ -104,6 +104,31 @@ describe('ReportRenderer', () => {
assert.equal(url.href, sampleResults.finalUrl);
});
it('renders special score gauges after the mainstream ones', () => {
const container = renderer._dom._document.body;
const output = renderer.renderReport(sampleResults, container);
const allGaugeCount = output
.querySelectorAll('.lh-scores-header > a[class*="lh-gauge"]').length;
const regularGaugeCount = output
.querySelectorAll('.lh-scores-header > .lh-gauge__wrapper').length;
// Not all gauges are regular.
assert.ok(regularGaugeCount < allGaugeCount);
const scoresHeaderElem = output.querySelector('.lh-scores-header');
for (let i = 0; i < scoresHeaderElem.children.length; i++) {
const gauge = scoresHeaderElem.children[i];
if (i < regularGaugeCount) {
assert.ok(gauge.classList.contains('lh-gauge__wrapper'));
} else {
assert.ok(!gauge.classList.contains('lh-gauge__wrapper'));
assert.ok(gauge.classList.contains('lh-gauge--pwa__wrapper'));
}
}
});
it('should not mutate a report object', () => {
const container = renderer._dom._document.body;
const originalResults = JSON.parse(JSON.stringify(sampleResults));