Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .reports/embedded-react-sdk.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4239,6 +4239,8 @@ declare namespace Payroll {
PayrollBlockerListProps,
RecoveryCases,
RecoveryCasesProps,
PrintChecks,
PrintChecksProps,
OffCyclePayPeriodDateFormData,
OffCyclePayrollDateType,
OffCycleCreation,
Expand Down Expand Up @@ -4590,6 +4592,16 @@ export type PreparerSelectFieldProps = HookFieldProps<SelectHookFieldProps<SignE
// @public
export type PreparerTextFieldProps = HookFieldProps<TextInputHookFieldProps<SignEmployeeFormRequiredValidation>>;

// @public
function PrintChecks(input: PrintChecksProps): JSX;

// @public
interface PrintChecksProps extends Omit<BaseComponentInterface<never>, 'onEvent'> {
companyId: string;
onEvent?: BaseComponentInterface['onEvent'];
payrollId: string;
}

// @public
function Profile(input: ProfileProps): JSX;

Expand Down
26 changes: 26 additions & 0 deletions docs/guides/endpoint-inventory.json
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,32 @@
"recoveryCaseUuid"
]
},
"Payroll.PrintChecks": {
"endpoints": [
{
"method": "GET",
"path": "/v1/companies/:companyId/payrolls/:payrollId",
"docsUrl": "https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/get-v1-companies-company_id-payrolls-payroll_id"
},
{
"method": "GET",
"path": "/v1/generated_documents/:documentType/:requestUuid",
"docsUrl": "https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/get-v1-generated_documents-document_type-request_uuid"
},
{
"method": "POST",
"path": "/v1/payrolls/:payrollUuid/generated_documents/printable_payroll_checks",
"docsUrl": "https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/post-v1-payrolls-payroll_uuid-generated_documents-printable_payroll_checks"
}
],
"variables": [
"companyId",
"documentType",
"payrollId",
"payrollUuid",
"requestUuid"
]
},
"Payroll.OffCycleCreation": {
"endpoints": [
{
Expand Down
3 changes: 3 additions & 0 deletions docs/guides/endpoint-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,9 @@ import inventory from '@gusto/embedded-react-sdk/endpoint-inventory.json'
| | GET | [`/v1/companies/:companyUuid/recovery_cases`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/get-recovery-cases) |
| **Payroll.RecoveryCases** | GET | [`/v1/companies/:companyUuid/recovery_cases`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/get-recovery-cases) |
| | PUT | [`/v1/recovery_cases/:recoveryCaseUuid/redebit`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/redebit-recovery-case) |
| **Payroll.PrintChecks** | GET | [`/v1/companies/:companyId/payrolls/:payrollId`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/get-v1-companies-company_id-payrolls-payroll_id) |
| | GET | [`/v1/generated_documents/:documentType/:requestUuid`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/get-v1-generated_documents-document_type-request_uuid) |
| | POST | [`/v1/payrolls/:payrollUuid/generated_documents/printable_payroll_checks`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/post-v1-payrolls-payroll_uuid-generated_documents-printable_payroll_checks) |
| **Payroll.OffCycleCreation** | GET | [`/v1/companies/:companyId/employees`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/get-v1-companies-company_id-employees) |
| | POST | [`/v1/companies/:companyId/payrolls`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/post-v1-companies-company_id-payrolls) |
| | GET | [`/v1/companies/:companyUuid/payment_configs`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/get-v1-company-payment-configs) |
Expand Down
1 change: 1 addition & 0 deletions docs/reference/blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ Individual form and UI components with SDK logic built in — use these for cust
| [Payroll.PayrollList](payroll/blocks#payrolllist) | Lists upcoming payrolls and lets users start running them. |
| [Payroll.PayrollOverview](payroll/blocks#payrolloverview) | Final review screen for a calculated payroll before submission, with submit, cancel, and edit controls. After submission, tracks processing status and surfaces the receipt and per-employee paystub downloads once complete. |
| [Payroll.PayrollReceipts](payroll/blocks#payrollreceipts) | Displays a detailed receipt for a completed payroll, including the debited total, per-category breakdown, tax breakdown, and a per-employee summary of payment method, garnishments, reimbursements, taxes, and net pay. |
| [Payroll.PrintChecks](payroll/blocks#printchecks) | Displays a banner prompting the user to print checks for employees paid by check on a processed payroll, and walks them through choosing check stock and generating the check PDF. |
| [Payroll.RecoveryCases](payroll/blocks#recoverycases) | Displays open recovery cases for a company and provides an in-modal resubmit workflow for resolving them. |
| [Payroll.TransitionCreation](payroll/blocks#transitioncreation) | Creation form for transition payrolls covering the gap between an old and new pay schedule. |
| [TimeOff.AddEmployeesHoliday](time-off/blocks#addemployeesholiday) | Employee selection screen for assigning employees to a company's holiday pay policy. |
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ custom_edit_url: null

## Browse by domain

<DocCardList items={[{"type":"link","href":"company","label":"Companies","description":"Enroll a business with Gusto — company setup, pay schedules, and required form signing — and respond to ongoing compliance information requests.","customProps":{"footer":"📁 CompanyOnboarding : 🚂 1 workflow, 🧩 18 blocks\n📁 InformationRequests : 🚂 1 workflow, 🧩 2 blocks\n✍️ 2 form hooks"}},{"type":"link","href":"employee","label":"Employees","description":"Onboard and manage W-2 workers (hourly, salary, commissioned, and more) — compensation, banking, tax withholding, documents, and terminations.","customProps":{"footer":"📁 EmployeeOnboarding : 🚂 3 workflows, 🧩 17 blocks\n📁 EmployeeManagement : 🚂 3 workflows, 🧩 35 blocks\n✍️ 15 form hooks\n🌐 1 data hook"}},{"type":"link","href":"contractor","label":"Contractors","description":"Onboard and manage 1099 contractors (individual or business) — profile, payment methods, documents, and contractor payments.","customProps":{"footer":"📁 ContractorOnboarding : 🚂 2 workflows, 🧩 11 blocks\n📁 ContractorManagement : 🚂 2 workflows, 🧩 20 blocks\n✍️ 6 form hooks\n🌐 1 data hook"}},{"type":"link","href":"payroll","label":"Payrolls","description":"Pay employees and contractors across all pay schedules, including off-cycle, dismissal, and transition payrolls.","customProps":{"footer":"📁 Payroll : 🚂 5 workflows, 🧩 15 blocks"}},{"type":"link","href":"time-off","label":"Time off policies","description":"Create and manage policies for vacation, sick leave, and company holidays.","customProps":{"footer":"📁 TimeOff : 🚂 1 workflow, 🧩 13 blocks"}}]} />
<DocCardList items={[{"type":"link","href":"company","label":"Companies","description":"Enroll a business with Gusto — company setup, pay schedules, and required form signing — and respond to ongoing compliance information requests.","customProps":{"footer":"📁 CompanyOnboarding : 🚂 1 workflow, 🧩 18 blocks\n📁 InformationRequests : 🚂 1 workflow, 🧩 2 blocks\n✍️ 2 form hooks"}},{"type":"link","href":"employee","label":"Employees","description":"Onboard and manage W-2 workers (hourly, salary, commissioned, and more) — compensation, banking, tax withholding, documents, and terminations.","customProps":{"footer":"📁 EmployeeOnboarding : 🚂 3 workflows, 🧩 17 blocks\n📁 EmployeeManagement : 🚂 3 workflows, 🧩 35 blocks\n✍️ 15 form hooks\n🌐 1 data hook"}},{"type":"link","href":"contractor","label":"Contractors","description":"Onboard and manage 1099 contractors (individual or business) — profile, payment methods, documents, and contractor payments.","customProps":{"footer":"📁 ContractorOnboarding : 🚂 2 workflows, 🧩 11 blocks\n📁 ContractorManagement : 🚂 2 workflows, 🧩 20 blocks\n✍️ 6 form hooks\n🌐 1 data hook"}},{"type":"link","href":"payroll","label":"Payrolls","description":"Pay employees and contractors across all pay schedules, including off-cycle, dismissal, and transition payrolls.","customProps":{"footer":"📁 Payroll : 🚂 5 workflows, 🧩 16 blocks"}},{"type":"link","href":"time-off","label":"Time off policies","description":"Create and manage policies for vacation, sick leave, and company holidays.","customProps":{"footer":"📁 TimeOff : 🚂 1 workflow, 🧩 13 blocks"}}]} />

---

Expand Down
49 changes: 49 additions & 0 deletions docs/reference/payroll/blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,55 @@ _Inherits `children`, `className`, `defaultValues`, `FallbackComponent`, `Loader

***

<a id="printchecks"></a>

## PrintChecks

Displays a banner prompting the user to print checks for employees paid by check on a
processed payroll, and walks them through choosing check stock and generating the check PDF.

<br />

### PrintChecksProps

<a id="printchecksprops"></a>

Props for [PrintChecks](#printchecks).

| Property | Type | Description |
| ------ | ------ | ------ |
| `companyId` | `string` | Identifier of the company that owns the payroll. |
| `payrollId` | `string` | Identifier of the payroll to generate printable checks for. |
| `onEvent?` | [`OnEventType`](../events.md#oneventtype)\<[`EventType`](../events.md#eventtype), `unknown`\> | Callback invoked each time the component emits an event. |

_Inherits `children`, `className`, `defaultValues`, `dictionary`, `FallbackComponent`, `LoaderComponent` from Omit._

<br />

### Events

| Event | Description | Data |
| ----- | ----------- | ---- |
| `payroll/printChecks/start` | User opened the print-checks modal from the banner | — |
| `payroll/printChecks/generate/start` | User submitted the print-checks form | — |
| `payroll/printChecks/generate/succeeded` | Printable checks finished generating | `{ documentUrl }` |
| `payroll/printChecks/generate/failed` | The print-checks request was rejected or generation failed | `{ errorMessage }` |
| `payroll/printChecks/retry` | User retried after a failed generation | — |
| `payroll/printChecks/cancel` | User cancelled the print-checks form | — |
| `payroll/printChecks/close` | User closed the failure or summary screen | — |

<br />

### Endpoints

| Method | Path |
| --- | --- |
| GET | [`/v1/companies/:companyId/payrolls/:payrollId`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/get-v1-companies-company_id-payrolls-payroll_id) |
| GET | [`/v1/generated_documents/:documentType/:requestUuid`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/get-v1-generated_documents-document_type-request_uuid) |
| POST | [`/v1/payrolls/:payrollUuid/generated_documents/printable_payroll_checks`](https://docs.gusto.com/embedded-payroll/v2026-06-15/reference/post-v1-payrolls-payroll_uuid-generated_documents-printable_payroll_checks) |

***

<a id="recoverycases"></a>

## RecoveryCases
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/payroll/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ Flows and blocks for running and managing payroll across a company's pay schedul
import { Payroll } from '@gusto/embedded-react-sdk'
```

<DocCardList items={[{"type":"link","href":"dismissal-flow","label":"🚂 DismissalFlow","description":"Guided flow to run a dismissed employee's final payroll."},{"type":"link","href":"off-cycle-flow","label":"🚂 OffCycleFlow","description":"Guided flow to create and run a bonus or correction payroll."},{"type":"link","href":"payroll-execution-flow","label":"🚂 PayrollExecutionFlow","description":"Guided flow to configure, review, and submit a single payroll."},{"type":"link","href":"payroll-flow","label":"🚂 PayrollFlow","description":"Hub for running and managing all payrolls across a company's pay schedules."},{"type":"link","href":"transition-flow","label":"🚂 TransitionFlow","description":"Guided flow to run a transition payroll when employees move from one pay schedule to another."},{"type":"link","href":"blocks","label":"🧩 15 blocks"}]} />
<DocCardList items={[{"type":"link","href":"dismissal-flow","label":"🚂 DismissalFlow","description":"Guided flow to run a dismissed employee's final payroll."},{"type":"link","href":"off-cycle-flow","label":"🚂 OffCycleFlow","description":"Guided flow to create and run a bonus or correction payroll."},{"type":"link","href":"payroll-execution-flow","label":"🚂 PayrollExecutionFlow","description":"Guided flow to configure, review, and submit a single payroll."},{"type":"link","href":"payroll-flow","label":"🚂 PayrollFlow","description":"Hub for running and managing all payrolls across a company's pay schedules."},{"type":"link","href":"transition-flow","label":"🚂 TransitionFlow","description":"Guided flow to run a transition payroll when employees move from one pay schedule to another."},{"type":"link","href":"blocks","label":"🧩 16 blocks"}]} />
1 change: 1 addition & 0 deletions docs/reference/payroll/namespace.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ import { Payroll } from '@gusto/embedded-react-sdk'
| [PayrollList](blocks.md#payrolllist) | Lists upcoming payrolls and lets users start running them. |
| [PayrollOverview](blocks.md#payrolloverview) | Final review screen for a calculated payroll before submission, with submit, cancel, and edit controls. After submission, tracks processing status and surfaces the receipt and per-employee paystub downloads once complete. |
| [PayrollReceipts](blocks.md#payrollreceipts) | Displays a detailed receipt for a completed payroll, including the debited total, per-category breakdown, tax breakdown, and a per-employee summary of payment method, garnishments, reimbursements, taxes, and net pay. |
| [PrintChecks](blocks.md#printchecks) | Displays a banner prompting the user to print checks for employees paid by check on a processed payroll, and walks them through choosing check stock and generating the check PDF. |
| [RecoveryCases](blocks.md#recoverycases) | Displays open recovery cases for a company and provides an in-modal resubmit workflow for resolving them. |
| [TransitionCreation](blocks.md#transitioncreation) | Creation form for transition payrolls covering the gap between an old and new pay schedule. |
1 change: 1 addition & 0 deletions sdk-app/src/generated-registry-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export const ENTITY_REQUIREMENTS: Record<string, string[]> = {
'Payroll.PayrollList': ['companyId'],
'Payroll.PayrollOverview': ['companyId', 'payrollId'],
'Payroll.PayrollReceipts': ['payrollId'],
'Payroll.PrintChecks': ['companyId', 'payrollId'],
'Payroll.RecoveryCases': ['companyId'],
'Payroll.TransitionCreation': ['companyId'],
'Payroll.TransitionFlow': ['companyId'],
Expand Down
123 changes: 123 additions & 0 deletions src/components/Payroll/PrintChecks/PrintChecks.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { HttpResponse } from 'msw'
import { PrintChecks } from './PrintChecks'
import {
handlePayrollsGeneratePrintableChecks,
handleGeneratedDocumentsGet,
createPayrollCheck,
createGeneratedDocument,
} from '@/test/mocks/apis/printable_payroll_checks'
import { server } from '@/test/mocks/server'
import { renderWithProviders } from '@/test-utils/renderWithProviders'
import { printChecksEvents } from '@/shared/constants'

const checkCompensation = {
employeeUuid: 'emp-check-1',
excluded: false,
paymentMethod: 'Check',
}

vi.mock('@gusto/embedded-api/react-query/payrollsGet', () => ({
usePayrollsGet: () => ({
data: {
payrollShow: {
processed: true,
employeeCompensations: [checkCompensation],
},
},
}),
}))

describe('PrintChecks', () => {
const onEvent = vi.fn()
const user = userEvent.setup()

const defaultProps = {
companyId: 'company-1',
payrollId: 'payroll-1',
onEvent,
}

beforeEach(() => {
vi.clearAllMocks()
// The download is triggered via a synthetic anchor click rather than a real navigation —
// jsdom throws "Not implemented: navigation" if this isn't stubbed.
vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => {})
})

it('opens the form from the banner CTA and cancels back to the banner', async () => {
renderWithProviders(<PrintChecks {...defaultProps} />)

await user.click(await screen.findByRole('button', { name: 'View and print checks' }))

expect(await screen.findByText('Choose check stock')).toBeInTheDocument()
expect(onEvent).toHaveBeenCalledWith(printChecksEvents.PRINT_CHECKS_START, undefined)

await user.click(screen.getByRole('button', { name: 'Cancel' }))

await waitFor(() => {
expect(screen.queryByText('Choose check stock')).toBeNull()
})
})

it('walks through a successful generate flow to the summary screen and back', async () => {
server.use(
handlePayrollsGeneratePrintableChecks(() =>
HttpResponse.json(createPayrollCheck(), { status: 200 }),
),
)
server.use(
handleGeneratedDocumentsGet(() =>
HttpResponse.json(
createGeneratedDocument({
status: 'succeeded',
document_urls: ['https://example.com/checks.pdf'],
}),
),
),
)

renderWithProviders(<PrintChecks {...defaultProps} />)

await user.click(await screen.findByRole('button', { name: 'View and print checks' }))
await user.click(screen.getByRole('button', { name: 'View checks' }))

expect(await screen.findByText('Your checks are ready')).toBeInTheDocument()
expect(screen.getByRole('link', { name: 'View checks' })).toHaveAttribute(
'href',
'https://example.com/checks.pdf',
)

await user.click(screen.getByRole('button', { name: 'Close' }))

await waitFor(() => {
expect(screen.queryByText('Your checks are ready')).toBeNull()
})
})

it('walks through a failed generate flow and allows retrying', async () => {
server.use(
handlePayrollsGeneratePrintableChecks(() =>
HttpResponse.json(createPayrollCheck(), { status: 200 }),
),
)
server.use(
handleGeneratedDocumentsGet(() =>
HttpResponse.json(createGeneratedDocument({ status: 'failed', document_urls: [] })),
),
)

renderWithProviders(<PrintChecks {...defaultProps} />)

await user.click(await screen.findByRole('button', { name: 'View and print checks' }))
await user.click(screen.getByRole('button', { name: 'View checks' }))

expect(await screen.findByText("We couldn't generate your checks")).toBeInTheDocument()

await user.click(screen.getByRole('button', { name: 'Try again' }))

expect(await screen.findByText('Choose check stock')).toBeInTheDocument()
})
})
Loading