Files
onenote-tools/src/components/DiagnosticsPanel.tsx

170 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from 'react';
import type { ParserDiagnostic } from '../onenote/model/diagnostics.js';
import {
DEFAULT_UI_RENDER_LIMITS,
limitUiString,
resolveUiLimit,
} from './ui-render-budget.js';
interface DiagnosticsPanelProps {
diagnostics: ParserDiagnostic[];
title?: string;
pageSize?: number;
}
function formatOffset(offset: number): string {
return `0x${offset.toString(16).toUpperCase()}`;
}
export function DiagnosticsPanel({
diagnostics,
title = 'Diagnostics',
pageSize: requestedPageSize,
}: DiagnosticsPanelProps) {
const [requestedPage, setRequestedPage] = useState(0);
const pageSize = resolveUiLimit(
requestedPageSize,
DEFAULT_UI_RENDER_LIMITS.maxDiagnosticsPerPage
);
const pageCount = Math.max(1, Math.ceil(diagnostics.length / pageSize));
const page = Math.min(requestedPage, pageCount - 1);
const firstDiagnostic = page * pageSize;
const limitedTitle = limitUiString(
title,
DEFAULT_UI_RENDER_LIMITS.maxPanelTitleCharacters
);
const displayedDiagnostics = diagnostics
.slice(firstDiagnostic, firstDiagnostic + pageSize)
.map((diagnostic, index) => {
const code = limitUiString(
diagnostic.code,
DEFAULT_UI_RENDER_LIMITS.maxDiagnosticCodeCharacters
);
const message = limitUiString(
diagnostic.message,
DEFAULT_UI_RENDER_LIMITS.maxDiagnosticMessageCharacters
);
const structure =
diagnostic.structure === undefined
? undefined
: limitUiString(
diagnostic.structure,
DEFAULT_UI_RENDER_LIMITS.maxDiagnosticContextCharacters
);
const propertyId =
diagnostic.propertyId === undefined
? undefined
: limitUiString(
diagnostic.propertyId,
DEFAULT_UI_RENDER_LIMITS.maxDiagnosticContextCharacters
);
return {
diagnostic,
index: firstDiagnostic + index,
code: code.value,
message: message.value,
structure: structure?.value,
propertyId: propertyId?.value,
truncated:
code.truncated ||
message.truncated ||
structure?.truncated === true ||
propertyId?.truncated === true,
};
});
const textTruncated =
limitedTitle.truncated ||
displayedDiagnostics.some((diagnostic) => diagnostic.truncated);
return (
<section className="diagnostics" aria-labelledby="diagnostics-title">
<div className="panel-heading">
<div>
<p className="eyebrow">Parser details</p>
<h2 id="diagnostics-title">{limitedTitle.value}</h2>
</div>
<span className="count-badge">{diagnostics.length}</span>
</div>
{diagnostics.length === 0 ? (
<p className="empty-message">No parser diagnostics for this view.</p>
) : (
<ul className="diagnostic-list">
{displayedDiagnostics.map((displayed) => (
<li
className={`diagnostic diagnostic--${displayed.diagnostic.severity}`}
key={displayed.index}
>
<div className="diagnostic__summary">
<strong>{displayed.code}</strong>
<span>{displayed.diagnostic.severity}</span>
</div>
<p>{displayed.message}</p>
{displayed.structure !== undefined ||
displayed.diagnostic.offset !== undefined ||
displayed.propertyId !== undefined ? (
<dl className="diagnostic__context">
{displayed.structure !== undefined ? (
<>
<dt>Structure</dt>
<dd>{displayed.structure}</dd>
</>
) : null}
{displayed.diagnostic.offset !== undefined ? (
<>
<dt>Offset</dt>
<dd>{formatOffset(displayed.diagnostic.offset)}</dd>
</>
) : null}
{displayed.propertyId !== undefined ? (
<>
<dt>Property</dt>
<dd>{displayed.propertyId}</dd>
</>
) : null}
</dl>
) : null}
</li>
))}
</ul>
)}
{diagnostics.length > 0 ? (
<nav className="panel-heading" aria-label="Diagnostic pages">
<p>
Showing diagnostics {firstDiagnostic + 1}
{firstDiagnostic + displayedDiagnostics.length} of{' '}
{diagnostics.length}.
</p>
<div>
<button
type="button"
disabled={page === 0}
onClick={() => setRequestedPage(Math.max(0, page - 1))}
>
Previous
</button>{' '}
<span>
Page {page + 1} of {pageCount}
</span>{' '}
<button
type="button"
disabled={page + 1 >= pageCount}
onClick={() =>
setRequestedPage(Math.min(pageCount - 1, page + 1))
}
>
Next
</button>
</div>
</nav>
) : null}
{textTruncated ? (
<p className="render-limit-notice" role="status">
Long diagnostic text was shortened for safe browser display.
</p>
) : null}
</section>
);
}