fix: bound navigation and inspector rendering
This commit is contained in:
@@ -1,8 +1,16 @@
|
||||
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 {
|
||||
@@ -12,13 +20,69 @@ function formatOffset(offset: number): string {
|
||||
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">{title}</h2>
|
||||
<h2 id="diagnostics-title">{limitedTitle.value}</h2>
|
||||
</div>
|
||||
<span className="count-badge">{diagnostics.length}</span>
|
||||
</div>
|
||||
@@ -27,36 +91,36 @@ export function DiagnosticsPanel({
|
||||
<p className="empty-message">No parser diagnostics for this view.</p>
|
||||
) : (
|
||||
<ul className="diagnostic-list">
|
||||
{diagnostics.map((diagnostic, index) => (
|
||||
{displayedDiagnostics.map((displayed) => (
|
||||
<li
|
||||
className={`diagnostic diagnostic--${diagnostic.severity}`}
|
||||
key={`${diagnostic.code}-${diagnostic.offset ?? 'none'}-${index}`}
|
||||
className={`diagnostic diagnostic--${displayed.diagnostic.severity}`}
|
||||
key={displayed.index}
|
||||
>
|
||||
<div className="diagnostic__summary">
|
||||
<strong>{diagnostic.code}</strong>
|
||||
<span>{diagnostic.severity}</span>
|
||||
<strong>{displayed.code}</strong>
|
||||
<span>{displayed.diagnostic.severity}</span>
|
||||
</div>
|
||||
<p>{diagnostic.message}</p>
|
||||
{diagnostic.structure !== undefined ||
|
||||
diagnostic.offset !== undefined ||
|
||||
diagnostic.propertyId !== undefined ? (
|
||||
<p>{displayed.message}</p>
|
||||
{displayed.structure !== undefined ||
|
||||
displayed.diagnostic.offset !== undefined ||
|
||||
displayed.propertyId !== undefined ? (
|
||||
<dl className="diagnostic__context">
|
||||
{diagnostic.structure !== undefined ? (
|
||||
{displayed.structure !== undefined ? (
|
||||
<>
|
||||
<dt>Structure</dt>
|
||||
<dd>{diagnostic.structure}</dd>
|
||||
<dd>{displayed.structure}</dd>
|
||||
</>
|
||||
) : null}
|
||||
{diagnostic.offset !== undefined ? (
|
||||
{displayed.diagnostic.offset !== undefined ? (
|
||||
<>
|
||||
<dt>Offset</dt>
|
||||
<dd>{formatOffset(diagnostic.offset)}</dd>
|
||||
<dd>{formatOffset(displayed.diagnostic.offset)}</dd>
|
||||
</>
|
||||
) : null}
|
||||
{diagnostic.propertyId !== undefined ? (
|
||||
{displayed.propertyId !== undefined ? (
|
||||
<>
|
||||
<dt>Property</dt>
|
||||
<dd>{diagnostic.propertyId}</dd>
|
||||
<dd>{displayed.propertyId}</dd>
|
||||
</>
|
||||
) : null}
|
||||
</dl>
|
||||
@@ -65,6 +129,41 @@ export function DiagnosticsPanel({
|
||||
))}
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user