Skip to content

Commit

Permalink
report: implement new design for opportunities (#5115)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulirish authored May 4, 2018
1 parent 91aa8d2 commit db6b887
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,64 +47,38 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
* @return {!Element}
*/
_renderOpportunity(audit, index, scale) {
const element = this.dom.createElement('details', [
'lh-load-opportunity',
`lh-load-opportunity--${Util.calculateRating(audit.result.score)}`,
'lh-expandable-details',
].join(' '));
const tmpl = this.dom.cloneTemplate('#tmpl-lh-opportunity', this.templateContext);
const element = this.dom.find('.lh-load-opportunity', tmpl);
element.classList.add(`lh-load-opportunity--${Util.calculateRating(audit.result.score)}`);
element.id = audit.result.name;

// TODO(paulirish): use a template instead.
const summary = this.dom.createChildOf(element, 'summary', 'lh-load-opportunity__summary ' +
'lh-expandable-details__summary');
const titleEl = this.dom.createChildOf(summary, 'div', 'lh-load-opportunity__title');
const summary = this.dom.find('.lh-load-opportunity__summary', tmpl);
const titleEl = this.dom.find('.lh-load-opportunity__title', tmpl);
titleEl.textContent = audit.result.description;
this.dom.find('.lh-audit__index', element).textContent = `${index + 1}`;

this.dom.createChildOf(summary, 'div', 'lh-toggle-arrow', {title: 'See resources'});

if (audit.result.error) {
if (audit.result.debugString || audit.result.error) {
const debugStrEl = this.dom.createChildOf(summary, 'div', 'lh-debug');
debugStrEl.textContent = audit.result.debugString || 'Audit error';
return element;
}
if (audit.result.error) return element;

const details = audit.result.details;
const summaryInfo = /** @type {!DetailsRenderer.OpportunitySummary}
*/ (details && details.summary);
// eslint-disable-next-line no-console
console.assert(summaryInfo, 'Missing `summary` for load-opportunities audit');
// eslint-disable-next-line no-console
console.assert(typeof summaryInfo.wastedMs === 'number',
'Missing numeric `summary.wastedMs` for load-opportunities audit');
if (!summaryInfo || !summaryInfo.wastedMs) {
return element;
}

const elemAttrs = {title: Util.formatDisplayValue(audit.result.displayValue)};
const sparklineContainerEl = this.dom.createChildOf(summary, 'div',
'lh-load-opportunity__sparkline', elemAttrs);
const sparklineEl = this.dom.createChildOf(sparklineContainerEl, 'div', 'lh-sparkline');
const sparklineBarEl = this.dom.createChildOf(sparklineEl, 'div', 'lh-sparkline__bar');
sparklineBarEl.style.width = summaryInfo.wastedMs / scale * 100 + '%';

const statsEl = this.dom.createChildOf(summary, 'div', 'lh-load-opportunity__stats', elemAttrs);
const statsMsEl = this.dom.createChildOf(statsEl, 'div', 'lh-load-opportunity__primary-stat');
statsMsEl.textContent = Util.formatMilliseconds(summaryInfo.wastedMs);

if (summaryInfo.wastedBytes) {
const statsKbEl = this.dom.createChildOf(statsEl, 'div',
'lh-load-opportunity__secondary-stat');
statsKbEl.textContent = Util.formatBytesToKB(summaryInfo.wastedBytes);
}

const descriptionEl = this.dom.createChildOf(element, 'div',
'lh-load-opportunity__description');
descriptionEl.appendChild(this.dom.convertMarkdownLinkSnippets(audit.result.helpText));

if (audit.result.debugString) {
const debugStrEl = this.dom.createChildOf(summary, 'div', 'lh-debug');
debugStrEl.textContent = audit.result.debugString;
}
const displayValue = Util.formatDisplayValue(audit.result.displayValue);
const sparklineWidthPct = `${summaryInfo.wastedMs / scale * 100}%`;
const wastedMs = Util.formatSeconds(summaryInfo.wastedMs, 0.01);
const auditDescription = this.dom.convertMarkdownLinkSnippets(audit.result.helpText);
this.dom.find('.lh-load-opportunity__sparkline', tmpl).title = displayValue;
this.dom.find('.lh-load-opportunity__wasted-stat', tmpl).title = displayValue;
this.dom.find('.lh-sparkline__bar', tmpl).style.width = sparklineWidthPct;
this.dom.find('.lh-load-opportunity__wasted-stat', tmpl).textContent = wastedMs;
this.dom.find('.lh-load-opportunity__description', tmpl).appendChild(auditDescription);

// If there's no `type`, then we only used details for `summary`
if (details.type) {
Expand Down Expand Up @@ -163,6 +137,9 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
const maxWaste = Math.max(...opportunityAudits.map(audit => audit.result.rawValue));
const scale = Math.ceil(maxWaste / 1000) * 1000;
const groupEl = this.renderAuditGroup(groups['load-opportunities'], {expandable: false});
const tmpl = this.dom.cloneTemplate('#tmpl-lh-opportunity-header', this.templateContext);
const headerEl = this.dom.find('.lh-load-opportunity__header', tmpl);
groupEl.appendChild(headerEl);
opportunityAudits.forEach((item, i) =>
groupEl.appendChild(this._renderOpportunity(item, i, scale)));
groupEl.open = true;
Expand Down
10 changes: 10 additions & 0 deletions lighthouse-core/report/html/renderer/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ class Util {
return `${coarseTime.toLocaleString()}${NBSP}ms`;
}

/**
* @param {number} ms
* @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1
* @return {string}
*/
static formatSeconds(ms, granularity = 0.1) {
const coarseTime = Math.round(ms / 1000 / granularity) * granularity;
return `${coarseTime.toLocaleString()}${NBSP}s`;
}

/**
* Format time.
* @param {string} date
Expand Down
118 changes: 55 additions & 63 deletions lighthouse-core/report/html/report-styles.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 34 additions & 1 deletion lighthouse-core/report/html/templates.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,48 @@

<!-- Lighthouse perf metric -->
<template id="tmpl-lh-metric">

<div class="lh-metric">
<div class="lh-metric__innerwrap tooltip-boundary">
<span class="lh-metric__title"></span>
<div class="lh-metric__value"></div>
<div class="lh-metric__description tooltip"></div>
</div>
</div>
</template>

<!-- Lighthouse perf opportunity -->
<template id="tmpl-lh-opportunity">
<details class="lh-load-opportunity lh-expandable-details">
<summary class="lh-load-opportunity__summary lh-expandable-details__summary">
<div class="lh-load-opportunity__cols">
<div class="lh-load-opportunity__col lh-load-opportunity__col--one">
<span class="lh-audit__index"></span>
<div class="lh-load-opportunity__title"></div>
</div>
<div class="lh-load-opportunity__col lh-load-opportunity__col--two">
<div class="lh-load-opportunity__sparkline">
<div class="lh-sparkline"><div class="lh-sparkline__bar"></div></div>
</div>
<div class="lh-load-opportunity__wasted-stat"></div>
<div class="lh-toggle-arrow" title="See resources"></div>
</div>
</div>
</summary>
<div class="lh-load-opportunity__description"></div>
</details>
</template>


<!-- Lighthouse perf opportunity header -->
<template id="tmpl-lh-opportunity-header">
<div class="lh-load-opportunity__header lh-load-opportunity__cols">
<div class="lh-load-opportunity__col lh-load-opportunity__col--one">
Resource to optimize
</div>
<div class="lh-load-opportunity__col lh-load-opportunity__col--two">
Estimated Savings
</div>
</div>
</template>

<!-- Lighthouse left nav -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,14 @@ describe('PerfCategoryRenderer', () => {
assert.equal(oppElements.length, oppAudits.length);

const oppElement = oppElements[0];
const oppSparklineBarElement = oppElement.querySelector('.lh-sparkline__bar');
const oppSparklineElement = oppElement.querySelector('.lh-load-opportunity__sparkline');
assert.ok(oppElement.querySelector('.lh-load-opportunity__title'), 'did not render title');
assert.ok(oppSparklineElement, 'did not render sparkline');
assert.ok(oppElement.querySelector('.lh-load-opportunity__stats'), 'did not render stats');
assert.ok(oppSparklineElement.title, 'did not render tooltip');
const oppTitleElement = oppElement.querySelector('.lh-load-opportunity__title');
const oppWastedElement = oppElement.querySelector('.lh-load-opportunity__wasted-stat');
assert.ok(oppTitleElement.textContent, 'did not render title');
assert.ok(oppSparklineBarElement.style.width, 'did not set sparkline width');
assert.ok(oppWastedElement.textContent, 'did not render stats');
assert.ok(oppSparklineElement.title, 'did not set tooltip on sparkline');
});

it('renders the performance opportunities with a debug string', () => {
Expand Down Expand Up @@ -132,22 +135,6 @@ describe('PerfCategoryRenderer', () => {
assert.ok(debugEl, 'did not render debug');
});

it('throws if a performance opportunities is missing summary.wastedMs', () => {
const auditWithDebug = {
score: 0,
group: 'load-opportunities',
result: {
rawValue: 100, description: 'Bug',
helpText: '', score: 0.32,
},
};

const fakeCategory = Object.assign({}, category, {audits: [auditWithDebug]});
assert.throws(_ => {
renderer.render(fakeCategory, sampleResults.reportGroups);
});
});

it('renders the failing diagnostics', () => {
const categoryDOM = renderer.render(category, sampleResults.reportGroups);
const diagnosticSection = categoryDOM.querySelectorAll('.lh-category > .lh-audit-group')[2];
Expand Down

0 comments on commit db6b887

Please sign in to comment.