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

Category dashboard with collapsible filters #934

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d3987bf
custom techreport header
sarahfossheim Aug 28, 2024
b21cc6e
add prototype categories page
sarahfossheim Aug 28, 2024
04628eb
add filters in sidebar and change page based on amount of techs
sarahfossheim Aug 30, 2024
07171f7
add new nav and filter structure (wip
sarahfossheim Sep 4, 2024
c312252
responsive filters prototype
sarahfossheim Sep 17, 2024
706d6f0
wip: add collapsible filter sidebar
sarahfossheim Sep 24, 2024
fa8c689
fix bug with double close button
sarahfossheim Sep 24, 2024
e58f7ce
add aria
sarahfossheim Sep 24, 2024
7f837c1
merge in master
sarahfossheim Sep 25, 2024
6b24d96
add checkboxes on category page, update texts, minor bugfixes
sarahfossheim Oct 18, 2024
cf7c0e8
select correct technology in comparison view, remove category selecto…
sarahfossheim Oct 23, 2024
bf11a13
fix css linting
sarahfossheim Oct 23, 2024
ce8df02
fix linting
sarahfossheim Oct 23, 2024
bc5af01
Merge branch 'main' into cwvtech-category-dashboard
sarahfossheim Oct 30, 2024
b74de3b
fix linting
sarahfossheim Oct 30, 2024
728d42f
Merge remote-tracking branch 'refs/remotes/origin/cwvtech-category-da…
sarahfossheim Oct 30, 2024
0d062cc
fix sorting order multi apps
sarahfossheim Oct 30, 2024
aea33e1
sort table alternative correctly
sarahfossheim Nov 8, 2024
7aaf182
merge main into branch
sarahfossheim Nov 18, 2024
fea35f0
close <nav>
sarahfossheim Nov 19, 2024
c16fe45
merge master into branch
sarahfossheim Dec 3, 2024
3987516
update tests
sarahfossheim Dec 18, 2024
5a45a40
format tests
sarahfossheim Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 110 additions & 12 deletions config/techreport.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
"name": "Technology Report",
"summary": "The Core Web Vitals Technology Report is a dashboard combining the powers of real-user experiences in the [Chrome User Experience Report (CrUX)](https://developers.google.com/web/tools/chrome-user-experience-report/) dataset with web technology detections available in HTTP Archive, to allow analysis of the way websites are both built and experienced.",
"config": {
"default_apps": {
"drilldown": [ "ALL" ],
"comparison": [ "ALL", "WordPress", "Wix", "Next.js" ]
},
"default_category": "CMS",
"cwv_subcategories": [
"CLS",
"LCP",
Expand Down Expand Up @@ -39,7 +44,7 @@
"id": "landing",
"title": "Technology Report",
"subtitle": "Report",
"description": "This is placeholder text about how the report works",
"description": "",
"data": {},
"filters": {
"technologies": ["WordPress", "Squarespace", "Drupal"]
Expand Down Expand Up @@ -263,7 +268,7 @@
},
"good_cwv_timeseries": {
"title": "Good Core Web Vitals over time",
"description": "Placeholder - combination of several metrics",
"description": "The percentage of origins passing all three Core Web Vitals (LCP, INP, CLS) with a good experience. Note that if an origin is missing INP data, it's assessed based on the performance of the remaining metrics.",
"id": "good_cwv_timeseries",
"endpoint": "vitals",
"metric": "good_pct",
Expand Down Expand Up @@ -425,7 +430,7 @@
},
"lighthouse_timeseries": {
"title": "Lighthouse over time",
"description": "Placeholder text",
"description": "",
"id": "lighthouse_timeseries",
"endpoint": "lighthouse",
"metric": "median_score_pct",
Expand Down Expand Up @@ -557,7 +562,7 @@
},
"weight_timeseries": {
"title": "Weight over time",
"description": "Placeholder text",
"description": "",
"id": "weight_timeseries",
"summary": true,
"endpoint": "pageWeight",
Expand Down Expand Up @@ -695,7 +700,8 @@
"#E24070"
],
"overrides": {
"WordPress": "#fff000"
"WordPress": "#3858e9",
"ALL": "#69797e"
}
},
"default": {
Expand Down Expand Up @@ -857,7 +863,7 @@
},
"lighthouse_timeseries": {
"title": "Lighthouse over time",
"description": "Placeholder text",
"description": "",
"id": "lighthouse_timeseries",
"endpoint": "lighthouse",
"metric": "median_score_pct",
Expand Down Expand Up @@ -952,7 +958,7 @@
},
"weight_timeseries": {
"title": "Page weight over time",
"description": "Placeholder text",
"description": "",
"id": "weight_timeseries",
"endpoint": "pageWeight",
"metric": "median_bytes",
Expand Down Expand Up @@ -1047,7 +1053,7 @@
},
"adoption_timeseries": {
"title": "Origins over time",
"description": "Placeholder text",
"description": "",
"id": "adoption_timeseries",
"endpoint": "adoption",
"metric": "origins",
Expand Down Expand Up @@ -1118,6 +1124,89 @@
}
},
"description": "Comparison placeholder"
},
"category": {
"id": "category",
"title": "Categories",
"subtitle": "Technology Report",
"config": {
"default": {
"category": "CMS",
"app": ["ALL", "WordPress", "Drupal"],
"series": {
"breakdown": "app"
}
},
"tech_comparison_summary": {
"id": "tech_comparison_summary",
"table": {
"caption": "Summary",
"columns": [
{
"key": "selectTech",
"name": "Select technology",
"hiddenName": true,
"type": "checkbox"
},
{
"key": "technology",
"name": "Tech",
"type": "heading"
},
{
"key": "origins",
Copy link
Member

Choose a reason for hiding this comment

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

Values appear as 3222201 which can be harder to read. Could we format with commas, ie 3,222,201? It'd also be helpful for comparing values if the text in this column is right-aligned, like a spreadsheet.

Copy link
Member

Choose a reason for hiding this comment

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

In addition to the number of origins, I think a primary use case for this data would be to see the proportion of origins that adopt this category relative to all origins that adopt this category.

For example, there are 3.2M WordPress origins and the /categories endpoint says that there are 8.8M origins in the CMS category, so we could show that "market share" value as 36%.

What do you think would be the best way to show the 36% stat? It could be a new column, combined with the # of origins somehow, or replace the # origin column entirely. Alternatively, would you hate a pie chart at the top of the page?

Aside: I suspect the 8.8M number is wrong as it's double-counting origins that use multiple CMSs. WordPress market share should be more like 75% of the CMS category. This is something we'll need to fix in BigQuery.

cc @max-ostapenko

Copy link
Member

Choose a reason for hiding this comment

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

When filtering to the top 1k rank, I'm seeing technologies with "null" origin counts

https://technology-report-dot-httparchive.uk.r.appspot.com/reports/techreport/category?tech=&geo=ALL&rank=Top+1k&category=CMS

image

"name": "Origins",
"breakdown": "subcategory",
"subcategory": "adoption",
"endpoint": "adoption",
"metric": "origins"
},
{
"key": "good_pct",
"name": "Good CWV",
"breakdown": "subcategory",
"subcategory": "overall",
"suffix": "%",
"className": "main-cell pct-value",
"endpoint": "vitals",
"metric": "good_pct"
},
{
"key": "good_pct",
"name": "LCP",
"breakdown": "subcategory",
"subcategory": "LCP",
"suffix": "%",
"endpoint": "vitals",
"metric": "good_pct"
},
{
"key": "good_pct",
"name": "INP",
"breakdown": "subcategory",
"subcategory": "INP",
"suffix": "%",
"endpoint": "vitals",
"metric": "good_pct"
},
{
"key": "good_pct",
"name": "CLS",
"breakdown": "subcategory",
"subcategory": "CLS",
"suffix": "%",
"endpoint": "vitals",
"metric": "good_pct"
},
{
"key": "client",
"name": "Client",
"className": "client"
}
]
}
}
}
}
},

