| name | react-ant-patterns |
|---|---|
| description | React 19 + Ant Design 5 + Tailwind 4 + Vite 7 patterns for the KMS Web UI: WASM integration, FIPS guards, data-testid placement, Playwright companion. Use as a reference for UI development patterns. |
Reference for KMS UI-specific patterns. Stack: React 19 + Vite 7 + Ant Design 5 + Tailwind CSS 4 + pnpm.
- Ant Design LLM context: Fetch
https://ant.design/llms.txtfor up-to-date component API reference when coding AntD components. - Ant Design MCP server: https://ant-design.antgroup.com/docs/react/mcp — provides structured component documentation to AI agents.
- Context7 MCP (
io.github.upstash/context7): Generic library documentation fetcher — use when AntD or other JS library APIs seem outdated in the agent's training data. See https://context7.com/.
ui/src/
actions/ ← 14 feature modules (one per KMIP operation group)
Access/ ← Grant, List, Obtained, Revoke
Attributes/ ← Delete, Get, Modify, Set
Certificates/ ← Certify, Decrypt, Encrypt, Export, Import, Validate
CloudProviders/ ← AWS/Azure key export/import
Covercrypt/ ← Non-FIPS only
EC/ ← Elliptic Curve
Keys/ ← Derive, Export, Import, Symmetric
MAC/ ← Compute, Verify
Objects/ ← Destroy, List, Revoke, Opaque, SecretData
PQC/ ← Non-FIPS only
RSA/ ← RSA key/enc/sign
Symmetric/ ← Encrypt, Decrypt, Hash
App.tsx ← React Router routes
menuItems.tsx ← Sidebar menu
wasm/pkg/ ← Generated WASM bindings (do not edit manually)
Hide non-FIPS features in the menu and routing when FIPS mode is active.
// Read the branding/FIPS flag from the server info endpoint
import { useServerInfo } from '../hooks/useServerInfo'
export function menuItems() {
const { fipsMode } = useServerInfo()
return [
{ key: 'symmetric', label: 'Symmetric', icon: <KeyOutlined /> },
{ key: 'ec', label: 'Elliptic Curve', icon: <KeyOutlined /> },
// Hide Covercrypt in FIPS mode
...(!fipsMode ? [{ key: 'covercrypt', label: 'Covercrypt', icon: <LockOutlined /> }] : []),
// Hide PQC in FIPS mode (non-standardized variants)
...(!fipsMode ? [{ key: 'pqc', label: 'Post-Quantum', icon: <SafetyOutlined /> }] : []),
]
}For routing in App.tsx:
{!fipsMode && <Route path="/covercrypt/*" element={<Covercrypt />} />}The KMS UI uses WASM for building KMIP request objects (TTLV encoding). Always handle async WASM initialization.
import { useState, useEffect } from 'react'
// Import from the generated pkg (do not import from wasm.rs directly)
import initWasm, { create_symmetric_key_ttlv_request } from '../wasm/pkg/cosmian_kms_client_wasm'
export function CreateSymmetricKey() {
const [wasmReady, setWasmReady] = useState(false)
useEffect(() => {
initWasm().then(() => setWasmReady(true))
}, [])
const handleCreate = async () => {
if (!wasmReady) return
// Build the TTLV request via WASM
const ttlvRequest = create_symmetric_key_ttlv_request({
keyId: formValues.keyId,
algorithm: formValues.algorithm,
keyLength: Number(formValues.keyLength),
})
// Send to the KMS server
const response = await fetch('/kmip/2_1', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: ttlvRequest,
})
}
return (
<Button
data-testid="create-key-button"
disabled={!wasmReady}
onClick={handleCreate}
>
Create Key
</Button>
)
}Never hand-craft TTLV JSON in TypeScript — always use the WASM bindings. If a WASM function for your operation doesn't exist, add it to crate/clients/wasm/src/wasm.rs and rebuild with wasm-pack build --target web.
All interactive elements must have data-testid attributes for Playwright E2E tests.
import { Form, Input, Select, Button } from 'antd'
export function KeyCreationForm() {
const [form] = Form.useForm()
return (
<Form form={form} layout="vertical" data-testid="key-creation-form">
<Form.Item name="keyId" label="Key ID" rules={[{ required: true }]}>
<Input
data-testid="key-id-input"
placeholder="e.g. my-encryption-key"
/>
</Form.Item>
<Form.Item name="algorithm" label="Algorithm">
{/* Wrap Select in a div with data-testid for Playwright */}
<div data-testid="algorithm-select-wrapper">
<Select data-testid="algorithm-select" placeholder="Select algorithm">
<Select.Option value="AES">AES</Select.Option>
<Select.Option value="ChaCha20" disabled={fipsMode}>
ChaCha20 (non-FIPS)
</Select.Option>
</Select>
</div>
</Form.Item>
<Button
type="primary"
htmlType="submit"
data-testid="create-key-submit-button"
>
Create Key
</Button>
</Form>
)
}Note: Ant Design <Select> renders its dropdown in a portal attached to document.body. Playwright tests must use the helpers in ui/tests/e2e/helpers.ts to interact with it — do not try to select options directly on the <Select> element.
import { message, Alert } from 'antd'
// For transient feedback (toast)
const [messageApi, contextHolder] = message.useMessage()
const handleSubmit = async () => {
try {
const result = await callKmsOperation(/* ... */)
messageApi.success('Key created successfully')
// Store the UID for display
setCreatedKeyUid(result.uniqueIdentifier)
} catch (error) {
// Show user-friendly error — never expose internal server details
const userMessage = parseKmsError(error) ?? 'Operation failed. Check server logs.'
messageApi.error(userMessage)
}
}
// For persistent error display
{error && (
<Alert
data-testid="operation-error-alert"
type="error"
message={error}
showIcon
/>
)}
// For success result display
{createdKeyUid && (
<Alert
data-testid="created-key-uid"
type="success"
message={`Key created: ${createdKeyUid}`}
showIcon
/>
)}When adding a new feature module under ui/src/actions/<Module>/:
ui/src/actions/MyFeature/
index.tsx ← Main component (default export)
MyOperation1.tsx ← Sub-operation component
MyOperation2.tsx ← Sub-operation component
types.ts ← TypeScript interfaces for this feature
In App.tsx:
import MyFeature from './actions/MyFeature'
// Inside <Routes>:
<Route path="/myfeature/*" element={<MyFeature />} />In menuItems.tsx:
{
key: 'myfeature', // must match the route path prefix
label: 'My Feature',
icon: <MyIcon />,
// Conditionally hide if non-FIPS:
// hidden: fipsMode,
}In start_kms_server.rs (if new top-level path — required for browser refresh to work):
spa_routes.push("/myfeature{_:.*}");tsconfig.app.json enforces strict: true, noUnusedLocals: true, noUnusedParameters: true.
// ❌ Will fail: unused parameter
const MyComponent = ({ unusedProp }: { unusedProp: string }) => <div />
// ✅ Prefix unused params with _
const MyComponent = ({ _unusedProp }: { _unusedProp: string }) => <div />
// ❌ Will fail: implicit any
const handleData = (data) => data.value
// ✅ Explicit type
const handleData = (data: KmsResponse) => data.valueUse Tailwind utility classes for layout and spacing. Do not override Ant Design component styles with Tailwind unless necessary.
// ✅ Tailwind for layout, Ant Design for components
<div className="flex flex-col gap-4 p-6">
<Card title="Create Key" className="max-w-2xl">
<Form layout="vertical">
{/* Ant Design form components */}
</Form>
</Card>
</div>
// ❌ Avoid fighting Ant Design's styling
<Button className="!bg-red-500 !text-white"> {/* fragile */}- Every interactive element gets a
data-testid - Ant Design Select dropdowns need helpers in Playwright (portal-rendered)
- WASM must initialize before calling any WASM function — check
wasmReadystate - FIPS mode guard every non-FIPS feature in menu items AND routes
- TypeScript strict mode — no
any, no unused locals/parameters - Mirror the CLI — every
ckmssubcommand must have a corresponding UI action