@for (result of filteredResults(); track result) {
diff --git a/report-app/src/app/pages/report-viewer/report-viewer.scss b/report-app/src/app/pages/report-viewer/report-viewer.scss
index 44b05fb..fa6eb4e 100644
--- a/report-app/src/app/pages/report-viewer/report-viewer.scss
+++ b/report-app/src/app/pages/report-viewer/report-viewer.scss
@@ -147,51 +147,8 @@ lighthouse-category + lighthouse-category {
margin-bottom: 2em;
}
-.filter-dropdown {
- position: relative;
- display: inline-block;
-
- margin-bottom: 1.5rem;
- gap: 0.5rem;
- background-color: var(--card-bg-color);
- padding: 0;
- border-radius: var(--control-border-radius);
- border: 1px solid var(--border-color);
- transition: background-color var(--transition-speed) ease;
-
- &:hover {
- background-color: var(--button-active-bg-color);
- }
-
- & summary {
- cursor: pointer;
- padding: 0.5rem 1.25rem;
- border-radius: 4px;
- background-color: transparent;
- list-style: none;
- font-weight: 500;
- color: var(--text-secondary);
- transition: color var(--transition-speed) ease;
- }
-
- &[open] .dropdown-content {
- display: block;
- color: var(--text-primary);
- }
-}
-
-.dropdown-content {
- display: none;
- position: absolute;
- background-color: var(--card-bg-color);
- min-width: 380px;
- box-shadow: var(--shadow);
- z-index: 1;
- border: 1px solid var(--border-color);
- border-radius: var(--border-radius);
- padding: 10px;
- max-height: 300px;
- overflow: auto;
+.check-filter {
+ margin-bottom: 1rem;
}
.no-failed-checks {
diff --git a/report-app/src/app/pages/report-viewer/report-viewer.ts b/report-app/src/app/pages/report-viewer/report-viewer.ts
index 3175df1..08e7109 100644
--- a/report-app/src/app/pages/report-viewer/report-viewer.ts
+++ b/report-app/src/app/pages/report-viewer/report-viewer.ts
@@ -32,7 +32,6 @@ import {
StackedBarChartData,
} from '../../shared/visualization/stacked-bar-chart/stacked-bar-chart';
import {formatFile} from './formatter';
-import {FailedChecksFilter} from './failed-checks-filter';
import {MessageSpinner} from '../../shared/message-spinner';
import {createPromptDebuggingZip} from '../../shared/debugging-zip';
import {Score} from '../../shared/score/score';
@@ -42,6 +41,7 @@ import {ExpansionPanelHeader} from '../../shared/expansion-panel/expansion-panel
import {ProviderLabel} from '../../shared/provider-label';
import {AiAssistant} from '../../shared/ai-assistant/ai-assistant';
import {LighthouseCategory} from './lighthouse-category';
+import {MultiSelect} from '../../shared/multi-select/multi-select';
const localReportRegex = /-l\d+$/;
@@ -51,7 +51,6 @@ const localReportRegex = /-l\d+$/;
CodeViewer,
DatePipe,
DecimalPipe,
- FailedChecksFilter,
MessageSpinner,
Score,
ExpansionPanel,
@@ -60,12 +59,10 @@ const localReportRegex = /-l\d+$/;
NgxJsonViewerModule,
AiAssistant,
LighthouseCategory,
+ MultiSelect,
],
templateUrl: './report-viewer.html',
styleUrls: ['./report-viewer.scss'],
- host: {
- '(document:click)': 'closeDropdownIfOpen($event)',
- },
})
export class ReportViewer {
private clipboard = inject(Clipboard);
@@ -107,7 +104,7 @@ export class ReportViewer {
return this.reportsFetcher.reportGroups().find(group => group.id === id);
});
- protected selectedChecks = signal
>(new Set());
+ protected selectedChecks = signal([]);
protected allFailedChecks = computed(() => {
if (!this.selectedReport.hasValue()) {
@@ -136,11 +133,11 @@ export class ReportViewer {
}
const failedChecksArray = Array.from(failedChecksMap.entries()).map(([name, count]) => ({
- name,
- count,
+ label: `${name} (${count})`,
+ value: name,
}));
- return failedChecksArray.sort((a, b) => a.name.localeCompare(b.name));
+ return failedChecksArray.sort((a, b) => a.label.localeCompare(b.label));
});
protected filteredResults = computed(() => {
@@ -151,7 +148,7 @@ export class ReportViewer {
return [];
}
- if (checks.size === 0) {
+ if (checks.length === 0) {
return report.results;
}
@@ -164,7 +161,7 @@ export class ReportViewer {
if (this.isSkippedAssessment(assessment)) {
continue;
}
- if (assessment.successPercentage < 1 && checks.has(assessment.name)) {
+ if (assessment.successPercentage < 1 && checks.includes(assessment.name)) {
return true;
}
}
@@ -353,27 +350,6 @@ export class ReportViewer {
return value.state === IndividualAssessmentState.SKIPPED;
}
- protected dropdownRef = viewChild('dropdown');
-
- protected closeDropdownIfOpen(event: MouseEvent): void {
- const detailsElement = this.dropdownRef()?.nativeElement;
- if (detailsElement?.hasAttribute('open') && !detailsElement.contains(event.target)) {
- detailsElement.removeAttribute('open');
- }
- }
-
- protected toggleCheckFilter(check: string): void {
- this.selectedChecks.update(currentChecks => {
- const checks = new Set(currentChecks);
- if (checks.has(check)) {
- checks.delete(check);
- } else {
- checks.add(check);
- }
- return checks;
- });
- }
-
protected async format(file: LlmResponseFile): Promise {
const result = await formatFile(file, this.selectedReport.value()!.details.summary.framework);
if (typeof result === 'string') {
diff --git a/report-app/src/app/shared/multi-select/multi-select.html b/report-app/src/app/shared/multi-select/multi-select.html
new file mode 100644
index 0000000..b98f9d0
--- /dev/null
+++ b/report-app/src/app/shared/multi-select/multi-select.html
@@ -0,0 +1,19 @@
+
diff --git a/report-app/src/app/shared/multi-select/multi-select.scss b/report-app/src/app/shared/multi-select/multi-select.scss
new file mode 100644
index 0000000..e0deba0
--- /dev/null
+++ b/report-app/src/app/shared/multi-select/multi-select.scss
@@ -0,0 +1,61 @@
+:host {
+ display: inline-flex;
+}
+
+button {
+ border-radius: var(--control-border-radius);
+ border: 1px solid var(--border-color);
+ background-color: var(--card-bg-color);
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+ font-weight: 500;
+ padding: 0 36px 0 16px;
+ height: var(--control-height);
+ display: inline-block;
+ transition: background-color var(--transition-speed) ease;
+ position: relative;
+ cursor: pointer;
+ background-image: url('data:image/svg+xml;charset=US-ASCII,');
+ background-repeat: no-repeat;
+ background-position: right 1rem center;
+ background-size: 0.65rem auto;
+
+ &:not(.open):focus, &:not(.open):hover {
+ border-color: var(--accent-blue);
+ }
+}
+
+.dropdown {
+ display: none;
+ position: absolute;
+ top: 100%;
+ left: 0;
+ background-color: var(--card-bg-color);
+ min-width: 380px;
+ box-shadow: var(--shadow);
+ z-index: 1;
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: 10px;
+ max-height: 300px;
+ overflow: auto;
+ text-align: left;
+
+ .open & {
+ display: block;
+ color: var(--text-primary);
+ }
+}
+
+label {
+ display: flex;
+ padding: 8px 12px;
+ cursor: pointer;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+label:hover {
+ background-color: #eff6ff;
+ border-radius: var(--border-radius);
+}
diff --git a/report-app/src/app/shared/multi-select/multi-select.ts b/report-app/src/app/shared/multi-select/multi-select.ts
new file mode 100644
index 0000000..0e06efb
--- /dev/null
+++ b/report-app/src/app/shared/multi-select/multi-select.ts
@@ -0,0 +1,39 @@
+import {Component, ElementRef, inject, input, model, signal} from '@angular/core';
+
+interface MultiSelectOption {
+ label: string;
+ value: unknown;
+}
+
+@Component({
+ selector: 'multi-select',
+ templateUrl: 'multi-select.html',
+ styleUrl: 'multi-select.scss',
+ host: {
+ '(document:click)': 'outsideClick($event)',
+ },
+})
+export class MultiSelect {
+ private elementRef = inject>(ElementRef);
+
+ options = input.required();
+ label = input.required();
+ selected = model([]);
+
+ protected isOpen = signal(false);
+
+ protected optionClicked(option: MultiSelectOption) {
+ this.selected.update(selected => {
+ if (selected.includes(option.value)) {
+ return selected.filter(current => current !== option.value);
+ }
+ return [...selected, option.value];
+ });
+ }
+
+ protected outsideClick(event: MouseEvent) {
+ if (this.isOpen() && !this.elementRef.nativeElement.contains(event.target as HTMLElement)) {
+ this.isOpen.set(false);
+ }
+ }
+}
diff --git a/report-app/src/app/shared/scoring.ts b/report-app/src/app/shared/scoring.ts
index e87a73d..f609b5e 100644
--- a/report-app/src/app/shared/scoring.ts
+++ b/report-app/src/app/shared/scoring.ts
@@ -55,8 +55,6 @@ export function getHardcodedColor(
CACHED_COLORS[colorMode][varName] = value;
}
- console.log(CACHED_COLORS);
-
return CACHED_COLORS[colorMode][varName];
}