feat: registration draft autosave, generic ConfirmDialog, component tests and Storybook - #144
Merged
codebestia merged 6 commits intoJul 31, 2026
Conversation
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.
|
@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! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four related pieces of frontend groundwork: a registration autosave hook, a shared confirmation dialog, the missing
testscript plus the first UI primitive test suite, and Storybook for thesrc/components/ui/primitives.Closes #121
Closes #123
Closes #128
Closes #129
Changes
#121 —
useRegistrationDraftautosave hooksrc/hooks/use-registration-draft.ts: debounced-persists an in-progress draft tosessionStorageand 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.RegisterClientso both the current step and the entered field values survive a refresh. The draft is cleared once registration completes.sessionStoragequota, and a restored draft never resumes past step 2 because the OTP is only valid for the current attempt.#123 — generic
ConfirmDialogsrc/components/ui/dialog.tsx: a Dialog primitive over@radix-ui/react-dialogusing 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 viarequireTypedConfirmation, owning the input state, the reset on close, and the pending state for asynconConfirm.InvoiceCancellationModal(was hand-wiring Radix) andDeleteApiKeyModal(was owning its own "type DELETE" state). Their existing tests pass unchanged.#129 — component unit tests
testandtest:watchscripts topackage.json. Vitest, React Testing Library,vitest.config.ts, andvitest.setup.tswere already present onmain— only the script was missing.src/components/ui/button.test.tsx: covers variant and size class output,classNamemerge precedence over variant classes,asChildSlot behaviour including prop forwarding, disabled click suppression, ref forwarding, and thebuttonVariantsexport.#128 — Storybook
@storybook/nextjs, plusstorybookandbuild-storybookscripts..storybook/preview.tsximports the app's ownglobals.cssso stories render with the real Tailwind v4 theme, and uses@storybook/addon-themesto toggle the samedarkclass on<html>thatThemeProvidersets — 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 theasChildanchor case.Verification
npm run typecheck— cleannpm run format:check— cleannpm run test— 11 files, 76 tests passed (49 new;mainhad 27)npm run build-storybook— succeeds, with the dark theme tokens present in the emitted CSSnpm run storybook— dev server serves all 7 Button storiesNotes
npm run buildfails onmaintoday, from four lint errors that predate this branch: an unusedrejectinsrc/app/payment/[id]/page.tsx, an unusedwaitForinInvoiceRowActions.test.tsx, a@ts-ignoreinProfileDetailsForm.test.tsx, and an unusederrorinapi-key-reveal.tsx. They are left alone here as out of scope — this branch adds no new lint errors, andnext lintis clean across every file it touches.One Storybook-specific workaround is worth calling out:
tsconfig.jsonmapslucide-reactandqrcode.reactto local TypeScript shims insrc/lib/. Next rewrites imports of the packages inexperimental.optimizePackageImportsinto__barrel_optimize__requests, which the SWC loader deliberately skips — so those rewritten requests handed webpack unparsed TypeScript and failed the preview build..storybook/main.tsdrops 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.tsxis now unused but left in place rather than deleted, in case other planned work still expects it.