Expand Down Expand Up @@ -1145,6 +1234,9 @@
}
},
"vitals": {
"general": {
"description": "Each of the Core Web Vitals represents a distinct facet of the user experience, is measurable in the field, and reflects the real-world experience of a critical user-centric outcome. A good threshold to measure is the 75th percentile of page loads, segmented across mobile and desktop devices."
},
"overall": {
"label": "Overall Core Web Vitals",
"title": "Passes Core Web Vitals",
Expand Down Expand Up @@ -1178,23 +1270,29 @@
}
},
"pageWeight": {
"general": {
"description": ""
},
"images": {
"title": "Image Weight",
"description": "todo"
"description": ""
},
"js": {
"title": "JavaScript Transfer Size",
"description": "todo"
"description": ""
},
"total": {
"title": "Total Page Weight",
"description": "todo"
"description": ""
}
},
"adoption": {
"general": {
"description": ""
},
"adoption": {
"title": "Adoption",
"description": "Todo"
"description": "The amount of origins using this technology over time."
}
}
},
Expand Down
61 changes: 59 additions & 2 deletions server/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,68 @@ def reports():

return render_template("reports.html", reports=all_reports)


@app.route("/reports/techreport/<page_id>", strict_slashes=False)
def techreport(page_id):
def techreportlanding(page_id):
# Needed for the header dropdown
all_reports = report_util.get_reports()

