Define semantic page archetypes
This commit is contained in:
@@ -22,6 +22,13 @@ import StatePanel from "../src/components/StatePanel";
|
||||
import WorkspaceLayout from "../src/components/WorkspaceLayout";
|
||||
import WorkspaceFrame from "../src/components/WorkspaceFrame";
|
||||
|
||||
// @ts-expect-error Refreshable pages must provide a Reload action.
|
||||
const refreshableWithoutReload = <PageActionBar variant="detail" refreshable />;
|
||||
// @ts-expect-error Editor pages must declare dirty state and persistence actions.
|
||||
const editorWithoutPersistence = <PageActionBar variant="editor" />;
|
||||
void refreshableWithoutReload;
|
||||
void editorWithoutPersistence;
|
||||
|
||||
const toolbarMarkup = renderToStaticMarkup(
|
||||
<PlatformLanguageProvider>
|
||||
<ActionToolbar label="Object actions" density="compact" justify="between" surface="subtle" interfaceId="object.toolbar">
|
||||
@@ -44,26 +51,49 @@ const editorActionBarMarkup = renderToStaticMarkup(
|
||||
<PlatformLanguageProvider>
|
||||
<PageActionBar
|
||||
variant="editor"
|
||||
refreshable
|
||||
dirty
|
||||
label="Editor actions"
|
||||
reloadAction={<button type="button">Reload</button>}
|
||||
contextActions={<button type="button">Preview</button>}
|
||||
helpAction={<button type="button">Help</button>}
|
||||
discardAction={<button type="button">Discard</button>}
|
||||
saveAction={<button type="button">Save</button>}
|
||||
destructiveActions={<button type="button">Delete</button>}
|
||||
discardAction={{ label: "Discard" }}
|
||||
saveAction={{ label: "Save" }}
|
||||
/>
|
||||
</PlatformLanguageProvider>
|
||||
);
|
||||
|
||||
assert(editorActionBarMarkup.includes('data-page-action-archetype="editor"'), "page action bars expose their semantic archetype");
|
||||
assert(editorActionBarMarkup.includes('aria-label="Editor actions"'), "page action bars are named toolbars");
|
||||
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="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");
|
||||
assert(editorActionBarMarkup.indexOf('data-page-action-slot="discard"') < editorActionBarMarkup.indexOf('data-page-action-slot="save"'), "save remains the far-right editor action");
|
||||
|
||||
const cleanEditorActionBarMarkup = renderToStaticMarkup(
|
||||
<PlatformLanguageProvider>
|
||||
<PageActionBar
|
||||
variant="editor"
|
||||
dirty={false}
|
||||
discardAction={{ label: "Discard" }}
|
||||
saveAction={{ label: "Save" }}
|
||||
/>
|
||||
</PlatformLanguageProvider>
|
||||
);
|
||||
assert(cleanEditorActionBarMarkup.includes('data-page-dirty-state="clean"'), "clean editors announce their persisted state");
|
||||
assert((cleanEditorActionBarMarkup.match(/disabled=""/g) ?? []).length === 2, "clean editors keep Save and Discard visible but disabled");
|
||||
assert((cleanEditorActionBarMarkup.match(/disabled-action-tooltip/g) ?? []).length === 2, "clean editor actions expose explanations for unavailable persistence");
|
||||
|
||||
const collectionActionBarMarkup = renderToStaticMarkup(
|
||||
<PlatformLanguageProvider>
|
||||
<PageActionBar
|
||||
variant="collection"
|
||||
refreshable
|
||||
reloadAction={<button type="button">Reload</button>}
|
||||
createAction={<button type="button">Create</button>}
|
||||
/>
|
||||
|
||||
@@ -3,15 +3,17 @@ function assert(condition: unknown, message = "assertion failed"): void {
|
||||
}
|
||||
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import PageActionBar from "../src/components/PageActionBar";
|
||||
import PageLayout from "../src/components/PageLayout";
|
||||
import { PlatformLanguageProvider } from "../src/i18n/LanguageContext";
|
||||
|
||||
const standaloneMarkup = renderToStaticMarkup(
|
||||
<PlatformLanguageProvider>
|
||||
<PageLayout
|
||||
archetype="collection"
|
||||
title="Shared page"
|
||||
description="One page frame"
|
||||
actions={<button type="button">Reload</button>}
|
||||
actions={<PageActionBar variant="collection" refreshable reloadAction={<button type="button">Reload</button>} />}
|
||||
error="Could not load"
|
||||
success="Saved"
|
||||
interfaceId="test.page"
|
||||
@@ -30,13 +32,14 @@ assert(standaloneMarkup.includes("page-layout-actions"), "route actions use the
|
||||
assert(standaloneMarkup.includes('data-interface-id="test.page"'), "page identity reaches the shared frame");
|
||||
assert(standaloneMarkup.includes('data-help-context-id="test.page.help"'), "context help reaches the shared frame");
|
||||
assert(standaloneMarkup.includes('data-help-module-id="test-module"'), "documentation ownership reaches the shared frame");
|
||||
assert(standaloneMarkup.includes('data-page-archetype="collection"'), "page intent is independent from layout geometry");
|
||||
assert(standaloneMarkup.includes("Could not load"), "page errors use the shared alert region");
|
||||
assert(standaloneMarkup.includes("Saved"), "page success notices use the shared alert region");
|
||||
assert(standaloneMarkup.includes("Page content"), "page content is preserved");
|
||||
|
||||
const embeddedMarkup = renderToStaticMarkup(
|
||||
<PlatformLanguageProvider>
|
||||
<PageLayout title="Embedded page" mode="embedded" documentationType="admin">
|
||||
<PageLayout archetype="detail" title="Embedded page" mode="embedded" documentationType="admin">
|
||||
Embedded content
|
||||
</PageLayout>
|
||||
</PlatformLanguageProvider>
|
||||
@@ -50,6 +53,7 @@ assert(embeddedMarkup.includes('data-help-documentation-type="admin"'), "embedde
|
||||
const workspaceMarkup = renderToStaticMarkup(
|
||||
<PlatformLanguageProvider>
|
||||
<PageLayout
|
||||
archetype="workspace"
|
||||
title="Workspace page"
|
||||
description={<p className="version-line">Version 3</p>}
|
||||
mode="workspace"
|
||||
@@ -68,7 +72,7 @@ assert(workspaceMarkup.includes("page-layout-notices"), "additional page notices
|
||||
|
||||
const headerLoadingMarkup = renderToStaticMarkup(
|
||||
<PlatformLanguageProvider>
|
||||
<PageLayout title="Refreshing" mode="workspace" headerLoading>
|
||||
<PageLayout archetype="detail" title="Refreshing" mode="workspace" headerLoading>
|
||||
Stable content
|
||||
</PageLayout>
|
||||
</PlatformLanguageProvider>
|
||||
@@ -79,7 +83,7 @@ assert(!headerLoadingMarkup.includes('aria-busy="true"'), "partial refresh does
|
||||
|
||||
const delegatedHeaderMarkup = renderToStaticMarkup(
|
||||
<PlatformLanguageProvider>
|
||||
<PageLayout title="Administration" mode="workspace" showHeader={false}>
|
||||
<PageLayout archetype="workspace" title="Administration" mode="workspace" showHeader={false}>
|
||||
<section><h1>Contributed administration panel</h1></section>
|
||||
</PageLayout>
|
||||
</PlatformLanguageProvider>
|
||||
|
||||
Reference in New Issue
Block a user