Skip to content

feat: registration draft autosave, generic ConfirmDialog, component tests and Storybook - #144

Merged
codebestia merged 6 commits into
ShadeProtocol:mainfrom
Damola09:feat/registration-draft-confirm-dialog-tests-storybook
Jul 31, 2026
Merged

feat: registration draft autosave, generic ConfirmDialog, component tests and Storybook#144
codebestia merged 6 commits into
ShadeProtocol:mainfrom
Damola09:feat/registration-draft-confirm-dialog-tests-storybook

Conversation

@Damola09

Copy link
Copy Markdown
Contributor

Summary

Four related pieces of frontend groundwork: a registration autosave hook, a shared confirmation dialog, the missing test script plus the first UI primitive test suite, and Storybook for the src/components/ui/ primitives.

Closes #121
Closes #123
Closes #128
Closes #129

Changes

#121useRegistrationDraft autosave hook

  • src/hooks/use-registration-draft.ts: debounced-persists an in-progress draft to sessionStorage and restores it on mount. Validates the stored payload through a caller-supplied narrowing function, discards malformed JSON, cancels a pending write on unmount, and tolerates a full or blocked storage quota without breaking the flow.
  • Wired into RegisterClient so both the current step and the entered field values survive a refresh. The draft is cleared once registration completes.
  • The base64 business logo is excluded from the draft to stay well inside the sessionStorage quota, and a restored draft never resumes past step 2 because the OTP is only valid for the current attempt.

#123 — generic ConfirmDialog

  • src/components/ui/dialog.tsx: a Dialog primitive over @radix-ui/react-dialog using the shared theme tokens. The repo had the Radix dependency but no primitive, so modals were wiring it by hand.
  • src/components/confirm-dialog.tsx: built on that primitive. Covers the simple confirm/cancel flow and the typed-confirmation flow via requireTypedConfirmation, owning the input state, the reset on close, and the pending state for async onConfirm.
  • Migrated the two existing one-off modals onto it: InvoiceCancellationModal (was hand-wiring Radix) and DeleteApiKeyModal (was owning its own "type DELETE" state). Their existing tests pass unchanged.

#129 — component unit tests

  • Added test and test:watch scripts to package.json. Vitest, React Testing Library, vitest.config.ts, and vitest.setup.ts were already present on main — only the script was missing.
  • src/components/ui/button.test.tsx: covers variant and size class output, className merge precedence over variant classes, asChild Slot behaviour including prop forwarding, disabled click suppression, ref forwarding, and the buttonVariants export.

#128 — Storybook

  • Storybook 10 with @storybook/nextjs, plus storybook and build-storybook scripts.
  • .storybook/preview.tsx imports the app's own globals.css so stories render with the real Tailwind v4 theme, and uses @storybook/addon-themes to toggle the same dark class on <html> that ThemeProvider sets — both light and dark are reviewable from the toolbar.
  • src/components/ui/button.stories.tsx: playground, every variant, every size, the full variant x size matrix, disabled states, an icon child, and the asChild anchor case.

Verification

  • npm run typecheck — clean
  • npm run format:check — clean
  • npm run test — 11 files, 76 tests passed (49 new; main had 27)
  • npm run build-storybook — succeeds, with the dark theme tokens present in the emitted CSS
  • npm run storybook — dev server serves all 7 Button stories

Notes

npm run build fails on main today, from four lint errors that predate this branch: an unused reject in src/app/payment/[id]/page.tsx, an unused waitFor in InvoiceRowActions.test.tsx, a @ts-ignore in ProfileDetailsForm.test.tsx, and an unused error in api-key-reveal.tsx. They are left alone here as out of scope — this branch adds no new lint errors, and next lint is clean across every file it touches.

One Storybook-specific workaround is worth calling out: tsconfig.json maps lucide-react and qrcode.react to local TypeScript shims in src/lib/. Next rewrites imports of the packages in experimental.optimizePackageImports into __barrel_optimize__ requests, which the SWC loader deliberately skips — so those rewritten requests handed webpack unparsed TypeScript and failed the preview build. .storybook/main.ts drops the two shimmed packages from that option and aliases them to the shim files directly, so Storybook renders the same icons the app does.

src/components/ui/confirmation-modal.tsx is now unused but left in place rather than deleted, in case other planned work still expects it.

Damola09 added 5 commits July 30, 2026 02:26
Wire Vitest into package.json via test and test:watch scripts, and add
the first UI primitive test suite covering Button variant/size class
output, className merge precedence, asChild Slot behaviour, disabled
click suppression, ref forwarding, and the buttonVariants export.

Closes ShadeProtocol#129
RegisterClient kept all entered values in useState only, so a refresh
before the final saveMerchantProfile call lost every field.

Add useRegistrationDraft, a debounced sessionStorage draft hook that
restores on mount, validates the stored payload, cancels pending writes
on unmount, and tolerates a full or blocked storage quota. Wire it into
RegisterClient so both the current step and the entered field values
survive a refresh, and clear the draft once registration completes.

The base64 business logo is excluded from the draft to stay inside the
sessionStorage quota, and a restored draft never resumes past step 2
because the OTP is only valid for the current attempt.

Closes ShadeProtocol#121
Confirmation modals were being re-implemented per feature: the invoice
cancellation modal wired Radix Dialog by hand, and the API key deletion
modal owned its own "type DELETE" input state on top of a separate
portal-based ConfirmationModal.

Add a Dialog primitive in src/components/ui/dialog.tsx using the shared
theme tokens, and build ConfirmDialog on top of it. ConfirmDialog covers
both the simple confirm/cancel flow and the typed-confirmation flow via
requireTypedConfirmation, owning the input state, the reset on close and
the pending state for async confirms.

Migrate InvoiceCancellationModal and DeleteApiKeyModal onto it so both
are now configuration rather than bespoke modal implementations. Their
existing tests pass unchanged.

Closes ShadeProtocol#123
The src/components/ui/ primitives had no isolated environment to develop
or visually review them outside the app's pages.

Add Storybook 10 with the Next.js framework, wired to the app's own
globals.css so stories render with the real Tailwind v4 theme, and use
addon-themes to toggle the same `dark` class on <html> that
ThemeProvider sets, giving both light and dark review in the toolbar.

Add Button stories covering the playground, every variant, every size,
the full variant x size matrix, disabled states, an icon child and the
asChild anchor case.

tsconfig maps lucide-react and qrcode.react to local TypeScript shims.
Next's optimizePackageImports rewrites those imports into
__barrel_optimize__ requests that the SWC loader skips, which handed
webpack unparsed TypeScript, so the two shimmed packages are dropped
from that option and aliased to the shim files directly.

Closes ShadeProtocol#128
next lint's no-html-link-for-pages rule flags a bare <a> pointing at an
app route, which broke next build. The tests only need Slot to render an
anchor, so they point at an external URL.
@drips-wave

drips-wave Bot commented Jul 30, 2026

Copy link
Copy Markdown

@Damola09 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@codebestia
codebestia merged commit c6c7c07 into ShadeProtocol:main Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants