-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Report v2: Basic table formatter #2019
Conversation
@@ -179,14 +179,17 @@ class UnusedCSSRules extends Audit { | |||
return UnusedCSSRules.mapSheetToResult(indexedSheets[sheetId], pageUrl); | |||
}).filter(sheet => sheet && sheet.wastedBytes > 1024); | |||
|
|||
|
|||
const headings = [ | |||
{key: 'url', itemType: 'url', text: 'URL'}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can override this per row correct? it'd be nice to finally have a nice solution for displaying the inline code preview in the url column
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to have an individual row change the itemType? that seems a little weird. whyd we want diff itemtypes in the column?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in all the css-based ones the "url column" doesn't always have a url and it's a code snippet preview instead since it's an inline style. it'd be nice to have the details renderer support that too so the code snippets can say {type: "code", content: ".inline-style {...}"}
or something
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sweet!
totalKb: 'Original', | ||
potentialSavings: 'Potential Savings', | ||
} | ||
headings, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The @return
docstring of this method needs to be updated to match.
@@ -88,6 +116,33 @@ class DetailsRenderer { | |||
return element; | |||
} | |||
|
|||
|
|||
/** | |||
* @param {!DetailsRenderer.TableDetailsJSON} details |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is TableDetailsJSON
typedef'd anywhere? not seeing it.
const element = this._dom.createElement('img', 'lh-thumbnail'); | ||
element.src = obj.text.url; | ||
// ignore obj.text.mimeType | ||
element.alt = 'Image preview'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also add element.title = obj.text.url;
so there's a full url when users hover over the image?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sg
* @return {!Element} | ||
*/ | ||
_renderTable(details) { | ||
const element = this._dom.createElement('details', 'lh-details', {open: true}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we open by default? There could be a lot of stuff in the table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nah. was only for development. will remove. :)
element.appendChild(this._dom.createElement('summary')).textContent = details.header; | ||
} | ||
|
||
const tableElem = element.createChild('table', 'lh-table lh-table__multicolumn'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need lh-table__multicolumn
? That was an impl detail of v1's handlebars table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope. will nuke.
* @return {!Element} | ||
*/ | ||
self.Element.prototype.createChild = function(elementName, className) { | ||
const element = instance.createElement(elementName, className); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pipe through the attrs
param too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i dont think we can because in devtools this method will already exist and it doesnt do attrs
self.Element.prototype.createChild = function(elementName, className) { | ||
const element = instance.createElement(elementName, className); | ||
this.appendChild(element); | ||
return element; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: return this.appendChild(element);
* @param {string=} className | ||
* @return {!Element} | ||
*/ | ||
self.Element.prototype.createChild = function(elementName, className) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're comfortable with modifying built in prototypes, then I have a bunch of other helpers to add :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check out https://github.com/ChromeDevTools/devtools-frontend/blob/master/front_end/dom_extension/DOMExtension.js .. we can polyfill whatever from there we want. (or easier: just load that file in the report)
--image-preview: 24px; | ||
} | ||
|
||
img.lh-thumbnail { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just .lh-thumbnail
? Seems ok to be tag-agnostic.
* @param {string=} className | ||
* @return {!Element} | ||
*/ | ||
self.Element.prototype.createChild = function(elementName, className) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just make this a method on DOM? We add five characters (DOM
and a ,
) but avoid all the usual downsides of doing this. Also has the benefit that Closure will understand it when not compiled with devtools, we can more easily test it, etc
lighthouse-core/audits/audit.js
Outdated
@@ -57,6 +57,60 @@ class Audit { | |||
} | |||
|
|||
/** | |||
* @param {!Audit.Headings} headings | |||
* @return {*} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@return {!Array<string>}
?
lighthouse-core/audits/audit.js
Outdated
|
||
/** | ||
* @param {!Audit.Headings} headings | ||
* @param {!Object} results |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, definitely not an object. {!Array<!Object<string, string>>}
? It's hard to follow the old createTable
helper
lighthouse-core/audits/audit.js
Outdated
@@ -57,6 +57,60 @@ class Audit { | |||
} | |||
|
|||
/** | |||
* @param {!Audit.Headings} headings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just one space (here and elsewhere). Is this a vscode setting that needs to be changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
heh. yah https://github.com/Microsoft/vscode-comment is at fault. reported the bug.
fixed
lighthouse-core/audits/audit.js
Outdated
|
||
/** | ||
* @param {!Audit.Headings} headings | ||
* @return {!Array<!DetailsRenderer.DetailsJSON>} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is fine for now, but it won't actually work if we were to turn on type checking on this file. Closure won't be able to see the typedef since it's in another module. We'll have to think of how we want to do that (move report typedefs to their own file?)
lighthouse-core/audits/audit.js
Outdated
|
||
/** | ||
* @param {!Audit.Headings} headings | ||
* @param {!Object} results |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same type as above results
lighthouse-core/audits/audit.js
Outdated
*/ | ||
Audit.Heading; // eslint-disable-line no-unused-expressions | ||
|
||
/** @typedef {!Array<!Audit.Heading>} */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it worth making a typedef for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually yeah. Basically we pass around this typedef and never use the Audit.Heading one directly. Not entirely worth it to nuke this guy, i tried.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if that's the case, what about
/** @typedef {
* !Array<{
* key: string,
* itemType: string,
* text: string,
* }>}
*/
Audit.Headings;
and skip Audit.Heading
?
*/ | ||
_renderThumbnail(obj) { | ||
const element = this._dom.createElement('img', 'lh-thumbnail'); | ||
element.src = obj.text.url; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this isn't a thing on DetailsJSON
lighthouse-core/audits/audit.js
Outdated
const tableRows = results.map(item => { | ||
return headings.map(heading => { | ||
return { | ||
type: heading.itemType, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not do this in _renderTable
rather than in the JSON report?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shrugz
I'm okay with either.
patrick is fine with either but thinks its slightly better to have all things coming into renderer be our details objects
dgozman also feels the same way, and thinks that if we wanted to conserve the size of the LH object we should shorten type
to t
etc. ;) ;)
so I'm comfortable expanding the objects over here before we deliver the LHR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so I'm comfortable expanding the objects over here before we deliver the LHR.
I think with our less rigid details taxonomy now I'm actually more OK with this extra verboseness :)
We can keep an eye on report file size and how much is taken up by "type": "text"
and figure out some defaults (default to text or default to header itemType) if/when needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note from sync: default item details type should be text
, handled as such in the details-renderer
lighthouse-core/audits/audit.js
Outdated
@@ -57,6 +57,60 @@ class Audit { | |||
} | |||
|
|||
/** | |||
* @param {!Audit.Headings} headings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
heh. yah https://github.com/Microsoft/vscode-comment is at fault. reported the bug.
fixed
@paulirish how's this coming? We've got a lot of audits that need this guy. |
roger that. on it. |
bc73833
to
160e6ef
Compare
PTAL. updated. |
const tableRows = results.map(item => { | ||
return headings.map(heading => { | ||
const value = item[heading.key]; | ||
if (typeof value === 'object' && value.type) return value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
@@ -424,4 +424,14 @@ summary.lh-passed-audits-summary::-webkit-details-marker { | |||
} | |||
} | |||
|
|||
.lh-table { | |||
--image-preview: 24px; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: --image-preview-size
or something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of things I want to tweak, but this is LGTM to get in.
@brendankenny to you |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reviewed!
* type: string, | ||
* header: ({text: string}|undefined), | ||
* items: !Array<!Array<!DetailsRenderer.DetailsJSON>>, | ||
* itemHeaders: !Array<!DetailsRenderer.DetailsJSON> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
by letting these be DetailsJSON
we're basically descending into recursive madness again since in theory when you call this.render
on them they could embed a whole other table (but of course, our css would quickly fall over itself if that sort of thing occurred). This leaves these basically untyped (except our assurance to the compiler that we're correct when we cast in this.render
)
I'm not sure of the right solution...maybe we could separate top level rendering (anything currently in this.render
) from more primitive-level rendering (just text, url, and thumbnail as of this PR) in a future PR or something, but we would need to make whatever we did non-annoying.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we're basically descending into recursive madness again
are we though?
detailsjson
only has type and text.. it can't contain other types.
really it and thumbnail
should include the word "item" as they are ALWAYS leaf nodes.
and the render()
method should accept all of these leaf & group types. the real bug seems that render's param is typed to a leaf node when we know its not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
detailsjson
is the base (structural) type that is the superset of all of them. It only has fields all of them have: type
and optional text
(really text is there from when they all had text
and should probably be removed). render()
takes that and casts according to its type
to the appropriate type, and we'll take responsibility to make sure that if type
is set to x, then that object really is of type
x. It's a hack, but simple (we only have to have humans in the loop checking the top level details
object), and can easily be reworked once we know the full outlines of the types we need.
Allowing arbitrary nesting means that it's much less simple since we have to guarantee that all the nested items and itemHeaders are really whatever their type
says. It also means we're implicitly breaking the no-typedef-cycles, just getting around it by casting, which is what I meant by recursive madness.
If you're saying we should have a different type explicitly for leaf nodes to make this better, I think that's great
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ya makes sense. let's sort this out in voice as i have a few ideas.
* @return {!Element} | ||
*/ | ||
_renderThumbnail(value) { | ||
if (/^image/.test(value.mimeType) === false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make a note of this in a jsdoc description?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
|
||
const element = this._dom.createElement('img', 'lh-thumbnail'); | ||
element.src = value.url; | ||
element.alt = 'Image preview'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
best we can do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was matching our v1. :) i changed it to empty string, which seems better, actually.
@@ -80,8 +81,7 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { | |||
|
|||
/** | |||
* @param {!Artifacts} artifacts | |||
* @return {{results: !Array<Object>, tableHeadings: Object, | |||
* passes: boolean=, debugString: string=}} | |||
* @return {{results: !Array<Object>, headings: !Audit.Headings, passes: boolean=, debugString: string=}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
boolean=
would be (boolean|undefined)
, but doesn't look like it can be undefined?
string=
would be (string|undefined)
(=
only works for @params
...technically it means optional param, which just so happens to manifest as being undefined
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks. made a new typdef for this guy since he was everywhere.
@@ -46,7 +46,7 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit { | |||
/** | |||
* @param {!Artifacts} artifacts | |||
* @param {number} networkThroughput | |||
* @return {!AuditResult} | |||
* @return {{results: !Array<Object>, passes: boolean=, headings: !Audit.Headings, debugString: string=}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see below
@@ -63,8 +63,7 @@ class UsesOptimizedImages extends ByteEfficiencyAudit { | |||
|
|||
/** | |||
* @param {!Artifacts} artifacts | |||
* @return {{results: !Array<Object>, tableHeadings: Object, | |||
* passes: boolean=, debugString: string=}} | |||
* @return {{results: !Array<Object>, headings: !Audit.Headings, passes: boolean=, debugString: string=}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
samsies
@@ -95,8 +96,7 @@ class OffscreenImages extends ByteEfficiencyAudit { | |||
|
|||
/** | |||
* @param {!Artifacts} artifacts | |||
* @return {{results: !Array<Object>, tableHeadings: Object, | |||
* passes: boolean=, debugString: string=}} | |||
* @return {{results: !Array<Object>, headings: !Audit.Headings, debugString: string=}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see notes below
lighthouse-core/audits/audit.js
Outdated
*/ | ||
Audit.Heading; // eslint-disable-line no-unused-expressions | ||
|
||
/** @typedef {!Array<!Audit.Heading>} */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if that's the case, what about
/** @typedef {
* !Array<{
* key: string,
* itemType: string,
* text: string,
* }>}
*/
Audit.Headings;
and skip Audit.Heading
?
const tableRows = results.map(item => { | ||
return headings.map(heading => { | ||
const value = item[heading.key]; | ||
if (typeof value === 'object' && value.type) return value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
results value
s might not just be a string
, then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
It's not very pretty but the basics are there.
AuditResult.details.itemHeaders
for thethead
cellsscreenshot, lol: