feat: consolidate shared UI and harden browser authority for release

This commit is contained in:
2026-09-08 01:35:05 +02:00
parent ac40774785
commit b75ca34295
143 changed files with 8664 additions and 752 deletions
+27 -1
View File
@@ -16,6 +16,7 @@ import FloatingStatus from "../src/components/FloatingStatus";
import FormSection from "../src/components/FormSection";
import { PlatformLanguageProvider } from "../src/i18n/LanguageContext";
import MetricGrid from "../src/components/MetricGrid";
import LoadingFrame from "../src/components/LoadingFrame";
import PageActionBar from "../src/components/PageActionBar";
import SelectionList, { SelectionListItem, SelectionListItemContent } from "../src/components/SelectionList";
import StatePanel from "../src/components/StatePanel";
@@ -23,6 +24,29 @@ import ProductAvailabilityState from "../src/components/ProductAvailabilityState
import WorkspaceLayout from "../src/components/WorkspaceLayout";
import WorkspaceFrame from "../src/components/WorkspaceFrame";
import WorkspaceActionBar from "../src/components/WorkspaceActionBar";
import Card from "../src/components/Card";
import FormField from "../src/components/FormField";
import ListSelectionFilter from "../src/components/ListSelectionFilter";
const tableCard = renderToStaticMarkup(<Card title="Directory" bodyLayout="table"><LoadingFrame loading={false}><div className="data-grid-shell">Rows</div></LoadingFrame></Card>);
assert(tableCard.includes('data-card-body-layout="table"'), "table cards explicitly declare zero-inset geometry even with a loading wrapper");
const wideField = renderToStaticMarkup(<FormField label="Wide field" className="form-field-wide"><input /></FormField>);
assert(wideField.includes('class="form-field form-field-wide"'), "form fields retain layout classes on their labelled root");
const filterOptions = [{ value: "a", label: "A" }, { value: "b", label: "B" }];
for (const [selection, expectedCount] of [[null, 2], [[], 0], [["a"], 1]] as const) {
const filter = renderToStaticMarkup(<ListSelectionFilter options={filterOptions} value={selection === null ? null : [...selection]} onChange={() => undefined} />);
assert((filter.match(/checked=""/g) ?? []).length === expectedCount, "list filters distinguish unrestricted, none and explicit subset");
}
const progressFrame = renderToStaticMarkup(<LoadingFrame loading indicator="none" label="Extracting" progress={25} progressLabel="1 of 4 files"><p>Preview</p></LoadingFrame>);
assert(progressFrame.includes('aria-busy="true"'), "the shared loading frame marks its content busy");
assert(progressFrame.includes('value="25"'), "measured progress is reported through a native progress bar");
assert(progressFrame.includes('aria-valuetext="1 of 4 files"'), "progress can describe real processed counts");
assert(!progressFrame.includes("loading-envelope"), "operation overlays can omit the envelope animation");
const unknownFrame = renderToStaticMarkup(<LoadingFrame loading indicator="none" label="Inspecting" progress={null}><p>Preview</p></LoadingFrame>);
assert(unknownFrame.includes("<progress") && !unknownFrame.includes('value="'), "unknown progress is indeterminate, never fabricated");
const legacyFrame = renderToStaticMarkup(<LoadingFrame loading><p>Existing consumer</p></LoadingFrame>);
assert(legacyFrame.includes("loading-envelope") && !legacyFrame.includes("<progress"), "existing loading-frame consumers retain their presentation");
// @ts-expect-error Refreshable pages must provide a Reload action.
const refreshableWithoutReload = <PageActionBar variant="detail" refreshable />;
@@ -71,7 +95,8 @@ assert(editorActionBarMarkup.includes('aria-label="Editor actions"'), "page acti
assert(editorActionBarMarkup.includes('data-page-refreshable="true"'), "refreshable pages expose their refresh contract");
assert(editorActionBarMarkup.includes('data-page-dirty="true"'), "editors expose their dirty state");
assert(editorActionBarMarkup.includes('data-page-dirty-state="dirty"'), "dirty editors announce unsaved changes");
assert(editorActionBarMarkup.indexOf('data-page-action-slot="reload"') < editorActionBarMarkup.indexOf('data-page-action-slot="context"'), "reload precedes contextual actions");
assert(editorActionBarMarkup.indexOf('data-page-action-slot="context"') < editorActionBarMarkup.indexOf('data-page-action-slot="reload"'), "context stays leading while reload belongs to trailing actions");
assert(editorActionBarMarkup.indexOf('data-page-action-group="trailing"') < editorActionBarMarkup.indexOf('data-page-action-slot="reload"'), "reload shares the right-aligned primary action group");
assert(editorActionBarMarkup.indexOf('data-page-action-slot="help"') < editorActionBarMarkup.indexOf('data-page-action-slot="discard"'), "help precedes editor persistence actions");
assert(editorActionBarMarkup.indexOf('data-page-action-slot="destructive"') < editorActionBarMarkup.indexOf('data-page-action-slot="discard"'), "destructive editor actions are separated from persistence actions");
assert(editorActionBarMarkup.includes('data-page-action-separation="destructive"'), "destructive actions expose their visual boundary");
@@ -103,6 +128,7 @@ const collectionActionBarMarkup = renderToStaticMarkup(
</PlatformLanguageProvider>
);
assert(collectionActionBarMarkup.indexOf('data-page-action-slot="reload"') < collectionActionBarMarkup.indexOf('data-page-action-slot="create"'), "collection creation remains the far-right action");
assert(!collectionActionBarMarkup.includes('data-page-action-group="leading"'), "empty context groups do not create phantom rows in narrow toolbars");
const workspaceEditorActionBarMarkup = renderToStaticMarkup(
<PlatformLanguageProvider>