Per-component audit of all canary components in @arda-cards/design-system
(ux-prototype repository) against the architectural framework established
by:
Each component is evaluated against these criteria:
| Criterion | Compliant | Needs Change |
|---|
| No TanStack import | Component has no useQuery/useMutation | Component fetches data directly |
| Typed data contract | Data comes via typed props or typed provider hooks | Data comes via untyped callbacks or unknown[] keys |
| Edit lifecycle | Uses initialValue → draft → onConfirm/onCancel | Mutates parent state directly or lacks lifecycle callbacks |
| Intrinsic validation | Validates own fields, exposes ValidationResult | No validation or validation is ad-hoc |
| Contextual errors | Accepts contextErrors from parent | No mechanism for parent-injected errors |
| Standardized types | Uses EditableComponentProps<T>, ValidationResult, etc. | Custom one-off interfaces |
Components rated:
- Compliant — no changes needed for the target architecture
- Minor — small additions (add
contextErrors prop, export validation function)
- Moderate — interface restructuring (replace callbacks with typed providers, add lifecycle)
- Major — significant rework (data fetching moved out, state model changed)
Three diagrams showing how components compose. Leaf components are primitives
or atoms; branching nodes are molecules or organisms that contain children.
Annotations show the data contract at each boundary.





Primitives are vendored ShadCN/Radix components. They are stateless UI
building blocks with no data-fetching, no business logic, and no edit
lifecycle. All compliant — no changes needed.
| Component | Status | Notes |
|---|
alert-dialog | Compliant | Radix primitive wrapper |
aspect-ratio | Compliant | Radix primitive wrapper |
button | Compliant | CVA variants, loading state |
checkbox | Compliant | Radix primitive wrapper |
collapsible | Compliant | Radix primitive wrapper |
dropdown-menu | Compliant | Radix primitive wrapper |
input | Compliant | Styled HTML input |
label | Compliant | Radix label wrapper |
popover | Compliant | Radix primitive wrapper |
progress | Compliant | Radix primitive wrapper |
separator | Compliant | Radix primitive wrapper |
sheet | Compliant | Radix primitive wrapper |
sidebar | Compliant | Radix sidebar primitive with context |
skeleton | Compliant | CSS-only loading placeholder |
slider | Compliant | Radix primitive wrapper |
table | Compliant | Styled HTML table |
tabs | Compliant | Radix primitive wrapper |
textarea | Compliant | Styled HTML textarea |
toggle | Compliant | Radix primitive wrapper |
tooltip | Compliant | Radix primitive wrapper |
Simple presentational components with no data-fetching or complex state.
| Component | Status | Notes |
|---|
avatar | Compliant | Renders initials/image; no state |
badge | Compliant | CVA-styled label |
badge-base | Compliant | Base badge component |
brand-logo | Compliant | Static SVG/image |
button | Compliant | Styled button with loading |
card | Compliant | Container with styling |
dialog | Compliant | Radix dialog wrapper |
drawer | Compliant | Radix drawer wrapper |
icon-button | Compliant | Icon-only button variant |
icon-label | Compliant | Icon + text pair |
input-group | Compliant | Input with addon/button |
read-only-field | Compliant | Label + value display |
search-input | Compliant | Debounced search input |
copyright-acknowledgment | Compliant | Controlled checkbox — stateless |
| Component | Status | Change needed |
|---|
confirm-dialog | Minor | Currently has a custom interface (ArdaConfirmDialogStaticConfig + ArdaConfirmDialogRuntimeConfig). Should adopt standardized edit lifecycle types when available. Add contextErrors capability for forms that use it inside validation flows. |
All cell display components are pure renderers receiving value and data
from AG Grid’s row model. No data fetching, no state.
| Component | Status | Notes |
|---|
boolean-cell-display | Compliant | Renders checkbox icon from value |
color-cell-display | Compliant | Renders color swatch from value |
date-cell-display | Compliant | Formats date from value |
image-cell-display | Compliant | Renders ImageDisplay from value |
memo-cell-display | Compliant | Renders truncated text from value |
number-cell-display | Compliant | Formats number from value |
select-cell-display | Compliant | Renders label from value + options map |
text-cell-display | Compliant | Renders text from value |
Cell editors manage local state and interact with AG Grid’s edit lifecycle.
Most are value-only editors (Category 1 from section 6.2 of
tanstack-component-binding-analysis.md) and need minor changes. The image
cell editor is a mutation editor (Category 3) needing moderate changes.
| Component | Status | Change needed |
|---|
boolean-cell-editor | Minor | Value-only editor. Currently self-contained. Add standardized ValidationResult for intrinsic validation (boolean is always valid, but the pattern should be consistent). |
color-cell-editor | Minor | Value-only editor with static options. No data-fetching. Standardize lifecycle types. |
date-cell-editor | Minor | Value-only editor. Should add intrinsic validation (reject invalid dates like Feb 30). Currently no validation — date parsing delegated to browser’s date input. |
image-cell-editor | Moderate | Mutation editor. Currently receives config via factory createImageCellEditor(config). Under FD-01, factory should also accept typed provider hooks (useImageUpload, useCheckReachability). The existing callback interface (onUpload, onCheckReachability on ImageUploadDialog) is already close to FD-01; the factory just needs to forward them. |
memo-cell-editor | Minor | Value-only editor. Rich text editing. Standardize lifecycle. |
memo-button-cell | Minor | Button that opens memo editor. No data-fetching. |
create-memo-button-cell-editor | Minor | Similar to memo-button-cell. Standardize lifecycle. |
number-cell-editor | Minor | Value-only editor. Should add intrinsic validation (numeric range, integer vs. decimal). Currently accepts any input. |
select-cell-editor | Minor | Value-only editor with static options array. No data-fetching. Standardize lifecycle. |
text-cell-editor | Minor | Value-only editor. Simplest case. Standardize lifecycle. |
action-cell-renderer | Compliant | Not an editor — renders action buttons. Callbacks come from column def. |
| Component | Status | Notes |
|---|
field-list | Compliant | Renders array of FieldDef; no state, no fetching |
grid-action | Compliant | Action button wrapper for grid cells |
image-comparison-layout | Compliant | Side-by-side layout; pure rendering |
image-hover-preview | Compliant | Popover on hover; manages delay timer only |
item-details-card-preview | Compliant | Static card rendering |
item-details-header | Compliant | Header with title, image, and actions |
drag-header | Compliant | AG Grid custom header; no data-fetching |
| Component | Status | Notes |
|---|
sidebar-header | Compliant | Static rendering |
sidebar-nav | Compliant | Static rendering |
sidebar-nav-group | Compliant | Static rendering |
sidebar-nav-item | Compliant | Static rendering |
sidebar-user-menu | Compliant | Static rendering with callbacks |
sidebar-story-decorator | Compliant | Storybook-only decorator |
| Component | Status | Notes |
|---|
action-toolbar | Compliant | Renders action buttons from config; manages overflow |
overflow-toolbar | Compliant | Responsive toolbar with overflow menu |
| Component | Status | Change needed |
|---|
data-grid | Minor | AG Grid wrapper (DataGrid). Accepts rowData as prop — compliant with FD-01 (data provided externally). Manages column persistence, sort state, and selection internally. Needs no data-fetching changes. Should add typed onCellValueChanged callback with ValidationResult when cell editing evolves. |
sort-menu-header | Compliant | Custom sort header; no data-fetching |
| Component | Status | Change needed |
|---|
image-display | Minor | Renders image with loading/error states. Currently manages loadState internally via onLoad/onError on <img>. Under hierarchical edit lifecycle, should accept onImageChange as a typed callback (already does). Needs contextErrors prop for form contexts. |
image-drop-zone | Minor | Unified input surface. Accepts onInput: (input: ImageInput) => void callback — already FD-01 compliant. Performs intrinsic validation (MIME type, HTTPS URL). Should export a validateImageInput() function for parent composition. Does not fetch data (HEIC conversion is local). |
image-form-field | Minor | Form field wrapper. Accepts config, imageUrl, onChange. Already FD-01 compliant (data via props). **Should adopt `EditableComponentProps<string |
image-inspector-overlay | Compliant | Modal image viewer; pure rendering with optional onEdit callback. |
image-preview-editor | Compliant | Crop/zoom/rotate editor; manages crop state locally, pushes via onCropChange. Already follows the draft-push pattern. |
| Component | Status | Change needed |
|---|
item-grid-columns | Moderate | Column definition factory (createItemGridColumnDefs). Currently accepts ItemGridLookups — an interface with supplier? and classificationType? lookup functions typed as (search: string) => Promise<TypeaheadOption[]>. This is already a typed provider pattern (FD-01 compliant for lookups). Changes needed: (1) Expand ItemGridLookups to include all lookup fields (currently only 2 of 9+). (2) Add ImageCellEditor factory wiring for useImageUpload/useCheckReachability hooks. (3) Generalize to a broader GridEditorProviders interface that covers all editor types. |
| Component | Status | Change needed |
|---|
typeahead-cell-editor | Moderate | Currently accepts lookup: (search: string) => Promise<TypeaheadOption[]> — this is a typed async callback, which is the correct FD-01 pattern for data that must be fetched during editing. The callback is already TanStack-agnostic. Changes needed: (1) The lookup prop should be renamed to a hook-style interface for consistency: useLookup: (query: string) => { data, isLoading } — this lets the component react to loading state from the hook rather than managing its own loading/error state from the promise. (2) Add contextErrors support. (3) Export intrinsic validation function. (4) The manual debounce + AbortController logic (~30 lines) would be replaced by TanStack Query’s built-in deduplication and cancellation when the hook is TanStack-backed — but the component shouldn’t know that. Alternative: keep the current lookup promise interface — it’s simpler than a hook and the component already handles loading/error well. The debounce is a UI concern (don’t hammer the server) that belongs in the component regardless of the data source. This is a design decision. |
| Component | Status | Notes |
|---|
app-header | Compliant | Static rendering with callbacks |
sidebar | Compliant | Renders nav from config |
sidebar-context | Compliant | Context provider for sidebar state |
| Component | Status | Change needed |
|---|
image-upload-dialog | Moderate | State machine orchestrator. Currently accepts onUpload: (file: Blob) => Promise<string> and onCheckReachability: (url: string) => Promise<boolean> — these are already typed callback props (FD-01 compliant). Changes needed: (1) The upload progress is simulated internally (50ms timer). Under the hierarchical edit lifecycle, the onUpload callback should support progress reporting — either via an onProgress callback parameter or by returning a richer result. This is the Open Design Question from upload-component-backend-analysis.md. (2) Add UploadError state handling for production retry flows. (3) The onConfirm callback returns ImageUploadResult — should adopt EditLifecycleCallbacks<ImageUploadResult> for consistency. (4) Add contextErrors support. |
| Component | Status | Change needed |
|---|
item-card-editor | Moderate | WYSIWYG card editor. Currently accepts fields: ItemCardFields + onChange + onImageConfirmed. This is a draft-management component that already follows the hierarchical edit pattern partially (local state → push via onChange). Changes needed: (1) Adopt EditableComponentProps<ItemCardFields> — rename fields to initialValue, add onConfirm/onCancel. Currently only has onChange (every keystroke pushes up) without a confirm/cancel lifecycle. (2) Add intrinsic validation (required title). (3) Add contextErrors support. (4) The internal ImageUploadDialog needs typed provider hooks wired through (same as image-cell-editor — factory or context injection). |
| Component | Status | Change needed |
|---|
item-details | Minor | Drawer panel showing item fields. Accepts all data via props (title, fields, cardPreview, actions). FD-01 compliant — no data-fetching. The component is a pure renderer. Needs contextErrors support if it becomes editable in the future. Currently read-only. |
| Component | Status | Change needed |
|---|
item-grid | Minor | Wraps createEntityDataGrid factory. Accepts items (data) and lookups (typed provider callbacks) via props — already FD-01 compliant for data sourcing. The lookups: ItemGridLookups interface is a typed provider. Changes needed: expand ItemGridLookups to cover all lookup fields and image editor hooks. The onPublishRow callback is a mutation trigger — under the hierarchical lifecycle, this becomes the entity-level onConfirm. |
| Component | Status | Change needed |
|---|
create-entity-data-grid | Minor | Factory that creates typed grid components. Accepts data and onRowPublish via model props — FD-01 compliant. The onRowPublish: (rowId, changes, entity?) => Promise<void> is effectively an untyped mutation callback. Under the standardized types, this should adopt EditLifecycleCallbacks for row-level edit operations. The factory’s EntityDataGridConfig already follows the Static/Init separation pattern. |
create-entity-data-grid-shim | Compliant | Backward-compatible shim; no data-fetching. |
| File | Status | Change needed |
|---|
image-field-config.ts | Compliant | Types only; no data-fetching |
image-upload-handlers.ts | Compliant | Default stubs for Storybook; production implementations provided by app |
get-cropped-image.ts | Compliant | Canvas utility; no data-fetching |
maybe-convert-heic.ts | Compliant | File conversion utility; no data-fetching |
__mocks__/image-story-data.ts | Compliant | Test/story fixtures |
New types needed (from hierarchical-edit-lifecycle-exploration.md):
| Type | Purpose |
|---|
ValidationResult | Standardized validation output |
FieldError | Per-field error descriptor |
EditLifecycleCallbacks<T> | onChange, onConfirm, onCancel |
EditableComponentProps<T> | Base props for any editable component |
useDraft<T> hook | Reusable draft management |
| Tier | Total | Compliant | Minor | Moderate | Major |
|---|
| Primitives | 20 | 20 | 0 | 0 | 0 |
| Atoms (UI) | 14 | 14 | 0 | 0 | 0 |
| Atoms (grid cell displays) | 8 | 8 | 0 | 0 | 0 |
| Atoms (grid cell editors) | 10 | 1 | 8 | 1 | 0 |
| Molecules (presentational) | 15 | 15 | 0 | 0 | 0 |
| Molecules (data/image) | 7 | 2 | 3 | 2 | 0 |
| Organisms | 8 | 3 | 2 | 3 | 0 |
| Total | 82 | 63 | 13 | 6 | 0 |
These are the components that need interface restructuring:
image-cell-editor — Factory needs typed provider hooks for upload/reachability
typeahead-cell-editor — lookup callback may evolve to hook interface; design decision pending
item-grid-columns — Expand ItemGridLookups to all fields, add image editor hook wiring
image-upload-dialog — Upload progress interface, UploadError state, standardized lifecycle types
item-card-editor — Adopt EditableComponentProps, add confirm/cancel lifecycle, wire image providers
item-grid — Expand lookups interface to match column needs
Pattern: add contextErrors prop, export validation function, adopt
standardized types. These are additive — no breaking interface changes.
The canary components are already well-aligned with FD-01. The key
patterns — typed callback props for data and mutations, data via props not
fetching, factory functions for AG Grid editors — are already in place. The
changes are evolutionary (standardize types, add validation, expand provider
interfaces) rather than revolutionary.
The 6 moderate-change components are exactly the image-related and grid
components that the image upload project touches. The standardized types
(ValidationResult, EditableComponentProps<T>, useDraft<T>) should be
introduced as part of this project and applied to the image components first.
Other canary components can adopt the types incrementally in subsequent
projects.
Copyright: (c) Arda Systems 2025-2026, All rights reserved