Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f1da308
rm step wrapper
TheSonOfThomp Oct 22, 2025
1c67385
rm descendants dep
TheSonOfThomp Oct 22, 2025
79c4d84
export WizardProvider
TheSonOfThomp Oct 22, 2025
aab32ab
delete-wizard-demo
TheSonOfThomp Nov 4, 2025
b8758f4
Update pnpm
TheSonOfThomp Nov 7, 2025
7757e2a
fix wizard changes
TheSonOfThomp Nov 14, 2025
aef3a30
Adds `requiresAcknowledgement` prop to Wizard.Step
TheSonOfThomp Nov 21, 2025
76920c5
Implements `isAcknowledged` state inside provider
TheSonOfThomp Nov 21, 2025
674a76b
rm delete demo
TheSonOfThomp Nov 21, 2025
9c33bd0
rm temp changesets
TheSonOfThomp Nov 21, 2025
84b4317
footer tests
TheSonOfThomp Nov 21, 2025
d7c0135
revert toast changes?
TheSonOfThomp Nov 21, 2025
2879a58
Update .npmrc
TheSonOfThomp Nov 21, 2025
954b44e
adds `totalSteps` to wizard context
TheSonOfThomp Nov 24, 2025
59dc8f1
fix bad merge
TheSonOfThomp Nov 24, 2025
1a5f046
adds LGIDs
TheSonOfThomp Nov 21, 2025
b65d792
lint
TheSonOfThomp Nov 21, 2025
2100c5b
fix bad merge
TheSonOfThomp Nov 24, 2025
2ace0b5
removes Step test utils
TheSonOfThomp Nov 24, 2025
3ee6b63
add layout comments
TheSonOfThomp Nov 24, 2025
2be7f1d
Squashed commit of the following:
TheSonOfThomp Nov 25, 2025
4cbc815
update provider props
TheSonOfThomp Nov 21, 2025
b3de848
add templates scope
TheSonOfThomp Nov 20, 2025
d918251
init delete wizard
TheSonOfThomp Nov 19, 2025
2a5fb7f
initial port of delete wizard
TheSonOfThomp Nov 20, 2025
ce5d1f0
adds DeleteWizardStepContents
TheSonOfThomp Nov 20, 2025
ccb74db
Exports DeleteWizard . Header and Step wrappers
TheSonOfThomp Nov 21, 2025
43ff5eb
Create delete-wizard-3.md
TheSonOfThomp Nov 21, 2025
c6c09c6
updates delete-wizard changeset
TheSonOfThomp Nov 21, 2025
ed43e5b
rm temp changesets
TheSonOfThomp Nov 21, 2025
c7a1cfe
build
TheSonOfThomp Nov 21, 2025
2ebe1cb
Update README.md
TheSonOfThomp Nov 21, 2025
d4579b1
lint
TheSonOfThomp Nov 21, 2025
a6a7da9
Adds onCancel/onDelete handlers
TheSonOfThomp Nov 24, 2025
973a365
rm DeleteWizardStepContent
TheSonOfThomp Nov 24, 2025
3160611
fixes dependencies
TheSonOfThomp Nov 25, 2025
c9ba506
fixes dependencies
TheSonOfThomp Nov 26, 2025
9f3b30d
adds lgids and test utils
TheSonOfThomp Nov 26, 2025
51228b9
creates delete wizard tests
TheSonOfThomp Nov 26, 2025
5f74d60
add stories
TheSonOfThomp Nov 26, 2025
306b717
Squashed commit of the following:
TheSonOfThomp Nov 26, 2025
36ae864
reset versions
TheSonOfThomp Nov 26, 2025
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
64 changes: 64 additions & 0 deletions .changeset/delete-wizard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
'@lg-templates/delete-wizard': minor
---

Initial release of `DeleteWizard`.

