Skip to content
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

Add links to switch between different reports in left nav-bar #1477

Merged
merged 13 commits into from
Jan 31, 2017

Conversation

weiwei-lin
Copy link
Contributor

@weiwei-lin weiwei-lin commented Jan 16, 2017

Allow users to navigate between different report. Part of Project Perf-X #1143.
Screenshot of the new left nav-bar:
left-panel

@samuelli
Copy link

Best to add screenshots in PR description and to any followup changes.

this._url = url;
this._config = config;

this._root = fs.mkdtempSync(`${__dirname}/experiment-data`);

Choose a reason for hiding this comment

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

Is this current directory? Can we rename it to .lighthouse-performance-data-?

  • Dot directory might make more sense
  • If its in the current directory, user needs to be able to identify its from lighthouse
    • at end for the temp suffix to look a bit nicer

What's the size of this data? Could we store it in memory for now?

Copy link
Contributor Author

@weiwei-lin weiwei-lin Jan 17, 2017

Choose a reason for hiding this comment

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

It's not in CWD. It's under lighthouse/lighthouse-cli/performance-experiment/.
I tested it on YouTube.com, and the results.json was around 2.3 MB. It's pretty big already and could be bigger for some other websites. So I think we should store it in hard drive.
I addded a "-" to the end of the folder prefix. Thanks for the suggestion.

Choose a reason for hiding this comment

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

SGTM.

@brendankenny
Copy link
Member

I assume this PR will also have to be reworked a bit once #1455 lands?

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

I'll wait for #1455 before completely getting into this, but looking good so far.