# Get the configuration for the tech report
tech_report = tech_report_util.get_report()

# Get the settings for the current page
active_tech_report = tech_report.get("pages").get(page_id)

# Add the technologies requested in the URL to the filters
# Use the default configured techs as fallback
# Use ["ALL"] if there is nothing configured
requested_technologies = active_tech_report.get("config").get("default").get(
"app"
) or ["ALL"]

if request.args.get("tech"):
requested_technologies = request.args.get("tech").split(",")

# Get the filters
requested_geo = request.args.get("geo") or "ALL"
requested_rank = request.args.get("rank") or "ALL"
requested_category = request.args.get("category") or "ALL"
filters = {
"geo": requested_geo,
"rank": requested_rank,
"app": requested_technologies,
"category": requested_category,
}

active_tech_report["filters"] = filters

return render_template(
"techreport/%s.html" % page_id,
active_page=page_id,
tech_report_labels=tech_report.get("labels"),
tech_report_config=tech_report.get("config"),
tech_report_page=active_tech_report,
custom_navigation=True,
reports=all_reports,
)

@app.route("/reports/techreport/tech", strict_slashes=False)
def techreport():
# Needed for the header dropdown
all_reports = report_util.get_reports()

# Get the configuration for the tech report
tech_report = tech_report_util.get_report()

# Get the current page_id
requested_technologies = ["ALL"]
if request.args.get("tech"):
requested_technologies = request.args.get("tech").split(",")

if len(requested_technologies) > 1:
page_id = "comparison"
else:
page_id = "drilldown"

# Get the settings for the current page
active_tech_report = tech_report.get("pages").get(page_id)

Expand All @@ -91,20 +144,24 @@ def techreport(page_id):
# Get the filters
requested_geo = request.args.get("geo") or "ALL"
requested_rank = request.args.get("rank") or "ALL"
requested_category = request.args.get("category") or "ALL"
filters = {
"geo": requested_geo,
"rank": requested_rank,
"app": requested_technologies,
"category": requested_category,
}

active_tech_report["filters"] = filters

return render_template(
"techreport/%s.html" % page_id,
active_page=page_id,
requested_page="technology",
tech_report_labels=tech_report.get("labels"),
tech_report_config=tech_report.get("config"),
tech_report_page=active_tech_report,
custom_navigation=True,
reports=all_reports,
)

Expand Down
2 changes: 1 addition & 1 deletion src/js/components/drilldownHeader.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DataUtils } from "../techreport/utils/data";

function setTitle(title) {
const mainTitle = document.querySelector('h2 span.main-title');
const mainTitle = document.querySelector('h1 span.main-title');
mainTitle.textContent = title;
}

Expand Down
Loading
Loading