```tsx
<DeleteWizard
onStepChange={...}
onCancel={...}
onDelete={...}
>
<DeleteWizard.Header
pageTitle="Demo Delete Wizard"
/>
<DeleteWizard.Step requiresAcknowledgement>
<DeleteWizard.StepContent>
<div>Step 1 contents<div>
</DeleteWizard.StepContent>
<DeleteWizard.Footer
backButtonText="Go back"
cancelButtonText="Cancel flow"
primaryButtonText='Continue to next step'
/>
</DeleteWizard.Step>

<DeleteWizard.Step requiresAcknowledgement>
<DeleteWizard.StepContent>
<div>Step 2 contents<div>
</DeleteWizard.StepContent>
<DeleteWizard.Footer
backButtonText="Go back"
cancelButtonText="Cancel flow"
primaryButtonText='Delete my thing'
/>
</DeleteWizard.Step>
</DeleteWizard>
```

### DeleteWizard
Establishes a context, and only renders the `activeStep` (managed internally, or provided with the `activeStep` prop). Accepts a `DeleteWizard.Header` and any number of `DeleteWizard.Step`s as children.

`DeleteWizard` and all sub-components include template styling.

### DeleteWizard.Header
A convenience wrapper around `CanvasHeader`

### DeleteWizard.Step
A convenience wrapper around `Wizard.Step` to ensure the correct context.
Like the basic `Wizard.Step`, of `requiresAcknowledgement` is true, the step must have `isAcknowledged` set in context, (or passed in as a controlled prop) for the Footer's primary button to be enabled. (see the Wizard and DeleteWizard demos in Storybook)


### DeleteWizard.StepContent
A styled `div` for use inside a `DeleteWizard.Step` to ensure proper page scrolling and footer positioning

### DeleteWizard.Footer
A wrapper around `Wizard.Footer` with embedded styles and convenience props for the DeleteWizard template.
`DeleteWizard.Footer` accepts optional `backButtonText`, `cancelButtonText` and `primaryButtonText` props for simpler wizard creation.
The primary button variant is defined based on the `activeStep`— `"danger"` for the final steps, and `"primary"` for all preceding steps.
Also defines the `leftGlyph` to <TrashIcon /> for the final step.

You can override this behavior by providing the button props object (see FormFooter).

Use the top level `onDelete`, `onCancel` and `onStepChange` callbacks to handle footer button clicks.
2 changes: 1 addition & 1 deletion .changeset/wizard.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

Initial Wizard package release.

See [README.md](./README.md) for usage guidelines
See [README.md](./README.md) for usage guidelines
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"link": "lg link",
"lint": "lg lint",
"prepublishOnly": "pnpm run build && pnpm build:ts-downlevel && pnpm build:docs",
"publish": "pnpm changeset publish --public",
"publish": "pnpm publish -r",
"reset:react17": "npx node ./scripts/react17/reset.mjs; pnpm run init",
"slackbot": "lg slackbot",
"start": "npx storybook dev -p 9001 --no-version-updates --no-open",
Expand Down Expand Up @@ -99,7 +99,8 @@
"@lg-charts": "charts",
"@lg-chat": "chat",
"@lg-tools": "tools",
"@lg-mcp-ui": "mcp-ui"
"@lg-mcp-ui": "mcp-ui",
"@lg-templates": "templates"
}
},
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion packages/wizard/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@leafygreen-ui/wizard",
"version": "0.1.0-local.1",
"version": "0.0.1",
"description": "LeafyGreen UI Kit Wizard",
"main": "./dist/umd/index.js",
"module": "./dist/esm/index.js",
Expand Down
8 changes: 6 additions & 2 deletions packages/wizard/src/WizardContext/WizardContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { createContext, PropsWithChildren, useContext } from 'react';

import { Optional } from '@leafygreen-ui/lib';

import type { GetLgIdsReturnType } from '../utils/getLgIds';
import { getLgIds } from '../utils/getLgIds';
import { GetLgIdsReturnType } from '../utils/getLgIds';

