diff --git a/config/techreport.json b/config/techreport.json index 652c3e61..f3cd9007 100644 --- a/config/techreport.json +++ b/config/techreport.json @@ -705,7 +705,6 @@ "#E24070" ], "overrides": { - "WordPress": "#3858e9", "ALL": "#69797e" } }, @@ -719,6 +718,7 @@ "id": "tech_comparison_summary", "table": { "caption": "Summary", + "prefill": "tech", "columns": [ { "key": "technology", @@ -1159,14 +1159,14 @@ "endpoint": "category", "metric": "origins", "label": "Origins", - "description": "Origins analyzed in this category.", + "description": "Total amount of origins analyzed in this category.", "key": "info" }, { "endpoint": "category", "metric": "technologies", "label": "Technologies", - "description": "Amount of technologies in this category.", + "description": "Total number of technologies tagged with this category.", "key": "info" } ], diff --git a/server/routes.py b/server/routes.py index a4b1e10d..2013756c 100644 --- a/server/routes.py +++ b/server/routes.py @@ -88,12 +88,24 @@ def techreportlanding(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" + requested_category = request.args.get("category") or "CMS" + requested_page = request.args.get("page") or 1 + requested_page = int(requested_page) + selected_techs = request.args.get("selected") + selected_rows = request.args.get("rows") or 10 + selected_rows = str(selected_rows) + + last_page = request.args.get("last_page") or False + filters = { "geo": requested_geo, "rank": requested_rank, "app": requested_technologies, "category": requested_category, + "page": requested_page, + "last_page": last_page, + "selected": selected_techs, + "rows": selected_rows, } params = { "geo": requested_geo.replace(" ", "+"), diff --git a/src/js/components/filters.js b/src/js/components/filters.js index b47c4d33..b0055659 100644 --- a/src/js/components/filters.js +++ b/src/js/components/filters.js @@ -78,19 +78,15 @@ class Filters { /* Update the list of technologies */ updateTechnologies() { - this.technologies = DataUtils.filterDuplicates(this.technologies, 'technology'); - /* Get existing tech selectors on the page */ const allTechSelectors = document.querySelectorAll('select[name="tech"]'); - const techNames = this.technologies.map(element => element.app); /* Update the options inside all of the selectors */ allTechSelectors.forEach(techSelector => { techSelector.innerHTML = ''; /* If the technology doesn't exist, throw a warning */ - const techNamesFiltered = techNames; - if(!techNamesFiltered) { + if(!this.technologies) { const errorMsg = document.createElement('p'); errorMsg.textContent = 'Technology not found, please select a different one'; techSelector.before(errorMsg); @@ -98,16 +94,15 @@ class Filters { /* Get a list of technologies */ const techs = this.technologies; - techs.unshift({ technology: 'ALL' }); /* Add one option per technology */ if(document.getElementById('filter-option')) { techs.forEach((technology) => { const optionTmpl = document.getElementById('filter-option').content.cloneNode(true); const option = optionTmpl.querySelector('option'); - const formattedTech = DataUtils.formatAppName(technology.technology); + const formattedTech = DataUtils.formatAppName(technology); option.textContent = formattedTech; - option.value = technology.technology; + option.value = technology; if(formattedTech === techSelector.getAttribute('data-selected')) { option.selected = true; } @@ -161,17 +156,17 @@ class Filters { const all = document.createElement('option'); all.value = 'ALL'; - all.innerHTML = 'ALL'; + all.textContent = 'ALL'; select.append(all); - const sortedCategories = this.categories.sort((a, b) => a.category !== b.category ? a.category < b.category ? -1 : 1 : 0); + const sortedCategories = this.categories.sort((a, b) => a !== b ? a < b ? -1 : 1 : 0); sortedCategories.forEach((category) => { const option = document.createElement('option'); - if(category.category === select.getAttribute('data-selected')) { + if(category === select.getAttribute('data-selected')) { option.selected = true; } - option.value = category.category; - option.innerHTML = category.category; + option.value = category; + option.textContent = category; select.append(option); }); }) diff --git a/src/js/techreport/index.js b/src/js/techreport/index.js index dbd76b31..ae4fe58e 100644 --- a/src/js/techreport/index.js +++ b/src/js/techreport/index.js @@ -90,9 +90,8 @@ class TechReport { break; case 'category': { - const category = this.filters.category || 'CMS'; this.initializeReport(); - this.getCategoryData(category); + this.getCategoryData(); break; } } @@ -277,83 +276,9 @@ class TechReport { }); } - getCategoryData(category) { - const url = `${Constants.apiBase}/categories?category=${category}`; - const apis = [ - { - endpoint: 'cwv', - metric: 'vitals', - parse: DataUtils.parseVitalsData, - }, - { - endpoint: 'lighthouse', - metric: 'lighthouse', - parse: DataUtils.parseLighthouseData, - }, - { - endpoint: 'adoption', - metric: 'adoption', - parse: DataUtils.parseAdoptionData, - }, - { - endpoint: 'page-weight', - metric: 'pageWeight', - parse: DataUtils.parsePageWeightData, - }, - ]; - - fetch(url) - .then(result => result.json()) - .then(result => { - const category = result[0]; - const technologyFormatted = category?.technologies?.join('%2C') - .replaceAll(" ", "%20"); - - const geo = this.filters.geo.replaceAll(" ", "%20"); - const rank = this.filters.rank.replaceAll(" ", "%20"); - const geoFormatted = geo.replaceAll(" ", "%20"); - const rankFormatted = rank.replaceAll(" ", "%20"); - - let allResults = {}; - category.technologies.forEach(tech => allResults[tech] = []); - - Promise.all(apis.map(api => { - const url = `${Constants.apiBase}/${api.endpoint}?technology=${technologyFormatted}&geo=${geoFormatted}&rank=${rankFormatted}&start=latest`; - - return fetch(url) - .then(techResult => techResult.json()) - .then(techResult => { - techResult.forEach(row => { - const parsedRow = { - ...row, - } - - if(api.parse) { - parsedRow[api.metric] = api.parse(parsedRow[api.metric], parsedRow?.date); - } - - const resIndex = allResults[row.technology].findIndex(res => res.date === row.date); - if(resIndex > -1) { - allResults[row.technology][resIndex] = { - ...allResults[row.technology][resIndex], - ...parsedRow - } - } else { - allResults[row.technology].push(parsedRow); - } - }); - }); - })).then(() => { - category.data = { - technologies: allResults, - info: { - origins: category.origins, - technologies: Object.keys(allResults).length, - }, - }; - this.updateCategoryComponents(category); - }); - }); + getCategoryData() { + const callback = this.updateCategoryComponents.bind(this); + DataUtils.fetchCategoryData(this.filters.rows, this.filters, callback) } // Get the information about the selected technology @@ -411,14 +336,31 @@ class TechReport { getFilterInfo() { const filterData = {}; - const filterApis = ['categories', 'technologies', 'ranks', 'geos']; + const filterApis = [ + { + name: 'categories', + endpoint: 'categories?onlyname', + }, + { + name: 'technologies', + endpoint: 'technologies?onlyname', + }, + { + name: 'ranks', + endpoint: 'ranks', + }, + { + name: 'geos', + endpoint: 'geos', + }, + ]; Promise.all(filterApis.map(api => { - const url = `${Constants.apiBase}/${api}`; + const url = `${Constants.apiBase}/${api.endpoint}`; return fetch(url) .then(result => result.json()) - .then(result => filterData[api] = result) + .then(result => filterData[api.name] = result) .catch(error => console.log('Something went wrong', error)); })).then(() => { const FilterComponent = new Filters(filterData, this.filters); diff --git a/src/js/techreport/tableLinked.js b/src/js/techreport/tableLinked.js index 46ba9991..fd2b792c 100644 --- a/src/js/techreport/tableLinked.js +++ b/src/js/techreport/tableLinked.js @@ -9,8 +9,14 @@ class TableLinked { this.submetric = ''; // TODO: Fetch the default one from somewhere this.data = data; this.dataArray = []; + this.selectedTechs = this.getTechsFromURL()?.split(',') || []; + this.rows = filters.rows || 10; this.updateContent(); + this.updateSelectionText(this.getTechsFromURL()); + + const rowCount = document.getElementById('rowsPerPage'); + rowCount?.addEventListener('change', (e) => this.updateRowsPerPage(e)); } // Update content in the table @@ -29,7 +35,11 @@ class TableLinked { this.dataArray = this.dataArray.filter(row => row.length > 0); - if(tbody) { + console.log('set content', content, this.dataArray); + + const isContent = content?.length > 0 || this.dataArray?.length > 0; + + if(tbody && isContent) { // Reset what's in the table before adding new content tbody.innerHTML = ''; @@ -107,37 +117,7 @@ class TableLinked { link.innerHTML = formattedApp; cell.append(link); } else if (column.type === 'checkbox') { - cell = document.createElement('td'); - const formattedApp = DataUtils.formatAppName(app); - const checkbox = document.createElement('input'); - checkbox.setAttribute('type', 'checkbox'); - checkbox.setAttribute('data-app', formattedApp); - checkbox.setAttribute('data-name', `table-${this.id}`); - checkbox.setAttribute('id', `${app}-table-${this.id}`); - checkbox.setAttribute('name', `${app}-table-${this.id}`); - checkbox.addEventListener('change', () => { - const appLinks = document.querySelectorAll('[data-name="selected-apps"]'); - const selectedApps = document.querySelectorAll(`[data-name="table-${this.id}"]:checked`); - - const selectedAppsFormatted = []; - - selectedApps.forEach(selectedApp => { - selectedAppsFormatted.push(selectedApp.dataset.app); - }); - - appLinks.forEach(appLinkEl => { - appLinkEl.setAttribute('href', `/reports/techreport/tech?tech=${selectedAppsFormatted.join(',')}`); - appLinkEl.innerHTML = `Compare ${selectedApps.length} technologies`; - }); - }); - cell.append(checkbox); - - const label = document.createElement('label'); - label.innerHTML = `Select ${formattedApp}`; - label.classList.add('sr-only'); - label.setAttribute('for', `${app}-table-${this.id}`); - cell.append(label); - cell.classList.add('check-cell'); + cell = this.addColumnCheckbox(app); } else if(column.key === 'client') { cell = document.createElement('td'); cell.innerHTML = component.dataset.client; @@ -175,8 +155,168 @@ class TableLinked { } }); + this.disableCheckboxes(this.selectedTechs); + } + } + + getTechsFromURL() { + const url = new URL(window.location); + return url.searchParams.get('selected') || null; + } + + addColumnCheckbox(app) { + const cell = document.createElement('td'); + const formattedApp = DataUtils.formatAppName(app); + const checkbox = document.createElement('input'); + const isChecked = this.isTechSelected(app); + + checkbox.setAttribute('type', 'checkbox'); + checkbox.setAttribute('data-app', formattedApp); + checkbox.setAttribute('data-name', `table-${this.id}`); + checkbox.setAttribute('id', `${app}-table-${this.id}`); + checkbox.setAttribute('name', `${app}-table-${this.id}`); + checkbox.checked = isChecked; + checkbox.addEventListener('change', (e) => this.changeCheckbox(e)); + + cell.append(checkbox); + + const label = document.createElement('label'); + label.textContent = `Select ${formattedApp}`; + label.classList.add('sr-only'); + label.setAttribute('for', `${app}-table-${this.id}`); + cell.append(label); + cell.classList.add('check-cell'); + + return cell; + } + + // Set selected content + isTechSelected(app) { + const urlSelected = this.getTechsFromURL(); + return urlSelected?.includes(app) || false; + } + + // Checkbox is clicked + changeCheckbox(e) { + if(e.target.checked) { + this.tickCheckbox(e.target.dataset.app); + } else { + this.untickCheckbox(e.target.dataset.app); + } + } + + // Checkbox is checked + tickCheckbox(app) { + if(!this.selectedTechs || !this.selectedTechs.includes(app)) { + this.selectedTechs.push(app); + } + + const allSelectedApps = this.selectedTechs.join(','); + + this.updatePaginationUrl('selected', allSelectedApps); + this.updateSelectionText(allSelectedApps); + this.updateURL('selected', allSelectedApps); + this.disableCheckboxes(this.selectedTechs); + } + + // Checkbox is unchecked + untickCheckbox(app) { + this.selectedTechs = this.selectedTechs.filter(selected => selected !== app); + const selectedTechsStr = this.selectedTechs.join(','); + this.updateSelectionText(selectedTechsStr); + this.updatePaginationUrl('selected', selectedTechsStr); + this.updateURL('selected', selectedTechsStr); + this.disableCheckboxes(this.selectedTechs); + } + + disableCheckboxes(techs) { + const checkbox = 'input[type="checkbox"][data-name="table-tech_comparison_summary"]'; + if(techs.length >= 10) { + const checkboxes = document.querySelectorAll(`${checkbox}:not(:checked)`); + checkboxes.forEach(box => box.setAttribute('disabled', 'true')); + } else { + const checkboxes = document.querySelectorAll(`${checkbox}[disabled]`); + checkboxes.forEach(box => box.removeAttribute('disabled')); } } + + updateURL(param, value) { + const url = new URL(window.location); + url.searchParams.set(param, value); + window.history.replaceState(null, null, url); + } + + updateSelectionText(allSelectedApps) { + const appLinks = document.querySelectorAll('[data-name="selected-apps"]'); + appLinks.forEach(appLinkEl => { + let label = 'Compare all technologies on this page'; + let href = ''; + if(this.selectedTechs.length > 0) { + href = `/reports/techreport/tech?tech=${allSelectedApps}`; + label = `Compare ${this.selectedTechs.length} technologies`; + } else if(this.data.technologies) { + href = `/reports/techreport/tech?tech=${Object.keys(this.data.technologies).join(',')}`; + href = encodeURI(href); + } + + appLinkEl.setAttribute('href', href); + appLinkEl.innerHTML = label; + }); + + if(this.selectedTechs.length > 0) { + const selectionOverview = document.querySelector('[data-name="selected-apps-overview"]'); + selectionOverview.innerHTML = ''; + this.selectedTechs.forEach(tech => { + const li = document.createElement('li'); + const remove = document.createElement('button'); + const label = document.createElement('img'); + label.setAttribute('alt', 'Remove'); + label.setAttribute('src', '/static/img/close-filters.svg'); + remove.textContent = tech; + remove.addEventListener('click', () => { + this.untickCheckbox(tech); + const checkbox = document.querySelector(`input[type="checkbox"][data-name="table-tech_comparison_summary"][data-app="${tech}"]`); + checkbox.checked = false; + }); + remove.append(label); + li.append(remove); + selectionOverview.append(li); + }); + document.querySelector('.selected-apps')?.setAttribute('style', 'display: block;'); + } else { + document.querySelector('.selected-apps')?.setAttribute('style', 'display: none;'); + } + + } + + updatePaginationUrl(param, value) { + const pagination = ['[data-pagination="next"] a', '[data-pagination="previous"] a']; + pagination.forEach((element) => { + const paginationEl = document.querySelector(element); + if(paginationEl) { + let params = paginationEl.getAttribute('href').split('?')[1]; + params = new URLSearchParams(params); + params.set(param, value); + paginationEl.setAttribute('href', `/reports/techreport/category?${params}`); + } + }); + } + + updateRowsPerPage(e) { + const rows = parseInt(e.target.value); + this.rows = rows; + this.updateURL('rows', rows); + this.updatePaginationUrl('rows', rows); + + DataUtils.fetchCategoryData(rows, this.pageFilters, this.updateRowData.bind(this)); + } + + updateRowData(result) { + this.data = result.data; + this.updateContent(); + const rowsAnnouncement = document.getElementById('rows-announcement'); + rowsAnnouncement.innerHTML = `Showing ${this.rows} rows.`; + } } export default TableLinked; diff --git a/src/js/techreport/utils/data.js b/src/js/techreport/utils/data.js index 10eefbfd..2b064e5f 100644 --- a/src/js/techreport/utils/data.js +++ b/src/js/techreport/utils/data.js @@ -1,3 +1,5 @@ +import { Constants } from "./constants"; + const parseVitalsData = (metric, date) => { return metric.map(submetric => { return { @@ -100,6 +102,98 @@ const getLighthouseScoreCategories = (score, brackets) => { } +const fetchCategoryData = (rows, filters, callback) => { + const geoFormatted = encodeURI(filters.geo); + const rankFormatted = encodeURI(filters.rank); + const categoryFormatted = encodeURI(filters.category); + const url = `${Constants.apiBase}/categories?category=${categoryFormatted}&geo=${geoFormatted}&rank=${rankFormatted}`; + const apis = [ + { + endpoint: 'cwv', + metric: 'vitals', + parse: DataUtils.parseVitalsData, + }, + { + endpoint: 'lighthouse', + metric: 'lighthouse', + parse: DataUtils.parseLighthouseData, + }, + { + endpoint: 'adoption', + metric: 'adoption', + parse: DataUtils.parseAdoptionData, + }, + { + endpoint: 'page-weight', + metric: 'pageWeight', + parse: DataUtils.parsePageWeightData, + }, + ]; + + const pageNr = filters.page; + fetch(url) + .then(result => result.json()) + .then(result => { + const category = result[0]; + const firstTechNr = (pageNr - 1) * rows; + const lastTechNr = pageNr * rows; + const paginatedTechs = category?.technologies?.slice(firstTechNr, lastTechNr); + + const technologyFormatted = encodeURI(paginatedTechs?.join('%2C')); + + const compare = document.querySelector('[data-name="selected-apps"]'); + compare.setAttribute('href', `/reports/techreport/tech?tech=${technologyFormatted}`); + + let allResults = {}; + paginatedTechs.forEach(tech => allResults[tech] = []); + + Promise.all(apis.map(api => { + const url = `${Constants.apiBase}/${api.endpoint}?technology=${technologyFormatted}&geo=${geoFormatted}&rank=${rankFormatted}&start=latest`; + + return fetch(url) + .then(techResult => techResult.json()) + .then(techResult => { + techResult.forEach(row => { + const parsedRow = { + ...row, + } + + if(api.parse) { + parsedRow[api.metric] = api.parse(parsedRow[api.metric], parsedRow?.date); + } + + const resIndex = allResults[row.technology].findIndex(res => res.date === row.date); + if(resIndex > -1) { + allResults[row.technology][resIndex] = { + ...allResults[row.technology][resIndex], + ...parsedRow + } + } else { + allResults[row.technology].push(parsedRow); + } + }); + }); + })).then(() => { + category.data = { + technologies: allResults, + info: { + origins: category.origins, + technologies: category?.technologies?.length, + }, + }; + + /* Update the pagination info */ + const current = document.querySelectorAll('[data-page="current"]'); + const total = document.querySelectorAll('[data-page="total"]'); + current.forEach(c => c.textContent = pageNr); + total.forEach(t => t.textContent = Math.ceil(category?.technologies?.length / rows)); + + /* Update components */ + callback(category); + }); + }); +} + export const DataUtils = { parseVitalsData, parseLighthouseData, @@ -108,4 +202,5 @@ export const DataUtils = { filterDuplicates, getLighthouseScoreCategories, formatAppName, + fetchCategoryData, }; diff --git a/static/css/techreport/techreport.css b/static/css/techreport/techreport.css index a4506ee8..1f4dd878 100644 --- a/static/css/techreport/techreport.css +++ b/static/css/techreport/techreport.css @@ -197,6 +197,29 @@ nav { } /* Report navigation */ +.skip { + position: absolute; + z-index: 999; + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + white-space: nowrap; + width: 1px; + text-decoration: none; +} + +.skip:focus-visible { + clip: unset; + clip-path: unset; + width: unset; + height: unset; + overflow: unset; + white-space: unset; + top: 0.35rem; + left: 0.35rem; +} + .report-navigation { color: var(--color-text); background-color: var(--color-card-background); @@ -308,8 +331,9 @@ nav { height: 100%; } -#page-filters button[type="submit"]:is(:hover, :focus-visible) { +.main-btn:is(:hover, :focus-visible) { background-color: var(--color-button-background-hover); + color: var(--color-button-text); } #page-filters .meta { @@ -350,14 +374,18 @@ nav { height: auto; } -#page-filters button[type="submit"] { +.main-btn { background-color: var(--color-button-background); border-radius: 4px; color: var(--color-button-text); font-weight: 400; font-size: 1rem; - border: none; padding: 0.65rem; + transition: background 0.15s ease-in; +} + +#page-filters button[type="submit"] { + border: none; width: 100%; margin-top: 1rem; transition: background 0.15s ease-in; @@ -527,7 +555,7 @@ nav { } .breakdown { - margin-top: 2.5rem; + margin-top: 1rem; } .breakdown legend { @@ -1115,6 +1143,27 @@ select { ); } +.table-pagination { + display: flex; + justify-content: space-between; + padding: 1.5rem 0 1rem; + margin-top: 2.5rem; + border-top: 1px solid var(--color-card-border); +} + +.table-page-info p { + text-align: center; +} + +.table-row-count { + margin-top: 0.5rem; + text-align: center; +} + +.table-row-count label { + font-weight: normal; +} + /* Metric components */ .component-heading-wrapper { display: flex; @@ -1175,7 +1224,7 @@ select { } .intro .heading-wrapper .breakdown { - min-width: 17ch; + width: 10rem; } .intro h2 { @@ -1198,7 +1247,10 @@ select { } .intro .app-description { - margin-bottom: 2rem; + margin-top: 1rem; + line-height: 160%; + display: block; + min-height: 3.5rem; } .intro ul { @@ -1207,7 +1259,7 @@ select { } .intro .categories { - margin: -1rem 0 3.5rem; + margin: 1rem 0; display: flex; flex-wrap: wrap; gap: 0.5rem; @@ -1257,6 +1309,14 @@ h2.summary-heading { margin-bottom: 0.5rem; } +.table-descr { + margin-bottom: 1.5rem; +} + +.table-descr p + p { + margin-top: 0.25rem; +} + .report-section > .card { margin-top: 2rem; } @@ -1511,6 +1571,11 @@ path.highcharts-tick { --progess-color-border: #ba8d1e; } +.progress-circle .summary-linked-value { + background-color: var(--color-card-background); + border-radius: 2rem; +} + /* ----------------------- */ /* ---- Accessibility ---- */ /* ----------------------- */ @@ -1616,6 +1681,51 @@ path.highcharts-tick { position: relative; } +.table-info-grid { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1.5rem; +} + +.cta-link[data-name="selected-apps"] { + border: 1.5px solid var(--color-teal-dark); + background-color: transparent; + color: var(--color-teal-dark); +} + +.selected-apps { + margin-top: 1.5rem; + font-size: var(--font-size-small); +} + +.selected-apps p { + font-weight: 600; + margin-bottom: 0.25rem; +} + +.selected-apps ul li { + list-style-type: none; + display: inline-block; + margin-bottom: 0.5em; +} + +.selected-apps ul li:not(:last-of-type) { + margin-right: 1em; +} + +.selected-apps ul li button { + padding: 0.1em 0.5em; + border-radius: 2rem; + border: 1px solid var(--color-text-lighter); + background-color: transparent; + color: var(--color-text-lighter); +} + +.selected-apps ul li button img { + padding-left: 0.25em; +} + /* ----------------------- */ /* ---- Media queries ---- */ /* ----------------------- */ @@ -1648,6 +1758,10 @@ path.highcharts-tick { position: relative; top: 2.25rem; } + + .intro .heading-wrapper { + display: block; + } } @media screen and (max-width: 50rem) { diff --git a/templates/main.html b/templates/main.html index 29add03c..c9c41782 100644 --- a/templates/main.html +++ b/templates/main.html @@ -189,7 +189,7 @@ {% block report_navigation %}{% endblock %} -
+
{% block main %}{% endblock %}
diff --git a/templates/techreport/category.html b/templates/techreport/category.html index 80177256..319a5974 100644 --- a/templates/techreport/category.html +++ b/templates/techreport/category.html @@ -41,7 +41,7 @@ - + {% include "techreport/components/filter_meta.html" %} @@ -62,13 +62,6 @@ {% block section %} {{ super() }}
-
-
-

Beta

-

We're still actively working on the categories page.

-
-
-
@@ -105,6 +98,16 @@

Summary

data-api="cwv,lighthouse,page-weight" >

Technologies

+
+

+ Overview of the latest Core Web Vitals of all technologies within this category, sorted by number of origins. + Select up to 10 technologies to compare their Core Web Vitals, Lighthouse scores, adoption, and page weight over time, or view details about a single technology. +

+

+ Currently showing page 1 of 1, based on the latest data (). +

+
+ {% set component = tech_report_page.config.tech_comparison_summary %} {% set id = component.id %} {% set client = request.args.get('client', '') or 'mobile' %} @@ -113,14 +116,61 @@

Technologies

{% set sort_key = "origins" %} {% set sort_order = "desc" %} -

- Latest data: -

+ Compare all technologies on this page + +
+

+ Selected technologies: +

+
    +
    {% set table = component.table %} {% include "techreport/components/table_linked.html" %} - Compare technologies +
    + {% set filters = tech_report_page.filters %} +

    + {% if filters.page and filters.page > 1 %} + Previous page + {% endif %} +

    +
    +

    + Page 1 of 1 +

    +
    + + +
    +

    + +

    +
    +

    + {% if not filters.last_page and filters.last_page == False %} + Next page + {% endif %} +

    +
    {% endblock %} diff --git a/templates/techreport/components/table_linked.html b/templates/techreport/components/table_linked.html index d5961e65..abe263f4 100644 --- a/templates/techreport/components/table_linked.html +++ b/templates/techreport/components/table_linked.html @@ -43,11 +43,40 @@ {% if table.rows %} {% for row in table.rows %} - {% if row.header == "true" %} - {{ row.name }} - {% else %} - {{ row.name }} - {% endif %} + + {% if row.header == "true" %} + {{ row.name }} + {% else %} + {{ row.name }} + {% endif %} + + {% endfor %} + {% elif table.prefill == "tech" %} + {% for tech in technologies %} + + {% for column in table.columns %} + {% if column.key == "technology" %} + + + {{ tech }} + + {% elif column.viz == "progress" %} + + --% + + {% elif column.key == "client" %} + -- + {% else %} + + {% if column.viz == "progress-circle" %} + -- + {% else %} + -- + {% endif %} + + {% endif %} + {% endfor %} + {% endfor %} {% endif %} diff --git a/templates/techreport/report.html b/templates/techreport/report.html index 900e7ad4..9d09064a 100644 --- a/templates/techreport/report.html +++ b/templates/techreport/report.html @@ -31,7 +31,7 @@
    -
    +
    {% block section %}{% endblock %}
    diff --git a/templates/techreport/techreport.html b/templates/techreport/techreport.html index 1216641e..fbbe5ef8 100644 --- a/templates/techreport/techreport.html +++ b/templates/techreport/techreport.html @@ -11,6 +11,8 @@ {% endblock %} {% block custom_navigation %} + +