docs: address spec review feedback for admin pages design

- Move MultiSelect to composites (depends on portal, not a primitive)
- MultiSelect manages own positioning instead of wrapping Popover
- Add loading prop and info variant to ConfirmDialog
- Drop forwardRef from InlineEdit (input conditionally exists)
- Change InlineEdit blur to cancel (not save)
- Add router integration, barrel export, and accessibility details
- Add sidebar integration strategy (admin sub-nav, not sidebar clutter)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-18 22:46:45 +01:00
parent d41961dbe2
commit df5450925e

View File

@@ -9,9 +9,9 @@ Transfer the admin section from cameleer3-server UI to the design system project
## New Design System Components
### 1. MultiSelect (primitive)
### 1. MultiSelect (composite)
Dropdown trigger that opens a popover with searchable checkbox list and "Apply" action.
Dropdown trigger that opens a positioned panel with searchable checkbox list and "Apply" action.
**Props:**
```typescript
@@ -32,18 +32,25 @@ interface MultiSelectProps {
```
**Behavior:**
- Click trigger → popover opens below with search input + checkbox list + "Apply (N)" footer
- Click trigger → panel opens below with search input + checkbox list + "Apply (N)" footer
- Search filters options by label (case-insensitive)
- Checkboxes toggle selection; changes are local until "Apply" is clicked
- Apply calls `onChange` with selected values and closes popover
- Click outside or Escape closes without applying
- Apply calls `onChange` with selected values and closes panel
- Click outside or Escape closes without applying (discards pending changes)
- Trigger shows count: "2 selected" or placeholder when empty
- Arrow keys navigate checkbox list, Space toggles focused item, Tab moves between search/list/apply
- Panel has max-height with scroll for long option lists
**Accessibility:**
- Trigger: `role="combobox"`, `aria-expanded`, `aria-haspopup="listbox"`
- Option list: `role="listbox"`, options have `role="option"` with `aria-selected`
- Search input: `aria-label="Filter options"`
**Implementation:**
- New directory: `src/design-system/primitives/MultiSelect/`
- Uses Popover composite for positioning
- New directory: `src/design-system/composites/MultiSelect/`
- Manages its own open/close state and positioning (does NOT wrap Popover — needs controlled close behavior to distinguish apply vs. discard)
- Uses portal for the dropdown panel to avoid overflow clipping
- CSS Modules with design tokens
- No forwardRef needed (not a native form control)
### 2. ConfirmDialog (composite)
@@ -57,10 +64,11 @@ interface ConfirmDialogProps {
onConfirm: () => void
title?: string // default: "Confirm Deletion"
message: string // e.g., "Delete user 'alice'? This cannot be undone."
confirmText: string // text the user must type to enable confirm button
confirmText: string // text the user must type to enable confirm button (must be non-empty)
confirmLabel?: string // default: "Delete"
cancelLabel?: string // default: "Cancel"
variant?: 'danger' | 'warning' // default: 'danger'
variant?: 'danger' | 'warning' | 'info' // default: 'danger'
loading?: boolean // default: false — disables buttons, shows pending state
className?: string
}
```
@@ -71,7 +79,8 @@ interface ConfirmDialogProps {
- Confirm button disabled until input matches `confirmText` exactly
- Input clears on open
- Enter submits when enabled; Escape closes
- Confirm button uses danger/warning variant styling
- Confirm button uses danger/warning/info variant styling (matches AlertDialog pattern)
- When `loading` is true, both buttons are disabled
**Implementation:**
- New directory: `src/design-system/composites/ConfirmDialog/`
@@ -95,14 +104,14 @@ interface InlineEditProps {
**Behavior:**
- **Display mode:** Shows value as text with subtle edit icon (pencil). Clicking text or icon enters edit mode.
- **Edit mode:** Input field with current value. Enter or blur saves. Escape cancels (reverts to original value).
- **Edit mode:** Input field with current value. Enter saves. Escape cancels (reverts to original value). Blur cancels (same as Escape — prevents accidental saves when clicking away).
- If saved value is empty and placeholder exists, display mode shows placeholder in muted style.
- No save/cancel buttons — Enter saves, Escape cancels (lightweight inline pattern).
- No save/cancel buttons — Enter saves, Escape/blur cancels (lightweight inline pattern).
**Implementation:**
- New directory: `src/design-system/primitives/InlineEdit/`
- Uses `forwardRef` (wraps an input)
- Manages internal editing state
- No forwardRef — the component manages its own input internally (the input only exists in edit mode, so a forwarded ref would be null in display mode)
- Manages internal editing state with useState
## Admin Pages
@@ -117,11 +126,25 @@ interface InlineEditProps {
All pages use the standard AppShell + Sidebar + TopBar layout with breadcrumbs.
### Router Integration
Update `App.tsx` to replace the single `/admin` route with nested routes:
```tsx
<Route path="/admin" element={<Navigate to="/admin/rbac" replace />} />
<Route path="/admin/audit" element={<AuditLog />} />
<Route path="/admin/oidc" element={<OidcConfig />} />
<Route path="/admin/rbac" element={<UserManagement />} />
```
### Sidebar Integration
Add admin links to the Sidebar bottom links. The existing Sidebar component already supports `bottomLinks` — add entries for Audit Log, OIDC, and User Management.
Keep the existing single "Admin" bottom link in the Sidebar. The admin pages handle their own sub-navigation internally via a secondary nav bar at the top of each admin page (links to Audit Log, OIDC, User Management). This avoids cluttering the main sidebar with admin sub-entries.
Update the mock sidebar data in `src/mocks/sidebar.ts` to include admin navigation, and update the Sidebar usage across all example pages.
### Barrel Export Updates
- Add `MultiSelect` and `MultiSelectOption` type to `src/design-system/composites/index.ts`
- Add `ConfirmDialog` and `ConfirmDialogProps` type to `src/design-system/composites/index.ts`
- Add `InlineEdit` and `InlineEditProps` type to `src/design-system/primitives/index.ts`
### Page: Audit Log (`src/pages/Admin/AuditLog/`)
@@ -178,14 +201,14 @@ Update the mock sidebar data in `src/mocks/sidebar.ts` to include admin navigati
Add demos for all three new components:
- **MultiSelect** → PrimitivesSection: Demo showing multi-select with sample options, displaying selected count
- **MultiSelect** → CompositesSection: Demo showing multi-select with sample options, displaying selected count
- **InlineEdit** → PrimitivesSection: Demo showing display/edit toggle with a sample name
- **ConfirmDialog** → CompositesSection: Demo with a "Delete item" button that opens the dialog
## File Structure
```
src/design-system/primitives/MultiSelect/
src/design-system/composites/MultiSelect/
MultiSelect.tsx
MultiSelect.module.css
MultiSelect.test.tsx