Conversation
Add the standalone statement renderer and its focused tests. Co-authored-by: Cursor <cursoragent@cursor.com>
Set an explicit TypeScript base URL for clean remote builds. Co-authored-by: Cursor <cursoragent@cursor.com>
Add the Mintlify page, restore its Overview navigation entry, and point the embed at the public playground project. Co-authored-by: Cursor <cursoragent@cursor.com>
Fork the Cards chrome and layout contract into Statements. Drive the statement, API samples, and totals from one typed model. Reuse Cards presets and real Grid OpenAPI operation IDs. Co-authored-by: Cursor <cursoragent@cursor.com>
Document the cp-based chrome port contract. Add byte-parity, behavior, and visual audit scripts. Keep generated browser evidence out of Git. Co-authored-by: Cursor <cursoragent@cursor.com>
Map Mintlify preview branches to the Statements Vercel preview host. Pass embed, theme, and navigation state in the iframe URL. Co-authored-by: Cursor <cursoragent@cursor.com>
State that Grid account balances are current data and that the platform stores period balances. Generate distinct transaction identities for each statement period and pin the rebuild behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
3 Skipped Deployments
|
Rename the Vercel project to grid-statements-demo to match the Cards and Wallet projects and the components directory. Mirror the Cards embed snippet so Mintlify previews use the branch-derived Vercel host. Update the layout metadata base and the chrome parity exception. Document the 22-character branch name budget. Co-authored-by: Cursor <cursoragent@cursor.com>
Reg E periodic statements cover the Lead Bank prepaid account behind the card program, and card purchases are the statement line items, so the playground belongs in the Cards tab next to the Cards playground. - global-accounts/periodic-statements-playground -> cards/statements - snippet moved to snippets/cards/statements-demo-embed.mdx - redirect added for the old path - ls-page-playground path lists updated (head raw + sidebar-toggle.js) - sidebar label is the single word "Statements" to avoid wrapping Co-authored-by: Cursor <cursoragent@cursor.com>
|
Docs nav: the statements playground now lives in the Cards tab (commit 0e88008).
Why Cards: Reg E periodic statements cover the Lead Bank prepaid account behind the card program, and card purchases are the statement line items. Update (b45defc): both statements pages moved out of Overview into a new Cards sidebar group, Statements, placed directly after Transactions. Cards group order: Overview → Onboarding → Card management → Transactions → Statements → Card design → Platform tools. Pages in the group: Update (ab8b389): the guide page |
Add four typed statement color tokens, WCAG checks, print colors, and Cards-style controls. Co-authored-by: Cursor <cursoragent@cursor.com>
Parameterize the Cards AppShell shape for standard iPhone and iPhone Duo Mail views. Co-authored-by: Cursor <cursoragent@cursor.com>
Refresh calls for preset, account, and period changes without reacting to brand edits. Co-authored-by: Cursor <cursoragent@cursor.com>
Add a two-action export preview, print isolation, and self-contained HTML downloads. Co-authored-by: Cursor <cursoragent@cursor.com>
Lock the Duo outer preset, finish control and accessibility polish, and enforce browser audit evidence. Co-authored-by: Cursor <cursoragent@cursor.com>
Match the official open-body and inner-display proportions, and remove the invented camera treatment from the device preset. Co-authored-by: Cursor <cursoragent@cursor.com>
Assert both the official open-body ratio and inner-display ratio. Co-authored-by: Cursor <cursoragent@cursor.com>
Remove the portrait camera offset so the landscape status bar uses the shared safe inset. Co-authored-by: Cursor <cursoragent@cursor.com>
Use the measured closed outer geometry required for the Mail preview. Restore the asymmetric hinge bezel, corner radii, and camera hole. Co-authored-by: Cursor <cursoragent@cursor.com>
Restore the locked open-device geometry after a concurrent commit changed the view to the closed outer display. Assert both official ratios and use the neutral landscape safe inset. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the Mail preview on the measured closed outer display. Restore the asymmetric hinge bezel, corner radii, and camera hole. Co-authored-by: Cursor <cursoragent@cursor.com>
Move the live brand into a full-height sidebar with empty content and footer zones. Keep the main header empty and the statement scrolling inside the right column. Pin the shared direct and Export frame across desktop sizes and themes. Co-authored-by: Cursor <cursoragent@cursor.com>
- simplify the desktop shell and recess its document surface - make the mobile statement full bleed with scroll elevation - remove the redundant commercial footer divider - audit consumer and commercial screen, HTML, and PDF output Co-authored-by: Cursor <cursoragent@cursor.com>
Empty the desktop rail and sink the whole frame so only the statement card lifts. Bleed the mobile document under a scrolled header, drop the spare Commercial footer hairline, and match Cards API rail copy. Co-authored-by: Cursor <cursoragent@cursor.com>
Compare every chrome surface to the token-derived sunken mix, not the primary background, so the lifted statement card is the only exception. Co-authored-by: Cursor <cursoragent@cursor.com>
The frame surfaces now share the token-derived sunken mix. Keep the document on primary background as the only lifted element. Co-authored-by: Cursor <cursoragent@cursor.com>
Validate primary chrome around the token-derived recessed document area. Co-authored-by: Cursor <cursoragent@cursor.com>
Apply the recessed token only behind the statement document. Co-authored-by: Cursor <cursoragent@cursor.com>
The playground already groups Load statement under one divider; tests must reject the old explanatory sentence and require unified sunken frame surfaces. Co-authored-by: Cursor <cursoragent@cursor.com>
Add mintlify/cards/statements-guide.mdx and list it in the Cards tab, Overview group, after the statements playground. The page covers who receives a statement, the monthly and quarterly cadence (12 CFR 1005.9(b)), the section-to-field mapping for each Reg E item, consumer versus commercial differences, the three API calls the playground uses to build a statement view, delivery, and a note on what is not yet available (no statement endpoint, webhook, or balance snapshots). No back-link from the playground page: its CSS hides every child of Co-authored-by: Cursor <cursoragent@cursor.com> #content except the embed container.
|
Held at
|
Reframe the guide around what the API returns today. The Platform builds and delivers the statement from getCustomerById, listCustomerInternalAccounts, and listTransactions. Remove every sentence that said Grid generates, renders, or provides a statement artifact, and drop the "Not yet available" note. The note's content moves verbatim to components/grid-statements-demo/docs/prd-checklist.md under "Open items (not for public docs)". Balances: the page now says the Platform records the balance at period close and carries it forward, or derives the opening balance from the period's activity. Co-authored-by: Cursor <cursoragent@cursor.com>
Move the periodic statements guide and playground out of Cards → Overview into a new `Statements` group placed directly after `Transactions`. Labels are one word each within the group: "Periodic statements" (guide) and "Playground" (playground). Icons are unique within the Cards tab: IconNewspaper2.svg for the guide, receipt-check.svg for the playground (phone.svg stays with the Cards Playground). Paths and the /global-accounts/periodic-statements-playground redirect are unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
Ship only the Statements playground page. Remove mintlify/cards/statements-guide.mdx from the docs tree and drop cards/statements-guide from the Statements sidebar group. The group stays after Transactions with the single page cards/statements (Playground, receipt-check.svg). The guide is held, not deleted. The draft moves to components/grid-statements-demo/docs/statements-guide-draft.md with a header that records the hold reasons. prd-checklist.md points to it. Held 2026-09-22 pending: - Settled ownership of statement generation and delivery. The PRD says Grid generates; MSA 2.6(c) says Lightspark provides required notices; the draft said the partner produces. - Legal review of 12 CFR 1005.18 prepaid applicability and of commercial error language. - Accuracy fixes: paymentRail is not a top-level transaction field; the amount rule must key on type, not direction; balance is current-only; card transactions carry no fees; RTP Reg E coverage is unverified; the 60-day window runs from the send date. Verified on node 22 with mint 4.2.284: docs.json parses, mint broken-links has 0 hits for cards/statements, /cards/statements renders the Playground, /cards/statements-guide renders Page Not Found, and markdownlint passes on the moved draft. Co-authored-by: Cursor <cursoragent@cursor.com>
|
| url.searchParams.set('background', state.brand.colors.primaryBackground); | ||
| url.searchParams.set('text', state.brand.colors.primaryText); | ||
| url.searchParams.set('muted', state.brand.colors.secondaryText); | ||
| url.searchParams.set('logo', state.brand.logo.kind === 'none' ? 'none' : 'preset'); |
There was a problem hiding this comment.
When a user uploads a custom logo and copies a share link, this code serializes the logo as logo=preset. Opening that link reconstructs the selected preset’s icon instead of the uploaded logo, so the shared statement does not preserve the branding that was shown when the link was copied. The share state should either preserve uploaded logos or clearly exclude them from sharing.
Prompt To Fix With AI
This is a comment left during a code review.
Path: components/grid-statements-demo/src/statement/shareState.ts
Line: 31
Comment:
**Uploaded logos are lost**
When a user uploads a custom logo and copies a share link, this code serializes the logo as `logo=preset`. Opening that link reconstructs the selected preset’s icon instead of the uploaded logo, so the shared statement does not preserve the branding that was shown when the link was copied. The share state should either preserve uploaded logos or clearly exclude them from sharing.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Fixed in e64f73b. statementShareUrl now emits logo=none when the logo src starts with blob: (new isUploadedLogo helper in components/grid-statements-demo/src/statement/shareState.ts). Tests added in components/grid-statements-demo/src/statement/shareState.test.ts and components/grid-statements-demo/src/components/ShareSheet/ShareSheet.test.tsx. The Export sheet shows "Uploaded logos are not included in the link." when the current logo is an upload.
| useEffect(() => { | ||
| if (mobileView !== 'playground') return; | ||
| history.pushState({ mobileView: 'playground' }, ''); | ||
| const onPop = () => { | ||
| setMobileView('configure'); | ||
| window.scrollTo(0, 0); | ||
| }; | ||
| window.addEventListener('popstate', onPop); | ||
| return () => window.removeEventListener('popstate', onPop); | ||
| }, [mobileView]); |
There was a problem hiding this comment.
Mobile history entries accumulate
Every transition into the mobile playground pushes a history entry, but the Configure button only changes React state and leaves that entry on the stack. Repeating Explore → Configure therefore accumulates same-document entries, making users press Back multiple times to leave the page. The popstate handler also switches to Configure for every pop event without checking whether the entry belongs to this view.
Prompt To Fix With AI
This is a comment left during a code review.
Path: components/grid-statements-demo/src/app/page.tsx
Line: 217-226
Comment:
**Mobile history entries accumulate**
Every transition into the mobile playground pushes a history entry, but the Configure button only changes React state and leaves that entry on the stack. Repeating Explore → Configure therefore accumulates same-document entries, making users press Back multiple times to leave the page. The `popstate` handler also switches to Configure for every pop event without checking whether the entry belongs to this view.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Confirmed. This block is verbatim from the sibling playgrounds: Cards components/grid-cards-demo/src/app/page.tsx:99-108, Wallet components/grid-wallet-demo/src/app/page.tsx:99-108, and the same pattern in Visualizer components/grid-visualizer/src/app/page.tsx:81-90. The fix (call history.back() from goConfigure when history.state?.mobileView === 'playground', and guard onPop on the popped state) lands in one follow-up PR across all four playgrounds so mobile Back behaves the same everywhere. Tracking: #1059
| const handleMessage = (e) => { | ||
| if (e.data && e.data.type === 'theme-request') { | ||
| sendTheme(); | ||
| return; | ||
| } | ||
| if (e.data && e.data.type === 'nav-request') { | ||
| sendNav(); | ||
| return; | ||
| } | ||
| if (e.data && e.data.type === 'theme-sync') { | ||
| const wantsDark = e.data.theme === 'dark'; | ||
| if (isDark() !== wantsDark) { | ||
| ignoreNextMutation = true; | ||
| document.documentElement.classList.toggle('dark'); | ||
| } | ||
| } | ||
| }; | ||
| window.addEventListener('message', handleMessage); |
There was a problem hiding this comment.
Iframe messages trust all senders
The embed accepts theme-sync and nav-request messages based only on their type, without checking the sender window or origin, and replies using targetOrigin="*". Another frame or window with a reference to the docs page can therefore spoof these messages, alter the page theme, or solicit synchronization traffic. Restrict incoming messages to the expected iframe and origin, and send replies to that specific origin.
Prompt To Fix With AI
This is a comment left during a code review.
Path: mintlify/snippets/cards/statements-demo-embed.mdx
Line: 77-94
Comment:
**Iframe messages trust all senders**
The embed accepts `theme-sync` and `nav-request` messages based only on their type, without checking the sender window or origin, and replies using `targetOrigin="*"`. Another frame or window with a reference to the docs page can therefore spoof these messages, alter the page theme, or solicit synchronization traffic. Restrict incoming messages to the expected iframe and origin, and send replies to that specific origin.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Confirmed. handleMessage is byte-identical to mintlify/snippets/cards/cards-demo-embed.mdx:77-94 and mintlify/snippets/global-accounts/wallet-demo-embed.mdx:68-85. Impact is a docs-page theme flip and extra sync traffic; no data is exposed. The fix (e.source === iframe.contentWindow check, targetOrigin = new URL(base).origin) lands in the same follow-up across all three snippets, together with the parity-pinned src/hooks/useTheme.ts in Cards, Wallet, and Statements, which must change in one commit to keep check-chrome-parity.mjs green. Tracking: #1059
Uploaded logos are blob: URLs and cannot travel in a URL. The share link previously encoded them as logo=preset, so recipients saw the preset icon instead. Emit logo=none for uploads, add a test, and show a hint in the Export sheet. Addresses Greptile review on #1032. Co-authored-by: Cursor <cursoragent@cursor.com>
Delete dead modules and exports (refresh.ts, reconcileApiEntries, unused brand constants, dead CSS), enable noUnusedLocals/Parameters, fix keyboard dead-end on custom color swatches, correct the Reg E 60-day wording to run from send, clear all API replay timers on unmount, use normalizeHexColor in the color picker, replace nested main/h1 landmarks, drop branch-only redirect, and move held internal docs out of the public repo. Co-authored-by: Cursor <cursoragent@cursor.com>
Remove audit scripts, their npm entries, and the playwright dev dependency; remove internal planning docs; trim README deploy notes to what is needed to run and embed the playground. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Superseded by #1060 (single-commit resubmission with review feedback applied and shared chrome re-synced to main). |
What
Adds the Statements playground under Cards. It builds a consumer or commercial periodic statement from sample Grid customer, account, and transaction data, and shows the exact API calls behind it.
The app is a re-fork of the Cards playground chrome, with the Wallet
AppShellfor the device frame. It exports the statement as a one-page PDF or a self-contained HTML file.The docs page embeds the production app at
/cards/statements.Try it
/cards/statementsWhat's in the diff
components/grid-statements-demo/— new Next.js app (port 4003). Statement model, fixtures, and API samples live insrc/statement/.scripts/check-chrome-parity.mjschecks byte parity with Cards and Wallet.mintlify/cards/statements.mdx+mintlify/snippets/cards/statements-demo-embed.mdx— the docs page and its iframe snippet.mintlify/docs.json— new Statements group after Transactions; head script adds/cards/statementsto thels-page-playgroundclass.mintlify/sidebar-toggle.js— same/cards/statementsroute for the playground class.components/grid-statements-demo/README.md— develop, parity, deploy, embed, and data contract.No OpenAPI changes. No changes to Cards, Wallet, or Flow Builder.
Parity with Cards/Wallet
npm run check:chromecompares every copied file to Cards (or Wallet) by bytes. It passes at194e8552.Byte-equal with Cards:
next.config.mjs,public/fonts,src/app/fonts,src/app/globals.scss,src/apps/shared/typography,src/components/ApiPanel,ColumnResizeHandle,liquid-glass/squircle.ts,GridWordmark.tsx,PanelHeader,SectionDivider,ThemeSync.tsx,Tooltip,src/data/actions.ts,flow.ts,src/hooks/useColumnResize.ts,useNowTick.ts,useTheme.ts,useThemeMode.ts,src/lib/apiCodeFormat.tsx,easing.ts,formatRelativeTime.ts,groupApiEntries.ts,layout.ts,src/styles/breakpoints.scss.Byte-equal with Wallet:
src/apps/shared/AppShell/*except the two adapted files below.Adapted, with the exact transform pinned in
scripts/check-chrome-parity.mjs:src/components/PlaygroundIntro/PlaygroundIntro.tsxsrc/app/layout.tsxsrc/app/page.module.scsssrc/data/flowIcons.tsxLoad statementiconsrc/apps/shared/AppShell/AppShell.tsxsrc/apps/shared/AppShell/usePhoneFitScale.tssrc/components/ShareSheet/StageShareButton.*Deploy notes
Vercel project
grid-statements-demo, root directorycomponents/grid-statements-demo.vercel.jsonskips builds for commits that do not touch this directory. After merge, connect the project to GitHub with production branchmain.Held
A periodic statements guide page is not part of this PR. Only the playground ships.
Review guidance
Squash-merge. The branch has 59 commits of iterative polish; the final tree is what matters.
Verification at
194e8552npm test: 72 tests pass in 18 files.npm run check:chrome: passes.npm run buildandnpx tsc --noEmit: pass on Node 22.make lint: 0 errors.