| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import '../../../ui/legacy/components/data_grid/data_grid.js'; |
| |
| import * as i18n from '../../../core/i18n/i18n.js'; |
| import * as Root from '../../../core/root/root.js'; |
| import type * as Protocol from '../../../generated/protocol.js'; |
| import * as UI from '../../../ui/legacy/legacy.js'; |
| import * as Lit from '../../../ui/lit/lit.js'; |
| import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js'; |
| |
| import reportsGridStyles from './reportsGrid.css.js'; |
| |
| const UIStrings = { |
| /** |
| * @description Placeholder text when there are no Reporting API reports. |
| *(https://developers.google.com/web/updates/2018/09/reportingapi#sending) |
| */ |
| noReportsToDisplay: 'No reports to display', |
| /** |
| * @description Placeholder text that explains Reporting API reports. |
| *(https://developers.google.com/web/updates/2018/09/reportingapi#sending) |
| */ |
| reportingApiDescription: 'Here you will find reporting api reports that are generated by the page.', |
| /** |
| * @description Link text to forward to a documentation page on reporting API. |
| */ |
| learnMore: 'Learn more', |
| /** |
| * @description Column header for a table displaying Reporting API reports. |
| *Status is one of 'Queued', 'Pending', 'MarkedForRemoval' or 'Success'. |
| */ |
| status: 'Status', |
| /** |
| * @description Column header for a table displaying Reporting API reports. |
| *Destination is the name of the endpoint the report is being sent to. |
| */ |
| destination: 'Destination', |
| /** |
| * @description Column header for a table displaying Reporting API reports. |
| *The column contains the timestamp of when a report was generated. |
| */ |
| generatedAt: 'Generated at', |
| } as const; |
| const str_ = i18n.i18n.registerUIStrings('panels/application/components/ReportsGrid.ts', UIStrings); |
| export const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); |
| |
| const {render, html} = Lit; |
| |
| const REPORTING_API_EXPLANATION_URL = 'https://developer.chrome.com/docs/capabilities/web-apis/reporting-api'; |
| |
| export interface ReportsGridData { |
| reports: Protocol.Network.ReportingApiReport[]; |
| } |
| |
| export interface ViewInput { |
| reports: Protocol.Network.ReportingApiReport[]; |
| protocolMonitorExperimentEnabled: boolean; |
| onSelect: (id: string) => void; |
| } |
| |
| export const DEFAULT_VIEW = (input: ViewInput, output: undefined, target: HTMLElement): void => { |
| // clang-format off |
| render(html` |
| <style>${reportsGridStyles}</style> |
| <style>${UI.inspectorCommonStyles}</style> |
| <div class="reporting-container" jslog=${VisualLogging.section('reports')}> |
| <div class="reporting-header">${i18n.i18n.lockedString('Reports')}</div> |
| ${input.reports.length > 0 ? html` |
| <devtools-data-grid striped> |
| <table> |
| <tr> |
| ${input.protocolMonitorExperimentEnabled ? html` |
| <th id="id" weight="30">${i18n.i18n.lockedString('ID')}</th> |
| ` : ''} |
| <th id="url" weight="30">${i18n.i18n.lockedString('URL')}</th> |
| <th id="type" weight="20">${i18n.i18n.lockedString('Type')}</th> |
| <th id="status" weight="20"> |
| <style>${reportsGridStyles}</style> |
| <span class="status-header">${i18nString(UIStrings.status)}</span> |
| <x-link href="https://web.dev/reporting-api/#report-status" |
| jslog=${VisualLogging.link('report-status').track({click: true})}> |
| <devtools-icon class="inline-icon medium" name="help" style="color: var(--icon-link);" |
| ></devtools-icon> |
| </x-link> |
| </th> |
| <th id="destination" weight="20">${i18nString(UIStrings.destination)}</th> |
| <th id="timestamp" weight="20">${i18nString(UIStrings.generatedAt)}</th> |
| <th id="body" weight="20">${i18n.i18n.lockedString('Body')}</th> |
| </tr> |
| ${input.reports.map(report => html` |
| <tr @select=${() =>input.onSelect(report.id)}> |
| ${input.protocolMonitorExperimentEnabled ? html`<td>${report.id}</td>` : ''} |
| <td>${report.initiatorUrl}</td> |
| <td>${report.type}</td> |
| <td>${report.status}</td> |
| <td>${report.destination}</td> |
| <td>${new Date(report.timestamp * 1000).toLocaleString()}</td> |
| <td>${JSON.stringify(report.body)}</td> |
| </tr> |
| `)} |
| </table> |
| </devtools-data-grid> |
| ` : html` |
| <div class="empty-state"> |
| <span class="empty-state-header">${i18nString(UIStrings.noReportsToDisplay)}</span> |
| <div class="empty-state-description"> |
| <span>${i18nString(UIStrings.reportingApiDescription)}</span> |
| <x-link |
| class="devtools-link" |
| href=${REPORTING_API_EXPLANATION_URL} |
| jslog=${VisualLogging.link() |
| .track({ click: true, keydown: 'Enter|Space' }) |
| .context('learn-more')} |
| >${i18nString(UIStrings.learnMore)}</x-link> |
| </div> |
| </div> |
| `} |
| </div> |
| `, target); |
| // clang-format on |
| }; |
| |
| type View = typeof DEFAULT_VIEW; |
| |
| export class ReportsGrid extends UI.Widget.Widget { |
| reports: Protocol.Network.ReportingApiReport[] = []; |
| #protocolMonitorExperimentEnabled = false; |
| #view: View; |
| onReportSelected: (id: string) => void = () => {}; |
| |
| constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) { |
| super(element); |
| this.#view = view; |
| this.#protocolMonitorExperimentEnabled = Root.Runtime.experiments.isEnabled('protocol-monitor'); |
| this.requestUpdate(); |
| } |
| |
| override performUpdate(): void { |
| const viewInput = { |
| reports: this.reports, |
| protocolMonitorExperimentEnabled: this.#protocolMonitorExperimentEnabled, |
| onSelect: this.onReportSelected, |
| }; |
| this.#view(viewInput, undefined, this.contentElement); |
| } |
| } |