Skip to content

Add basic rendering to report generator v2#1933

Closed
dgozman wants to merge 3 commits intoGoogleChrome:masterfrom
dgozman:report-generator-v2-basics
Closed

Add basic rendering to report generator v2#1933
dgozman wants to merge 3 commits intoGoogleChrome:masterfrom
dgozman:report-generator-v2-basics

Conversation

@dgozman
Copy link
Copy Markdown
Contributor

@dgozman dgozman commented Mar 29, 2017

Supports categories, audits, scores, lists, blocks and text.
Changed is-on-https audit to show list of urls as an example.
Clearly needs some design love.

Ref #1887

Supports categories, audits, scores, lists, blocks and text.
Changed is-on-https audit to show list of urls as an example.
Clearly needs some design love.
@paulirish
Copy link
Copy Markdown
Member

FYI y'all: it's very basic.

Screenshot:
image

};

// /** @typedef {{type: string, text: string|undefined, header: DetailsJSON|undefined, items: Array<DetailsJSON>|undefined}} */
// var DetailsJSON;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linter doesn't like var here, but using let will pollute the scope. Is there a known way for typedefs?

Copy link
Copy Markdown
Contributor

@brendankenny brendankenny Mar 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linter doesn't like var here, but using let will pollute the scope. Is there a known way for typedefs?

yeah, I was thinking about this when looking at enabling closure compilation of the project again. If we do more typedefs in files just meant for node/browserification, the nice thing is that those will be limited in scope to the file. For stuff in the browser it's harder. A few ideas:

  1. we could add an .eslintrc to this directory to modify the rules, since in theory this js file will only ever run in a browser context and not in node (never say never, though :)
  2. NTI doesn't support typedefs as namespaces, but would doing ReportRenderer.DetailsJSON work here since the namespace would be a real object?
  3. we live with the pollution and in the future add something to report-generator.js that strips it out
  4. We live with the pollution and pick names that will (probably) only ever be used for typedefs

Copy link
Copy Markdown
Member

@paulirish paulirish Mar 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My vote is for 4. The pollution seems very manageable, for the limited number of typedefs we have.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My vote is for 4

SGTM2.

We were also just talking and realized it doesn't matter if this is var or let here anyways, since even if we use var here if somewhere else a global with the same name is declared with let or const it will throw an error. So I'm good with let and global pollution.


/* Text */

