Skip to content

Commit

Permalink
Missing one implementation draft
Browse files Browse the repository at this point in the history
  • Loading branch information
KyleJu committed Dec 14, 2024
1 parent 2c9faf9 commit 0445599
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 39 deletions.
14 changes: 10 additions & 4 deletions frontend/src/static/js/components/webstatus-gchart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export class WebstatusGChart extends LitElement {
})
dataObj: WebStatusDataObj | undefined;

@property({attribute: false})
hasMax!: boolean;

@property({state: true, type: Object})
dataTable:
| google.visualization.DataTable
Expand Down Expand Up @@ -142,10 +145,13 @@ export class WebstatusGChart extends LitElement {
augmentOptions(
options: google.visualization.ComboChartOptions,
): google.visualization.ComboChartOptions {
options = {
...options,
tooltip: {trigger: 'selection'},
};
if (!this.hasMax) {
options = {
...options,
tooltip: {trigger: 'selection'},
};
return options;
}

const numColumns = this.dataTable!.getNumberOfColumns();
// The number of series is the number of columns with role 'data'.
Expand Down
157 changes: 122 additions & 35 deletions frontend/src/static/js/components/webstatus-stats-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ import {apiClientContext} from '../contexts/api-client-context.js';
import './webstatus-gchart';
import {WebStatusDataObj} from './webstatus-gchart.js';

/** Generate a key for globalFeatureSupport. */
function globalFeatureSupportKey(browser: BrowsersParameter): string {
/** Generate a key for globalFeatureSupport and missingOneImplementationMap. */
function statsDataKey(browser: BrowsersParameter): string {
return browser;
}

Expand All @@ -47,7 +47,7 @@ export class StatsPage extends LitElement {
apiClient!: APIClient;

@state()
globalFeatureSupportBrowsers: BrowsersParameter[] = ALL_BROWSERS;
supportedBrowsers: BrowsersParameter[] = ALL_BROWSERS;

@state()
// Default: Date.now() - 1 year.
Expand All @@ -57,7 +57,7 @@ export class StatsPage extends LitElement {
endDate: Date = new Date(); // Today

// Map from browser-channel to global feature support.
// The key is generated by globalFeatureSupportKey().
// The key is generated by statsDataKey().
@state()
globalFeatureSupport = new Map<string, Array<BrowserReleaseFeatureMetric>>();

Expand All @@ -67,6 +67,15 @@ export class StatsPage extends LitElement {
@state()
globalFeatureSupportChartDataObj: WebStatusDataObj | undefined;

@state()
missingOneImplementationMap = new Map<
string,
Array<BrowserReleaseFeatureMetric>
>();

@state()
missingOneImplementationChartDataObj: WebStatusDataObj | undefined;

static get styles(): CSSResultGroup {
return [
SHARED_STYLES,
Expand Down Expand Up @@ -101,7 +110,7 @@ export class StatsPage extends LitElement {
) as Array<SlMenuItem>;

// Build the list of values of checked menu-items.
this.globalFeatureSupportBrowsers = menuItemsArray
this.supportedBrowsers = menuItemsArray
.filter(menuItem => menuItem.checked)
.map(menuItem => menuItem.value) as BrowsersParameter[];
// Regenerate data and redraw. We should instead just filter it.
Expand All @@ -121,6 +130,8 @@ export class StatsPage extends LitElement {

globalFeatureSupportResizeObserver: ResizeObserver | null = null;

missingOneImplementationResizeObserver: ResizeObserver | null = null;

setupResizeObserver() {
// Set up ResizeObserver one time to redraw chart when container resizes.
if (!this.globalFeatureSupportResizeObserver) {
Expand All @@ -133,6 +144,17 @@ export class StatsPage extends LitElement {
});
this.globalFeatureSupportResizeObserver.observe(gfsChartElement);
}

if (!this.missingOneImplementationResizeObserver) {
const gfsChartElement = this.shadowRoot!.getElementById(
'missing-one-implementation-chart',
);
if (!gfsChartElement) return;
this.missingOneImplementationResizeObserver = new ResizeObserver(() => {
// TODO: trigger update based on resize.
});
this.missingOneImplementationResizeObserver.observe(gfsChartElement);
}
}

async _fetchGlobalFeatureSupportData(
Expand All @@ -149,18 +171,46 @@ export class StatsPage extends LitElement {
)) {
// Append the new data to existing data
const existingData =
this.globalFeatureSupport.get(globalFeatureSupportKey(browser)) || [];
this.globalFeatureSupport.set(globalFeatureSupportKey(browser), [
this.globalFeatureSupport.get(statsDataKey(browser)) || [];
this.globalFeatureSupport.set(statsDataKey(browser), [
...existingData,
...page,
]);
}
this.globalFeatureSupportChartDataObj =
this.createGlobalFeatureSupportDataFromMap();
this.globalFeatureSupportChartDataObj = this.createDisplayDataFromMap(
this.globalFeatureSupport,
true,
);
});
await Promise.all(promises); // Wait for all browsers to finish
}

// TODO: Finish this method with real data.
async _fetchMissingOneImplemenationCounts() {
const browserReleaseFeatureMetric: BrowserReleaseFeatureMetric = {
count: 8,
timestamp: new Date(Date.now() - 200 * 24 * 60 * 60 * 1000).toISOString(),
};
const browserReleaseFeatureMetric1: BrowserReleaseFeatureMetric = {
count: 5,
timestamp: new Date(Date.now() - 150 * 24 * 60 * 60 * 1000).toISOString(),
};
let count: number = 0;
ALL_BROWSERS.map(browser => {
browserReleaseFeatureMetric.count = 8 + count;
browserReleaseFeatureMetric1.count = 5 + count;
count++;
this.missingOneImplementationMap.set(statsDataKey(browser), [
{...browserReleaseFeatureMetric},
{...browserReleaseFeatureMetric1},
]);
});
this.missingOneImplementationChartDataObj = this.createDisplayDataFromMap(
this.missingOneImplementationMap,
false,
);
}

constructor() {
super();

Expand All @@ -181,31 +231,35 @@ export class StatsPage extends LitElement {
startDate,
endDate,
);
await this._fetchMissingOneImplemenationCounts();
return this.globalFeatureSupport;
},
});
}

// Make a DataTable from the data in globalFeatureSupport
createGlobalFeatureSupportDataFromMap(): WebStatusDataObj {
// Get the list of browsers from globalFeatureSupport
const browsers = this.globalFeatureSupportBrowsers;
// Make a DataTable from the target data map.
createDisplayDataFromMap(
targetMap: Map<string, Array<BrowserReleaseFeatureMetric>>,
addMax: boolean,
): WebStatusDataObj {
// Get the list of supported browsers.
const browsers = this.supportedBrowsers;

const dataObj: WebStatusDataObj = {cols: [], rows: []};
dataObj.cols.push({type: 'date', label: 'Date', role: 'domain'});
for (const browser of browsers) {
dataObj.cols.push({type: 'number', label: browser, role: 'data'});
}
dataObj.cols.push({type: 'number', label: 'Max features', role: 'data'});
if (addMax) {
dataObj.cols.push({type: 'number', label: 'Max features', role: 'data'});
}

// Map from date to an object with counts for each browser
const dateToBrowserDataMap = new Map<number, {[key: string]: number}>();

// Merge data across all browsers into one array of rows.
for (const browser of browsers) {
const data = this.globalFeatureSupport.get(
globalFeatureSupportKey(browser),
);
const data = targetMap.get(statsDataKey(browser));
if (!data) continue;
for (const row of data) {
if (!row) continue;
Expand All @@ -218,36 +272,42 @@ export class StatsPage extends LitElement {
browserCounts[browser] = featureCount;
}
}

// Create array of dateToBrowserDataMap entries and sort by dateSeconds
const data = Array.from(dateToBrowserDataMap.entries()).sort(
([d1], [d2]) => d1 - d2,
);

// For each date, add a row to the dataTable
// Accumulate the max count across all browsers.
let max = 0;
for (const datum of data) {
const dateSeconds = datum[0];
const date = new Date(dateSeconds);
const browserCounts = datum[1];
// Make an array of browser counts, in the order of browsers.
// If the browser is not in the browserCounts, add null.
const browserCountArray = browsers.map(browser => {
if (browserCounts[browser]) {
max = Math.max(max, browserCounts[browser]);
return browserCounts[browser];
const browserCountArray = browsers.map(
browser => browserCounts[browser] || null,
);

if (addMax) {
let maxCount = 0;
for (const count of browserCountArray) {
if (count) {
maxCount = Math.max(maxCount, count);
}
}
return null;
});
dataObj.rows.push([date, ...browserCountArray, max]);
dataObj.rows.push([date, ...browserCountArray, maxCount]);
} else {
dataObj.rows.push([date, ...browserCountArray]);
}
}
return dataObj;
}

generateGlobalFeatureSupportChartOptions(): google.visualization.LineChartOptions {
generatedisplayDataChartOptions(
vAxisTitle: string,
): google.visualization.LineChartOptions {
// Compute seriesColors from selected browsers and BROWSER_ID_TO_COLOR
const selectedBrowsers = this.globalFeatureSupportBrowsers;
const selectedBrowsers = this.supportedBrowsers;
const seriesColors = [...selectedBrowsers, 'total'].map(browser => {
const browserKey = browser as keyof typeof BROWSER_ID_TO_COLOR;
return BROWSER_ID_TO_COLOR[browserKey];
Expand All @@ -264,7 +324,7 @@ export class StatsPage extends LitElement {
},
vAxis: {
minValue: 0,
title: 'Number of features supported',
title: vAxisTitle,
format: '#,###',
},
legend: {position: 'top'},
Expand Down Expand Up @@ -331,10 +391,13 @@ export class StatsPage extends LitElement {
return html`
<webstatus-gchart
id="global-feature-support-chart"
?hasMax="${true}"
.containerId="${'global-feature-support-chart-container'}"
.chartType="${'LineChart'}"
.dataObj="${this.globalFeatureSupportChartDataObj}"
.options="${this.generateGlobalFeatureSupportChartOptions()}"
.options="${this.generatedisplayDataChartOptions(
'Number of features supported',
)}"
>
Loading chart...
</webstatus-gchart>
Expand All @@ -350,6 +413,32 @@ export class StatsPage extends LitElement {
});
}

renderMissingOneImplementationChartWhenComplete(): TemplateResult {
return html`
<webstatus-gchart
id="missing-one-implementation-chart"
?hasMax="${false}"
.containerId="${'missing-one-implementation-chart-container'}"
.chartType="${'LineChart'}"
.dataObj="${this.missingOneImplementationChartDataObj}"
.options="${this.generatedisplayDataChartOptions(
'Number of features missing',
)}"
>
Loading chart...
</webstatus-gchart>
`;
}

renderMissingOneImplementationChart(): TemplateResult | undefined {
return this._loadingGFSTask.render({
complete: () => this.renderMissingOneImplementationChartWhenComplete(),
error: () => this.renderChartWhenError(),
initial: () => this.renderChartWhenInitial(),
pending: () => this.renderChartWhenPending(),
});
}

renderGlobalFeatureSupport(): TemplateResult {
return html`
<sl-card id="global-feature-support">
Expand All @@ -364,7 +453,7 @@ export class StatsPage extends LitElement {
id="global-feature-support-browser-selector"
multiple
stay-open-on-select
.value="${this.globalFeatureSupportBrowsers.join(' ')}"
.value="${this.supportedBrowsers.join(' ')}"
>
<sl-button slot="trigger">
<sl-icon slot="suffix" name="chevron-down"></sl-icon>
Expand Down Expand Up @@ -402,9 +491,7 @@ export class StatsPage extends LitElement {
<sl-option>Safari</sl-option>
</sl-dropdown>
</div>
<div class="under-construction" id="features-lagging-chart">
Chart goes here...
</div>
<div>${this.renderMissingOneImplementationChart()}</div>
</sl-card>
`;
}
Expand Down

0 comments on commit 0445599

Please sign in to comment.