export interface WizardContextData {
/**
Expand Down Expand Up @@ -43,7 +45,9 @@ export const WizardContext = createContext<WizardContextData>({
});

interface WizardProviderProps
extends PropsWithChildren<Omit<WizardContextData, 'isWizardContext'>> {}
extends PropsWithChildren<
Omit<Optional<WizardContextData, 'lgIds'>, 'isWizardContext'>
> {}

export const WizardProvider = ({
children,
Expand Down
212 changes: 212 additions & 0 deletions packages/wizard/src/WizardFooter/WizardFooter.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,216 @@
expect(getByTestId('step-1')).toBeInTheDocument();
});
});

describe('primary button behavior', () => {

Check failure on line 260 in packages/wizard/src/WizardFooter/WizardFooter.spec.tsx

View workflow job for this annotation

GitHub Actions / Check lints

Describe block title is used multiple times in the same describe block
test('primary button is enabled by default', () => {
const { getByRole } = render(
<Wizard>
<Wizard.Step>
<Wizard.Footer primaryButtonProps={{ children: 'Continue' }} />
</Wizard.Step>
</Wizard>,
);

const primaryButton = getByRole('button', { name: 'Continue' });
expect(primaryButton).toBeEnabled();
});

test('primary button advances to next step when clicked', async () => {
const onStepChange = jest.fn();

const { getByRole, getByTestId } = render(
<Wizard onStepChange={onStepChange}>
<Wizard.Step>
<div data-testid="step-1">Step 1</div>
<Wizard.Footer primaryButtonProps={{ children: 'Next' }} />
</Wizard.Step>
<Wizard.Step>
<div data-testid="step-2">Step 2</div>
<Wizard.Footer primaryButtonProps={{ children: 'Finish' }} />
</Wizard.Step>
</Wizard>,
);

expect(getByTestId('step-1')).toBeInTheDocument();

const nextButton = getByRole('button', { name: 'Next' });
await userEvent.click(nextButton);

expect(onStepChange).toHaveBeenCalledWith(1);
expect(getByTestId('step-2')).toBeInTheDocument();
});

describe('requiresAcknowledgement', () => {
test('primary button is disabled when step requires acknowledgement and is not acknowledged', () => {
const { getByRole } = render(
<Wizard>
<Wizard.Step requiresAcknowledgement>
<div>Step content</div>
<Wizard.Footer primaryButtonProps={{ children: 'Continue' }} />
</Wizard.Step>
</Wizard>,
);

const primaryButton = getByRole('button', { name: 'Continue' });
expect(primaryButton).toHaveAttribute('aria-disabled', 'true');
});

test('primary button is enabled when step requires acknowledgement and is acknowledged', async () => {
const TestComponent = () => {
const { setAcknowledged } = useWizardStepContext();
return (
<>
<div>Step content</div>
<button onClick={() => setAcknowledged(true)}>Acknowledge</button>
<Wizard.Footer primaryButtonProps={{ children: 'Continue' }} />
</>
);
};

const { getByRole } = render(
<Wizard>
<Wizard.Step requiresAcknowledgement>
<TestComponent />
</Wizard.Step>
</Wizard>,
);

const primaryButton = getByRole('button', { name: 'Continue' });
expect(primaryButton).toHaveAttribute('aria-disabled', 'true');

await userEvent.click(getByRole('button', { name: 'Acknowledge' }));

expect(primaryButton).toHaveAttribute('aria-disabled', 'false');
});

test('primary button is enabled when step does not require acknowledgement', () => {
const { getByRole } = render(
<Wizard>
<Wizard.Step>
<div>Step content</div>
<Wizard.Footer primaryButtonProps={{ children: 'Continue' }} />
</Wizard.Step>
</Wizard>,
);

const primaryButton = getByRole('button', { name: 'Continue' });
expect(primaryButton).toHaveAttribute('aria-disabled', 'false');
});

test('primary button can advance step after acknowledgement', async () => {
const TestComponent = () => {
const { setAcknowledged } = useWizardStepContext();
return (
<>
<div>Step content</div>
<button onClick={() => setAcknowledged(true)}>Acknowledge</button>
<Wizard.Footer primaryButtonProps={{ children: 'Continue' }} />
</>
);
};

const { getByRole, getByTestId } = render(
<Wizard>
<Wizard.Step requiresAcknowledgement>
<div data-testid="step-1">Step 1</div>
<TestComponent />
</Wizard.Step>
<Wizard.Step>
<div data-testid="step-2">Step 2</div>
</Wizard.Step>
</Wizard>,
);

expect(getByTestId('step-1')).toBeInTheDocument();

const primaryButton = getByRole('button', { name: 'Continue' });
expect(primaryButton).toHaveAttribute('aria-disabled', 'true');

// Acknowledge the step
await userEvent.click(getByRole('button', { name: 'Acknowledge' }));
expect(primaryButton).toHaveAttribute('aria-disabled', 'false');

// Advance to next step
await userEvent.click(primaryButton);
expect(getByTestId('step-2')).toBeInTheDocument();
});
});
});

describe('back button', () => {

Check failure on line 396 in packages/wizard/src/WizardFooter/WizardFooter.spec.tsx

View workflow job for this annotation

GitHub Actions / Check lints

Describe block title is used multiple times in the same describe block
test('back button is not rendered on first step', () => {
const { queryByRole } = render(
<Wizard>
<Wizard.Step>
<Wizard.Footer
primaryButtonProps={{ children: 'Next' }}
backButtonProps={{ children: 'Back' }}
/>
</Wizard.Step>
</Wizard>,
);

expect(queryByRole('button', { name: 'Back' })).not.toBeInTheDocument();
});

test('back button is rendered on subsequent steps', async () => {
const { getByRole } = render(
<Wizard>
<Wizard.Step>
<div data-testid="step-1">Step 1</div>
<Wizard.Footer
primaryButtonProps={{ children: 'Next' }}
backButtonProps={{ children: 'Back' }}
/>
</Wizard.Step>
<Wizard.Step>
<div data-testid="step-2">Step 2</div>
<Wizard.Footer
primaryButtonProps={{ children: 'Finish' }}
backButtonProps={{ children: 'Back' }}
/>
</Wizard.Step>
</Wizard>,
);

// Move to step 2
await userEvent.click(getByRole('button', { name: 'Next' }));

// Back button should now be visible
expect(getByRole('button', { name: 'Back' })).toBeInTheDocument();
});

test('back button navigates to previous step', async () => {
const onStepChange = jest.fn();

const { getByRole, getByTestId } = render(
<Wizard onStepChange={onStepChange}>
<Wizard.Step>
<div data-testid="step-1">Step 1</div>
<Wizard.Footer
primaryButtonProps={{ children: 'Next' }}
backButtonProps={{ children: 'Back' }}
/>
</Wizard.Step>
<Wizard.Step>
<div data-testid="step-2">Step 2</div>
<Wizard.Footer
primaryButtonProps={{ children: 'Finish' }}
backButtonProps={{ children: 'Back' }}
/>
</Wizard.Step>
</Wizard>,
);

// Move to step 2
await userEvent.click(getByRole('button', { name: 'Next' }));
expect(getByTestId('step-2')).toBeInTheDocument();

// Go back to step 1
await userEvent.click(getByRole('button', { name: 'Back' }));
expect(onStepChange).toHaveBeenCalledWith(0);
expect(getByTestId('step-1')).toBeInTheDocument();
});
});
});
2 changes: 2 additions & 0 deletions packages/wizard/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { WizardSubComponentProperties } from './constants';
export { getLgIds } from './utils/getLgIds';
export { Wizard, type WizardProps } from './Wizard';
export {
useWizardContext,
Expand Down
1 change: 1 addition & 0 deletions packages/wizard/src/testing/getTestUtils.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { findByLgId, getByLgId, queryByLgId } from '@lg-tools/test-harnesses';
import { screen } from '@testing-library/react';

Check warning on line 2 in packages/wizard/src/testing/getTestUtils.tsx

View workflow job for this annotation

GitHub Actions / Check lints

'screen' is defined but never used. Allowed unused vars must match /^_/u

import { getTestUtils as getButtonUtils } from '@leafygreen-ui/button/testing';
import { LgIdString } from '@leafygreen-ui/lib';
Expand Down
37 changes: 37 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ packages:
- 'mcp-ui/*'
- 'packages/*'
- 'tools/*'
- 'templates/*'
Loading
Loading