.lighthouse-text {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tried to follow BEM in the previous report, do we have any opinions about it in the new world?

I'm personally not a huge fan, but I like the idea of having some convention to standardize around.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shadow dom is the solution :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with having a convention. That said, I don't love any of the options. But I think we need to pick one regardless

Honestly BEM probably has the most tooling support of any CSS convention, so I favor that.

Should we migrate to it in this PR? I could go either way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adopted BEM.

element.appendChild(pre);
renderReport(report) {
try {
var element = this._renderReport(report);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might have to make a decision if we follow devtools style and conventions or lighthouse (or drift the rest of lighthouse to devtools?). use of const, let vs. var, object shorthand, etc

*/
_renderException(e) {
var element = this._createElement('div', 'lighthouse-exception');
element.textContent = '' + e;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thoughts on e.stack instead for a little more info?

*/
_renderException(e) {
const element = this._createElement('div', 'lighthouse-exception');
element.textContent = String(e);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String(e.stack)

so we can get the message and callstack.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

};

/** @typedef {{type: string, text: string|undefined, header: DetailsJSON|undefined, items: Array<DetailsJSON>|undefined}} */
let DetailsJSON;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can do let DetailsJSON; // eslint-disable-line no-unused-vars for each of these to disable the lint failure on just these lines

* @param {string} text
* @return {Node}
*/
_createTextChild(parent, text) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this used anywhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. I'll remove it. Thanks!

generateReportHtml(reportAsJson) {
const sanitizedJson = JSON.stringify(reportAsJson).replace(/</g, '\\u003c');
const sanitizedJavascript = REPORT_JAVASCRIPT.replace(/<\//g, '\\u003c/');
const sanitizedCss = REPORT_CSS.replace(/<\//g, '\\u003c/');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @brendankenny mentioned this in another PR, but sanitizedJavascript and sanitizedCss aren't necessary b/c we control both files. Might be a good time to just remove.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ebidel I responded to that. This wasn't for XSS, this was for future-proof against us somewhere using </script> in a string and scratching heads for why the whole report is broken. We do it currently too.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can rename to escaped or something if we want to make that clearer

Copy link
Copy Markdown
Contributor

@ebidel ebidel Mar 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd know right away if the whole report was broken :) The current _escapeScriptTags does the same as your sanitizedJson, which sanitizes user input in the json results. But our own stuff should be safe (REPORT_JAVASCRIPT and REPORT_CSS).


/* Text */

.lighthouse-text {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shadow dom is the solution :)

* @param {string=} className
* @returns {Element}
*/
_createElement(name, className) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we support attributes too? There are a couple of places we use them in the current report.

_createElement(name, className, attrs = []) {
...
for (const [key, val] of Object.entries(attrs)) {
  element.setAttribute(key, val);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do it when we need it?

Copy link
Copy Markdown
Contributor

@ebidel ebidel Mar 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can wait if you want. But we'll need it for at least two places:

https://github.com/GoogleChrome/lighthouse/blob/master/lighthouse-core/report/partials/cards.html#L5
https://github.com/GoogleChrome/lighthouse/blob/master/lighthouse-core/report/partials/critical-request-chains.html#L2

I started playing with porting some of the html formatters today and ran into this.

const element = this._createElement('div', 'lighthouse-score');
const value = this._createChild(element, 'div', 'lighthouse-score-value');
value.textContent = score.toLocaleString(undefined, {maximumFractionDigits: 1});
if (score <= 50)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fore scoring you can use this (taken from the existing report):

const RATINGS = {
  GOOD: {label: 'good', minScore: 75},
  AVERAGE: {label: 'average', minScore: 45},
  POOR: {label: 'poor'}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the double comments. Github was being weird today.

const element = this._createElement('div', 'lighthouse-score');
const value = this._createChild(element, 'div', 'lighthouse-score__value');
value.textContent = score.toLocaleString(undefined, {maximumFractionDigits: 1});
if (score <= 50)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For scoring, you can use this from the existing report:

const RATINGS = {
  GOOD: {label: 'good', minScore: 75},
  AVERAGE: {label: 'average', minScore: 45},
  POOR: {label: 'poor'}
};

*/
_renderScore(score, title, description) {
const element = this._createElement('div', 'lighthouse-score');
const value = this._createChild(element, 'div', 'lighthouse-score__value');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could easily get away without having a _createChild:

element.appendChild(this._createElement('div', 'lighthouse-score__value'));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_createChild returns an element for further manipulations.

const renderer = new ReportRenderer(window.__LIGHTHOUSE_JSON__);
renderer.render(document.body);
const renderer = new ReportRenderer(document);
document.body.appendChild(renderer.renderReport(window.__LIGHTHOUSE_JSON__));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully in the near future, we can move some of the UI into static HTML that gets populated. Is there a plan for that? I'm still very concerned about the maintainability and scalability of a pure dom-based approach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is against the idea of report being embeddable and template-free.
Regarding maintainability: we plan to implement some basic primitives like list/table/url/etc, so that on average nobody has to touch report generator at all.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think those goals are at odds with a declarative approach. For example, we could declare <template> partials in the page:

<template id="template-audit">
  <div class="lighthouse-audit">
    <div class="lighthouse-score">
      <div class="lighthouse-score__value"><!--score--></div>
      <div class="lighthouse-score__text">
        <div class="lighthouse-score__title"><!--title--></div>
        <div class="lighthouse-score__description"><!--detail--></div>
      </div>
    </div>
  </div>
</template>

http://jsbin.com/bojinipecu/edit?html,output

And various embedders could instantiate slightly different <template>s based on their needs. Alternatively, override a render method and add their extra functionality. It's still 100% DOM, but it would be easier to reason about app structure and make modifications to the UI.

This would also lend itself nicely to creating reusable chunks of markup+functionality in the form of web components. Each component would be responsible for its own css/html/js. DevTools could extend a given component if it needs something different.

const categories = __LIGHTHOUSE_JSON__.reportCategories;

const container = document.querySelector('lighthouse-category');
categories.forEach(audit => {
  const auditEl = document.createElement('lighthouse-audit');
  auditEl.audit = audit;
  container.appendChild(auditEl);
});

might produce something like:

<lighthouse-category>
  <lighthouse-audit id="critical-request-chains">
    #shadow-root
      <div class="lighthouse-score">...</div>
      ...
      <card-fortmatter></card-fortmatter>
  </lighthouse-audit>
  <lighthouse-audit>...</lighthouse-audit>
</lighthouse-category>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<title>Lighthouse Report</title>
<style>%%LIGHTHOUSE_CSS%%</style>
Copy link
Copy Markdown
Contributor

@ebidel ebidel Mar 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one causes vscode's syntax highlighting to blow up. WDYT about making this replace str a css comment: <style>/*%%LIGHTHOUSE_CSS%%*/</style>?

@paulirish paulirish mentioned this pull request Mar 30, 2017
52 tasks
@paulirish
Copy link
Copy Markdown
Member

There's not too much left here.
@ebidel do you want to finish off this PR? if not, i'm happy to.

@ebidel
Copy link
Copy Markdown
Contributor

ebidel commented Apr 2, 2017

Closing down for #1951

@ebidel ebidel closed this Apr 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants