-
Notifications
You must be signed in to change notification settings - Fork 9.6k
devtools: enable sticky header, top bar, and report ui features #9023
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
Changes from 36 commits
41aa187
000f841
c0bf21d
1e040cc
a6e722e
7129ae1
ca0afd1
4a6a796
fe61b86
720dbb4
0153c0e
009c7d2
0ee54c8
cc763f8
7ace084
6650b66
8063f18
63c9638
739b20a
4b86fb4
bdbf379
181a5ed
2031b5e
b2016ea
f16f78f
e3a4ba0
19215e9
6059968
9dd16c1
82bb8e6
aec1ac3
0c1b09e
656e941
8ded84e
6dabf64
df79fcb
427baf5
d383120
b6879b1
26f2864
018a64b
2f3da14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,6 +46,8 @@ class ReportUIFeatures { | |
| this._dom = dom; | ||
| /** @type {Document} */ | ||
| this._document = this._dom.document(); | ||
| /** @type {ParentNode} */ | ||
| this._templateContext = this._dom.document(); | ||
| /** @type {boolean} */ | ||
| this._copyAttempt = false; | ||
| /** @type {HTMLElement} */ | ||
|
|
@@ -78,16 +80,16 @@ class ReportUIFeatures { | |
| * @param {LH.Result} report | ||
| */ | ||
| initFeatures(report) { | ||
| if (this._dom.isDevTools()) return; | ||
|
|
||
| this.json = report; | ||
|
|
||
| this._setupMediaQueryListeners(); | ||
| this._setupExportButton(); | ||
| this._setupThirdPartyFilter(); | ||
| this._setUpCollapseDetailsAfterPrinting(); | ||
| this._resetUIState(); | ||
| this._document.addEventListener('keyup', this.onKeyUp); | ||
| this._document.addEventListener('copy', this.onCopy); | ||
|
|
||
| const topbarLogo = this._dom.find('.lh-topbar__logo', this._document); | ||
| topbarLogo.addEventListener('click', () => this._toggleDarkTheme()); | ||
|
|
||
|
|
@@ -96,15 +98,10 @@ class ReportUIFeatures { | |
| turnOffTheLights = true; | ||
| } | ||
|
|
||
| // Fireworks. | ||
| const scoresAll100 = Object.values(report.categories).every(cat => cat.score === 1); | ||
| if (!this._dom.isDevTools() && scoresAll100) { | ||
| const scoresAll100 = Object.values(this.json.categories).every(cat => cat.score === 1); | ||
| if (scoresAll100) { | ||
| turnOffTheLights = true; | ||
| const scoresContainer = this._dom.find('.lh-scores-container', this._document); | ||
| scoresContainer.classList.add('score100'); | ||
| scoresContainer.addEventListener('click', _ => { | ||
| scoresContainer.classList.toggle('fireworks-paused'); | ||
| }); | ||
| this._enableFireworks(); | ||
| } | ||
|
|
||
| if (turnOffTheLights) { | ||
|
|
@@ -114,11 +111,61 @@ class ReportUIFeatures { | |
| // There is only a sticky header when at least 2 categories are present. | ||
| if (Object.keys(this.json.categories).length >= 2) { | ||
| this._setupStickyHeaderElements(); | ||
| this._document.addEventListener('scroll', this._updateStickyHeaderOnScroll); | ||
| window.addEventListener('resize', this._updateStickyHeaderOnScroll); | ||
| const containerEl = this._dom.find('.lh-container', this._document); | ||
| const elToAddScrollListener = this._getScrollParent(containerEl); | ||
| elToAddScrollListener.addEventListener('scroll', this._updateStickyHeaderOnScroll); | ||
| // We can't rely on listening to the window resize event for DevTools, so we first | ||
| // attempt the new ResizeObserver web platform feature. It has poor cross browser | ||
| // support, so we should check that it's supported. However, there are some performance | ||
| // issues with using window.ResizeObserver - it updates much more often than the 'resize' | ||
|
||
| // event fires, and the experience is choppy in LH. For now, limit use to just DevTools, | ||
| // which doesn't seem affected for some reason. | ||
|
||
| if (this._dom.isDevTools()) { | ||
| const resizeObserver = new window.ResizeObserver(this._updateStickyHeaderOnScroll); | ||
| resizeObserver.observe(containerEl); | ||
| } else { | ||
| window.addEventListener('resize', this._updateStickyHeaderOnScroll); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Define a custom element for <templates> to be extracted from. For example: | ||
| * this.setTemplateContext(new DOMParser().parseFromString(htmlStr, 'text/html')) | ||
| * @param {ParentNode} context | ||
| */ | ||
| setTemplateContext(context) { | ||
| this._templateContext = context; | ||
| } | ||
|
|
||
| /** | ||
| * Finds the first scrollable ancestor of `element`. Falls back to the document. | ||
| * @param {HTMLElement} element | ||
| * @return {Node} | ||
| */ | ||
| _getScrollParent(element) { | ||
| const {overflowY} = window.getComputedStyle(element); | ||
| const isScrollable = overflowY !== 'visible' && overflowY !== 'hidden'; | ||
|
|
||
| if (isScrollable && element.scrollHeight >= element.clientHeight) { | ||
brendankenny marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return element; | ||
| } | ||
|
|
||
| if (element.parentElement) { | ||
| return this._getScrollParent(element.parentElement); | ||
| } | ||
|
|
||
| return document; | ||
| } | ||
brendankenny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| _enableFireworks() { | ||
| const scoresContainer = this._dom.find('.lh-scores-container', this._document); | ||
| scoresContainer.classList.add('score100'); | ||
| scoresContainer.addEventListener('click', _ => { | ||
| scoresContainer.classList.toggle('fireworks-paused'); | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Fires a custom DOM event on target. | ||
| * @param {string} name Name of the event. | ||
|
|
@@ -179,13 +226,13 @@ class ReportUIFeatures { | |
| if (thirdPartyRows.size === urlItems.length || !thirdPartyRows.size) return; | ||
|
|
||
| // create input box | ||
| const filterTemplate = this._dom.cloneTemplate('#tmpl-lh-3p-filter', this._document); | ||
| const filterTemplate = this._dom.cloneTemplate('#tmpl-lh-3p-filter', this._templateContext); | ||
| const filterInput = this._dom.find('input', filterTemplate); | ||
| const id = `lh-3p-filter-label--${index}`; | ||
|
|
||
| filterInput.id = id; | ||
| filterInput.addEventListener('change', e => { | ||
| // Remove rows from the dom and keep track of them to readd on uncheck. | ||
| // Remove rows from the dom and keep track of them to re-add on uncheck. | ||
brendankenny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // Why removing instead of hiding? To keep nth-child(even) background-colors working. | ||
| if (e.target instanceof HTMLInputElement && !e.target.checked) { | ||
| for (const row of thirdPartyRows.values()) { | ||
|
|
@@ -545,7 +592,15 @@ class ReportUIFeatures { | |
| * @param {boolean} [force] | ||
| */ | ||
| _toggleDarkTheme(force) { | ||
| this._document.body.classList.toggle('dark', force); | ||
| const el = this._dom.find('.lh-vars', this._document); | ||
| // This seems unnecessary, but in DevTools, passing "undefined" as the second | ||
brendankenny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // parameter acts like passing "false". | ||
| // https://github.com/ChromeDevTools/devtools-frontend/blob/dd6a6d4153647c2a4203c327c595692c5e0a4256/front_end/dom_extension/DOMExtension.js#L809-L819 | ||
| if (typeof force === 'undefined') { | ||
| el.classList.toggle('dark'); | ||
| } else { | ||
| el.classList.toggle('dark', force); | ||
| } | ||
| } | ||
|
|
||
| _updateStickyHeaderOnScroll() { | ||
|
|
@@ -565,11 +620,12 @@ class ReportUIFeatures { | |
| // Category order matches gauge order in sticky header. | ||
| const gaugeWrapperEls = this.stickyHeaderEl.querySelectorAll('.lh-gauge__wrapper'); | ||
| const gaugeToHighlight = gaugeWrapperEls[highlightIndex]; | ||
| const offset = gaugeToHighlight.getBoundingClientRect().left + 'px'; | ||
| const origin = gaugeWrapperEls[0].getBoundingClientRect().left; | ||
| const offset = gaugeToHighlight.getBoundingClientRect().left - origin; | ||
|
|
||
| // Mutate at end to avoid layout thrashing. | ||
| this.highlightEl.style.transform = `translate(${offset}px)`; | ||
| this.stickyHeaderEl.classList.toggle('lh-sticky-header--visible', showStickyHeader); | ||
| this.highlightEl.style.left = offset; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -159,7 +159,6 @@ | |
| .lh-scores-container { | ||
| display: flex; | ||
| flex-direction: column; | ||
| margin-top: var(--topbar-height); | ||
| padding: var(--header-padding); | ||
| position: relative; | ||
| width: 100%; | ||
|
|
@@ -171,25 +170,30 @@ | |
| --plugin-icon-size: 75%; | ||
| --score-container-width: 60px; | ||
| --score-number-font-size: 13px; | ||
| position: fixed; | ||
| position: sticky; | ||
| left: 0; | ||
| right: 0; | ||
| top: var(--topbar-height); | ||
| font-weight: 700; | ||
| display: flex; | ||
| display: none; | ||
brendankenny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| justify-content: center; | ||
| background-color: var(--color-sticky-header-bg); | ||
| border-bottom: 1px solid var(--color-black-200); | ||
| padding-top: var(--score-container-padding); | ||
| padding-bottom: 4px; | ||
| z-index: 1; | ||
| pointer-events: none; | ||
| opacity: 0; | ||
| } | ||
|
|
||
| .lh-sticky-header--visible { | ||
| display: flex; | ||
| pointer-events: auto; | ||
| opacity: 1; | ||
| } | ||
|
|
||
| /* Disable the gauge arc animation for the sticky header, so toggling display: none | ||
| does not play the animation. */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. evidence number 25 that these different uses of the score gauges should share less than they do :) |
||
| .lh-sticky-header .lh-gauge-arc { | ||
| animation: none; | ||
| } | ||
|
|
||
| .lh-sticky-header .lh-gauge__label { | ||
|
|
@@ -201,8 +205,11 @@ | |
| height: 1px; | ||
| background: var(--color-highlighter-bg); | ||
| position: absolute; | ||
| bottom: -1px; | ||
| left: 0; | ||
| bottom: -5px; | ||
| } | ||
|
|
||
| .lh-gauge__wrapper:first-of-type { | ||
| contain: none; | ||
| } | ||
| </style> | ||
| <div class="lh-scores-wrapper"> | ||
|
|
@@ -219,7 +226,7 @@ | |
| <template id="tmpl-lh-topbar"> | ||
| <style> | ||
| .lh-topbar { | ||
| position: fixed; | ||
| position: sticky; | ||
| top: 0; | ||
| left: 0; | ||
| right: 0; | ||
|
|
@@ -256,6 +263,15 @@ | |
| cursor: pointer; | ||
| margin-right: 5px; | ||
| } | ||
| /* | ||
| Some features in the top right drop down menu don't work in the DevTools | ||
| client. They could with some tweaks, but currently they don't. For example: | ||
| Saving as HTML/JSON - does not bring up a file dialog, as one would expect in DevTools. | ||
| also, it saves the AuditsPanel HTML, which is funky. | ||
| */ | ||
| .lh-devtools .lh-export__button { | ||
| display: none; | ||
| } | ||
| .lh-export__button svg { | ||
| fill: var(--lh-export-icon-color); | ||
| } | ||
|
|
||

Uh oh!
There was an error while loading. Please reload this page.