chrome-devtools-frontend
Version:
Chrome DevTools UI
218 lines (188 loc) • 8.66 kB
text/typescript
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Common from '../../core/common/common.js';
import * as Host from '../../core/host/host.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
import type * as IconButton from '../../ui/components/icon_button/icon_button.js';
import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
import * as UI from '../../ui/legacy/legacy.js';
import {html, nothing, render} from '../../ui/lit/lit.js';
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
const UIStrings = {
/**
* @description The console error count in the Warning Error Counter shown in the main toolbar (top-left in DevTools). The error count refers to the number of errors currently present in the JavaScript console.
*/
sErrors: '{n, plural, =1 {# error} other {# errors}}',
/**
* @description The console warning count in the Warning Error Counter shown in the main toolbar (top-left in DevTools). The warning count refers to the number of warnings currently present in the JavaScript console.
*/
sWarnings: '{n, plural, =1 {# warning} other {# warnings}}',
/**
* @description Tooltip shown for a main toolbar button that opens the Console panel
* @example {2 errors, 1 warning} PH1
*/
openConsoleToViewS: 'Open Console to view {PH1}',
/**
* @description Title for the issues count in the Issues Error Counter shown in the main toolbar (top-left in DevTools). The issues count refers to the number of issues in the issues tab.
*/
openIssuesToView: '{n, plural, =1 {Open Issues to view # issue:} other {Open Issues to view # issues:}}',
} as const;
const str_ = i18n.i18n.registerUIStrings('panels/console_counters/WarningErrorCounter.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
interface ViewInput {
compact?: boolean;
errors: number;
warnings: number;
issues: number;
showIssuesHandler: () => void;
}
type View = (input: ViewInput, output: object, target: HTMLElement) => void;
const DEFAULT_VIEW: View = (input, _output, target) => {
const issuesManager = IssuesManager.IssuesManager.IssuesManager.instance();
const countToText = (c: number): string|undefined => c === 0 ? undefined : `${c}`;
const {errors, warnings, issues, compact} = input;
/* Update consoleCounter items. */
const errorCountTitle = i18nString(UIStrings.sErrors, {n: errors});
const warningCountTitle = i18nString(UIStrings.sWarnings, {n: warnings});
let consoleSummary = '';
if (errors && warnings) {
consoleSummary = `${errorCountTitle}, ${warningCountTitle}`;
} else if (errors) {
consoleSummary = errorCountTitle;
} else if (warnings) {
consoleSummary = warningCountTitle;
}
const consoleTitle = i18nString(UIStrings.openConsoleToViewS, {PH1: consoleSummary});
const iconData: IconButton.IconButton.IconButtonData = {
clickHandler: Common.Console.Console.instance().show.bind(Common.Console.Console.instance()),
accessibleName: consoleTitle,
compact,
groups: [
{
iconName: 'cross-circle-filled',
text: countToText(errors)
},
{
iconName: 'warning-filled',
text: countToText(warnings)
},
],
};
/* Update issuesCounter items. */
const issueEnumeration = IssueCounter.IssueCounter.getIssueCountsEnumeration(issuesManager);
const issuesTitleLead = i18nString(UIStrings.openIssuesToView, {n: issues});
const issuesTitle = `${issuesTitleLead} ${issueEnumeration}`;
const issueCounterData: IssueCounter.IssueCounter.IssueCounterData = {
clickHandler: input.showIssuesHandler,
issuesManager,
compact,
accessibleName: issuesTitle,
displayMode: IssueCounter.IssueCounter.DisplayMode.ONLY_MOST_IMPORTANT,
};
render(
html`<div class="status-buttons"
><icon-button
.data=${iconData}
title=${consoleTitle}
class=${'small' + warnings || errors ? nothing as unknown as string : 'hidden'}
jslog=${VisualLogging.counter('console').track({
click: true
})}
></icon-button><devtools-issue-counter
class=${'main-toolbar' + (issues ? '' : ' hidden')}
title=${issuesTitle}
.data=${issueCounterData}
jslog=${VisualLogging.counter('issue').track({
click: true
})}
></devtools-issue-counter></div>`,
target);
};
export class WarningErrorCounterWidget extends UI.Widget.Widget {
private readonly throttler: Common.Throttler.Throttler;
updatingForTest?: boolean;
private compact?: boolean;
static instanceForTest: WarningErrorCounterWidget|null = null;
constructor(
element: HTMLElement, private readonly setVisibility: (visible: boolean) => void,
private readonly view: View = DEFAULT_VIEW) {
super(element);
this.throttler = new Common.Throttler.Throttler(100);
SDK.TargetManager.TargetManager.instance().addModelListener(
SDK.ConsoleModel.ConsoleModel, SDK.ConsoleModel.Events.ConsoleCleared, this.update, this);
SDK.TargetManager.TargetManager.instance().addModelListener(
SDK.ConsoleModel.ConsoleModel, SDK.ConsoleModel.Events.MessageAdded, this.update, this);
SDK.TargetManager.TargetManager.instance().addModelListener(
SDK.ConsoleModel.ConsoleModel, SDK.ConsoleModel.Events.MessageUpdated, this.update, this);
const issuesManager = IssuesManager.IssuesManager.IssuesManager.instance();
issuesManager.addEventListener(IssuesManager.IssuesManager.Events.ISSUES_COUNT_UPDATED, this.update, this);
this.update();
WarningErrorCounterWidget.instanceForTest = this;
}
onSetCompactLayout(event: Common.EventTarget.EventTargetEvent<boolean>): void {
this.setCompactLayout(event.data);
}
setCompactLayout(enable: boolean): void {
this.compact = enable;
void this.performUpdate();
}
private updatedForTest(): void {
// Sniffed in tests.
}
private update(): void {
this.updatingForTest = true;
void this.throttler.schedule(this.performUpdate.bind(this));
}
get titlesForTesting(): string|null {
const button = this.contentElement.querySelector('icon-button')?.shadowRoot?.querySelector('button');
return button ? button.getAttribute('aria-label') : null;
}
private showIssues(): void {
Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.STATUS_BAR_ISSUES_COUNTER);
void UI.ViewManager.ViewManager.instance().showView('issues-pane');
}
override async performUpdate(): Promise<void> {
const errors = SDK.ConsoleModel.ConsoleModel.allErrors();
const warnings = SDK.ConsoleModel.ConsoleModel.allWarnings();
const issuesManager = IssuesManager.IssuesManager.IssuesManager.instance();
const issues = issuesManager.numberOfIssues();
this.view(
{compact: this.compact, errors, warnings, issues, showIssuesHandler: this.showIssues.bind(this)}, {},
this.contentElement);
this.setVisibility(Boolean(errors || warnings || issues));
UI.InspectorView.InspectorView.instance().toolbarItemResized();
this.updatingForTest = false;
this.updatedForTest();
return;
}
}
let warningErrorCounterInstance: WarningErrorCounter;
export class WarningErrorCounter implements UI.Toolbar.Provider {
private readonly toolbarItem: UI.Toolbar.ToolbarItemWithCompactLayout;
private constructor() {
const widgetElement =
document.createElement('devtools-widget') as UI.Widget.WidgetElement<WarningErrorCounterWidget>;
const toolbarItem = new UI.Toolbar.ToolbarItemWithCompactLayout(widgetElement);
toolbarItem.setVisible(false);
widgetElement.widgetConfig = UI.Widget.widgetConfig(e => {
const widget = new WarningErrorCounterWidget(e, toolbarItem.setVisible.bind(toolbarItem));
toolbarItem.addEventListener(
UI.Toolbar.ToolbarItemWithCompactLayoutEvents.COMPACT_LAYOUT_UPDATED, widget.onSetCompactLayout, widget);
return widget;
});
this.toolbarItem = toolbarItem;
}
item(): UI.Toolbar.ToolbarItem|null {
return this.toolbarItem;
}
static instance(opts: {forceNew: boolean|null} = {forceNew: null}): WarningErrorCounter {
const {forceNew} = opts;
if (!warningErrorCounterInstance || forceNew) {
warningErrorCounterInstance = new WarningErrorCounter();
}
return warningErrorCounterInstance;
}
}