@@ -61,6 +61,18 @@
<h1 class="menu__header-title">Lighthouse</h1>
<div class="menu__header-version">Version: {{lighthouseVersion}}</div>
</div>
{{#each relatedReports}}
Copy link
Member

Choose a reason for hiding this comment

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

Like the config panel, I don't see us using this outside of an interactive mode like perfX. This one will be slightly more awkward, but possible to pull out into a partial?

There might be some fancy handlebars way of including it, but also could do it brute force, something like

{{#if_eq reportContext "perf-x" }}
  {{> related-reports}}
{{else}}
  <ul class="menu__nav">
    {{#each aggregations}}
    <li class="menu__nav-item">
      <a class="menu__link" href="#{{nameToLink this.name}}">
        {{ this.name }}
      </a>
    </li>
    {{/each}}
  </ul>
{{/if_eq}}

(including the menu__nav block in the related-reports partial)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thx for the advice. I will do this after #1455 is landed.

@@ -34,7 +34,7 @@ describe('Report', () => {
it('should format generated Time', () => {
const reportGenerator = new ReportGenerator();
const html = reportGenerator.generateHTML(sampleResults);
assert.ok(/on 11\/\d{1,2}\/2016\, /gim.test(html));
assert.ok(/on: 11\/\d{1,2}\/2016\, /gim.test(html));
Copy link
Member

Choose a reason for hiding this comment

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

hmm, how did this pass before?

Copy link
Member

Choose a reason for hiding this comment

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

I also am curious about this. @WeiweiAtGit do you know?

Copy link
Member

Choose a reason for hiding this comment

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

ah, it was matching on the footer, which has Generated by <b>Lighthouse</b> {{lighthouseVersion}} on {{generatedTime}}

this._url = url;
this._config = config;

this._root = fs.mkdtempSync(`${__dirname}/experiment-data-`);
Copy link
Member

Choose a reason for hiding this comment

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

reason for the ending -?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just to make it looks nicer. Since fs.mkdtempSync will adds some random characters as postfix.

Copy link
Member

Choose a reason for hiding this comment

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

ah, nice, didn't know fs.mkdtempSync existed :)

@weiwei-lin
Copy link
Contributor Author

I assume this PR will also have to be reworked a bit once #1455 lands?

Yes.

@paulirish
Copy link
Member

Previously, I thought we had discussed using assetSaver.getFilenamePrefix for the id, rather than an incrementing integer. I'd prefer that slightly, as "cool uris don't change"

@weiwei-lin weiwei-lin force-pushed the left-panel branch 4 times, most recently from 7695d47 to 54c5e2e Compare January 27, 2017 00:40
@weiwei-lin
Copy link
Contributor Author

Ready for review. PTAL

}
return formatter.format(new Date(date));
// format time
Handlebars.registerHelper('format_time', function(date) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

make this a helper so I can use it in perf-x-left-nav partial

@@ -323,7 +330,7 @@ class ReportGenerator {
return template({
url: results.url,
lighthouseVersion: results.lighthouseVersion,
generatedTime: this._formatTime(results.generatedTime),
generatedTime: results.generatedTime,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

pass generated time as time instead of string to ease time comparison in perf-x-left-nav

this._config = config;
this._defaultId = undefined;

this._root = fs.mkdtempSync(`${__dirname}/experiment-data-`);
Copy link
Member

Choose a reason for hiding this comment

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

rename to _fsRoot ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed. thx for your advice : )

const perfXReportGenerator = new PerfXReportGenerator();

const results = JSON.parse(fs.readFileSync(path.join(this._root, id, 'results.json'), 'utf8'));
const relatedReports = Object.keys(this._timeStamps)
Copy link
Member

Choose a reason for hiding this comment

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

otherReports as they aren't necessarily related, but they are the only other ones the server knows about, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

* @param {?string} id When id is not provided, this._defaultId will be used
*/
getHTML(id) {
id = id || this._defaultId;
Copy link
Member

Choose a reason for hiding this comment

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

i dont really follow what the purpose is of the defaultId. Seems like we don't need a fallback?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this allows users to access report from URL bar without inputting a long report id (when they accidentally closed the report tab). Although they can just go to history page most of the time and they still need to remember the port, I feel it's nice to have this feature. But if you feel it's confusing, I can remove it.

Copy link
Member

@paulirish paulirish Jan 30, 2017

Choose a reason for hiding this comment

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

Ah. I see. it's a fallback solution so navigating to the web root will always give them a page to use.

Seems okay since we dont have a "please select a report on the left to view" index page yet. cool.

I'd prefer this is handled at the router level rather than the database one. can we move it to the server?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

moved to server.js. Thx for the advice.

{{/each}}
{{/createRelatedReportsContext}}
<a class="menu__report-tab" href="#">
<p class="menu__report-tab__url" title="{{ url }}">Report for: {{ url }}</p>
Copy link
Member

Choose a reason for hiding this comment

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

you should probably run this URL through URL.getDisplayName in the url-shim

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried this. It didn't fit very well imo. When I tested http://localhost:8080, the url here simply became /. Just to clarify, the URL in Report for: {{ url }}` is the URL of the website being tested, not the report URL.
By the way, originally, the tabs in the left nav were meant to replace the meta data on the top bar. Since we are going to keep the top bar, shall we use report ID here?

}
</style>

{{#createRelatedReportsContext}}
Copy link
Member

Choose a reason for hiding this comment

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

i think we can do this partial better.

one approach: single loop through the reports. check if the reportId matches the requestedID and if it does, tweak the menu__report-tab link. maybe generate the menu__nav's for all the reports, but display:none the others with some classes/css

probably some better ways than that, but let's DRY this up some.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed. thx for the advice.

</li>
{{/each}}
</ul>
{{#if_eq reportContext "perf-x" }}
Copy link
Member

Choose a reason for hiding this comment

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

would it be possible to branch this template not on reportContext, but on the data provided to the template?

so we have a partial for this leftnav thing... we have _relatedReports or w/e, then we display all those, otherwise, simple menu__nav-items.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we can do this. But that also means we'd better move the left-nav partial to the core folder or even embed it into report-template.html directly, because it's no longer just for perf-x in that case (although only perf-x uses it for now). I'm not sure whether I should put the partial in formatters (all the partials there are related to audits), under perf-x, in report-template.html or in a separate folder under core/report. Do you have a good place in mind? And shall we support multiple reports in core/report-generator.js directly in that case?

Copy link
Member

Choose a reason for hiding this comment

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

Based on the loop refactor it seems like the left-nav partial should be pretty straightforward. I like tossing it into the report-template.html directly.

And yeah, report-generator can take the otherReports array now and handle that. Seems good!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

instead of otherReports array, I used reportsCatalog. Because the nav needs to know which report is selected currently. Adding a boolean flag to each of the element in the report is another approach, but I feel using reportsCatalog is safer, because we won't accidentally mark multiple reports as selected.

@@ -34,7 +34,7 @@ describe('Report', () => {
it('should format generated Time', () => {
const reportGenerator = new ReportGenerator();
const html = reportGenerator.generateHTML(sampleResults);
assert.ok(/on 11\/\d{1,2}\/2016\, /gim.test(html));
assert.ok(/on: 11\/\d{1,2}\/2016\, /gim.test(html));
Copy link
Member

Choose a reason for hiding this comment

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

I also am curious about this. @WeiweiAtGit do you know?

Copy link
Member

@paulirish paulirish left a comment

Choose a reason for hiding this comment

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

I think we're in a good shape here. Just some fairly rote changes left and it'll be ready to land!

@@ -0,0 +1,3 @@
'use strict';

module.exports = require('./database');
Copy link
Member

Choose a reason for hiding this comment

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

any reason to have this file and not just do const ExperimentDatabase = require('./experiment-database/database');?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because I wanted to encapsulate the database (i.e. database and the temp file it generated will be treated as a whole). Although it will work perfectly as well if we just do const ExperimentDatabase = require('./experiment-database/database');

Copy link
Member

Choose a reason for hiding this comment

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

+1 for not having an empty index.js

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed.

Copy link
Member

@paulirish paulirish left a comment

Choose a reason for hiding this comment

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

Nice. I really like the new createNav inline partial and handling there.

A few more ideas in the comments.

Also, should we have some tests? I think database.js is rather testable. And I can see us having a test for the server's reportRequestHandler, providing a fake request/response and asserting that the data provided to response.end includes a few key text strings. EDIT: If you want, we can do these tests in a followup PR.

@@ -94,8 +97,19 @@ function requestHandler(request, response) {

function reportRequestHandler(request, response) {
try {
const id = request.parsedUrl.query.id || defaultReportId;
Copy link
Member

Choose a reason for hiding this comment

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

lets make a little getSelectedId(request) method for this and the other handlers


const reportsMetadata = Object.keys(database.timeStamps).map(key => {
const generatedTime = database.timeStamps[key];
return {url: database.url, reportHref: `/?id=${key}`, generatedTime};
Copy link
Member

Choose a reason for hiding this comment

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

i think can keep the URL building out of here and only in the template for a cleaner view. Here's a proposal (this and the following comments).

return {url: database.url, reportId: key, generatedTime};

Copy link
Contributor Author

@weiwei-lin weiwei-lin Jan 31, 2017

Choose a reason for hiding this comment

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

I think it's better to define URL format only in perf-x server. Because if we somehow want to change the URL format in the future, we only need to make changes in perf-x server.
report-template.html does not need to know how routing is done in perf-x server. So there will be less coupling between perf-x server and report template.
Also, other clients will be able to pass reports in a different URL format to the generator if we build URL in the server side.

const generatedTime = database.timeStamps[key];
return {url: database.url, reportHref: `/?id=${key}`, generatedTime};
});
const reportsCatalog = {reportsMetadata, selectedReportHref: `/?id=${id}`};
Copy link
Member

Choose a reason for hiding this comment

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

const reportsCatalog = {reportsMetadata, selectedReportId: id};

{{> perf-x-reports-nav}}
{{#if reportsCatalog }}
{{#each reportsCatalog.reportsMetadata }}
<a class="menu__report-tab" href="{{ reportHref }}">
Copy link
Member

Choose a reason for hiding this comment

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

 ... href="/?id={{ reportId }}">

<p class="menu__report-tab__url" title="{{ url }}">Report for: {{ url }}</p>
<p class="menu__report-tab__time">Generated on: {{ format_time generatedTime }}</p>
</a>
{{#if_eq reportHref ../reportsCatalog.selectedReportHref }}
Copy link
Member

Choose a reason for hiding this comment

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

{{#if_eq reportId ../reportsCatalog.selectedReportId }}



let database;
let defaultReportId;
Copy link
Member

Choose a reason for hiding this comment

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

can we call this fallbackReportId ?

Copy link
Contributor Author

@weiwei-lin weiwei-lin Jan 31, 2017

Choose a reason for hiding this comment

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

changed. thx.

@weiwei-lin
Copy link
Contributor Author

EDIT: If you want, we can do these tests in a followup PR.

Sure : )

@@ -31,6 +31,8 @@ class ConfigPanel {
this._messageField = this._configPanel.querySelector('.js-message');
this._urlBlockingList = this._configPanel.querySelector('.js-url-blocking-patterns');
this._urlBlockingStatus = {};
const match = location.search.match(/[?&]id=([^&]*)/);
this._reportId = match ? match[1]: '';
Copy link
Member

@paulirish paulirish Jan 31, 2017

Choose a reason for hiding this comment

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

this._reportId = new URL(window.location).searchParams.get('id');

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Didn't know that. Thx for your advice :)

const generatedTime = database.timeStamps[key];
return {url: database.url, reportHref: `/?id=${key}`, generatedTime};
});
reportsMetadata.sort((metadata1, metadata2) => {
Copy link
Member

Choose a reason for hiding this comment

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

something like this should work:

reportsMetadata.sort((a, b) => b.generatedTime - a.generatedTime); 

(maybe flip the two if the order is wrong ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@paulirish paulirish merged commit 527bf8e into GoogleChrome:master Jan 31, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants