Skip to content

Validation feedback, accessibility, and loading/error states #47

Description

@patoperpetua

Parent: #5
Depends on: #44, #45

Goal

Add validation feedback, accessibility, and loading/error states to @singleton-sd/post-kit-editor. The editor must validate the working template against its declared variable catalogue and preview data, surface problems clearly before save or test-send, and be operable by keyboard and screen reader.

Scope

Validation model

  • src/validation/validate.ts:
export type ValidationSeverity = 'error' | 'warning';

export interface ValidationIssue {
  severity: ValidationSeverity;
  /** Stable machine-readable identifier, e.g. `undeclared-variable`. */
  code: string;
  /** Human-readable message shown in the UI. */
  message: string;
  /** Which part of the editor the issue belongs to. */
  field: 'metadata' | 'subject' | 'document' | 'previewData';
  /** Variable name, when the issue concerns one. */
  variable?: string;
}

export function validateTemplate(files: TemplateSourceFiles): ValidationIssue[];

Rules to implement

Errors:

  • missing-key / missing-name / missing-subject — required metadata fields empty.
  • invalid-keykey contains characters outside the accepted set.
  • undeclared-variable — a {{placeholder}} used in the subject or document is not present in metadata.variables.
  • missing-preview-value — a declared variable has no value in previewData.
  • render-failed — the compiler rejects the current source (reuse the failure already surfaced by the preview pane rather than compiling twice).

Warnings:

  • unused-variable — declared in metadata.variables but not referenced in subject or document.
  • extra-preview-value — present in previewData but not declared.

Presentation

  • A validation summary region listing all current issues, grouped by severity, each linking focus to the responsible control.
  • Inline indication on the metadata/subject fields and preview-data rows that own an issue, associated via aria-describedby.
  • The summary region is an ARIA live region so newly appearing errors are announced.
  • Expose results to the host application via an optional prop:
onValidationChange?: (issues: ValidationIssue[]) => void;

Gating save and test-send

  • Save and Send-test controls are disabled while any error-severity issue exists, with an accessible explanation of why.
  • Warnings never block.
  • This is the issue that wires validation into the controls delivered by the save/send-test issue.

Loading and error states

  • loading?: boolean prop: when true, the editor renders a non-interactive loading state (used while the consumer fetches template files from its repository).
  • loadError?: string prop: when set, the editor renders an error state with that message instead of the editing surface.
  • An internal render/parse failure renders an error boundary fallback with a retry affordance rather than unmounting the host application.

Accessibility

  • All interactive controls reachable and operable by keyboard, in a logical tab order.
  • Every control has an accessible name; icon-only affordances carry aria-label.
  • Visible focus indicators throughout.
  • Panels use appropriate landmarks/headings so a screen-reader user can navigate between metadata, variables, canvas, and preview.
  • Status changes (save in progress, validation errors, copy confirmation) are announced via live regions.
  • Colour is never the sole carrier of validation meaning — pair it with text or an icon.

Constraints

  • Do not duplicate the compiler's rules — where @singleton-sd/post-kit-compiler already validates something, call it and translate its errors into ValidationIssue rather than reimplementing the check.
  • Validation must be pure and synchronous apart from the compiler-backed render check; no network calls.
  • Do not auto-fix the user's template. Report issues; never silently rewrite the document, metadata, or preview data.
  • Do not add an accessibility testing framework that requires a real browser; assert accessible names, roles, and aria-* wiring in the existing node --test setup.
  • Warnings must not block save.

Acceptance criteria

  • validateTemplate() implements every error and warning rule listed above and is exported from the package root along with ValidationIssue.
  • Undeclared variables used in the subject or document are reported as errors naming the variable.
  • Declared variables missing a preview value are reported as errors.
  • Unused declared variables and extra preview values are reported as warnings and do not block save.
  • The validation summary is an ARIA live region and each entry moves focus to the responsible control.
  • Inline field errors are associated with their controls via aria-describedby.
  • Save and Send-test are disabled while any error-severity issue exists, with an accessible explanation.
  • loading and loadError props render the documented non-interactive states.
  • A render/parse failure is caught by an error boundary with a retry affordance and does not unmount the host application.
  • All interactive controls are keyboard operable with accessible names and visible focus indicators.
  • Validation meaning is conveyed by text or icon, not colour alone.
  • onValidationChange fires with the current issue list whenever it changes.
  • Tests cover each validation rule, the save-gating behaviour, the loading/error states, and the accessible-name/aria-describedby wiring.
  • pnpm -r --if-present run test passes.

Agent implementation notes

Read the metadata/variable-catalogue state from #44 and the preview/compiler integration from #45 first — this issue consumes both and must not fork their state. Read packages/post-kit-compiler/src/compiler-error.ts to see what the compiler already reports before writing any duplicate rule. Branch: feat/<issue-number>-editor-validation-accessibility.

Metadata

Metadata

Assignees

No one assigned

    Labels

    blockedHas an unresolved Depends on: line (docs/github-source-of-truth.md, section 5) — do not claimenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions