Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1e92da7
adds LGIDs
TheSonOfThomp Nov 21, 2025
0d7650c
adds test utils
TheSonOfThomp Nov 21, 2025
35274d8
lint
TheSonOfThomp Nov 21, 2025
c77268a
fix bad merge
TheSonOfThomp Nov 24, 2025
649c699
removes Step test utils
TheSonOfThomp Nov 24, 2025
aa6aedb
add layout comments
TheSonOfThomp Nov 24, 2025
1fdc3e2
form-footer lgids
TheSonOfThomp Nov 24, 2025
265e1a1
updates wizard testids
TheSonOfThomp Nov 24, 2025
5701b76
Squashed commit of the following:
TheSonOfThomp Nov 25, 2025
e2de46c
Update WizardStep.spec.tsx
TheSonOfThomp Nov 25, 2025
7d00ad2
Update WizardContext.tsx
TheSonOfThomp Nov 25, 2025
95432ad
Merge branch 'LG-5562-wizard-updates' into LG-5566-wizard-test-utils-…
TheSonOfThomp Nov 25, 2025
b469712
Squashed commit of the following:
TheSonOfThomp Nov 25, 2025
1500f35
update provider props
TheSonOfThomp Nov 21, 2025
b28d919
add templates scope
TheSonOfThomp Nov 20, 2025
dbcc61c
init delete wizard
TheSonOfThomp Nov 19, 2025
e5a6461
initial port of delete wizard
TheSonOfThomp Nov 20, 2025
276dd6e
adds DeleteWizardStepContents
TheSonOfThomp Nov 20, 2025
415768d
Exports DeleteWizard . Header and Step wrappers
TheSonOfThomp Nov 21, 2025
e3602f9
Create delete-wizard-3.md
TheSonOfThomp Nov 21, 2025
3e091e2
updates delete-wizard changeset
TheSonOfThomp Nov 21, 2025
ade4f7d
rm temp changesets
TheSonOfThomp Nov 21, 2025
2c48871
build
TheSonOfThomp Nov 21, 2025
f6ae0a0
Update README.md
TheSonOfThomp Nov 21, 2025
e00696e
lint
TheSonOfThomp Nov 21, 2025
9817744
Adds onCancel/onDelete handlers
TheSonOfThomp Nov 24, 2025
25ff784
rm DeleteWizardStepContent
TheSonOfThomp Nov 24, 2025
cf2d402
fixes dependencies
TheSonOfThomp Nov 25, 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.
5 changes: 5 additions & 0 deletions .changeset/form-footer-lgids.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@leafygreen-ui/form-footer': patch
---

Passes `data-lgid` to the root `footer` element
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
1 change: 1 addition & 0 deletions packages/form-footer/src/FormFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default function FormFooter({
<LeafyGreenProvider darkMode={darkMode}>
<footer
data-testid={lgIds.root}
data-lgid={lgIds.root}
className={getFormFooterStyles({ theme, className })}
{...rest}
>
Expand Down
14 changes: 13 additions & 1 deletion packages/wizard/src/Wizard/Wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ import {
import { useControlled } from '@leafygreen-ui/hooks';

import { WizardSubComponentProperties } from '../constants';
import { getLgIds } from '../utils/getLgIds';
import { WizardProvider } from '../WizardContext/WizardContext';
import { WizardFooter } from '../WizardFooter';
import { WizardStep } from '../WizardStep';

import { WizardProps } from './Wizard.types';

export const Wizard = CompoundComponent(
({ activeStep: activeStepProp, onStepChange, children }: WizardProps) => {
({
activeStep: activeStepProp,
onStepChange,
children,
'data-lgid': dataLgId,
}: WizardProps) => {
const lgIds = getLgIds(dataLgId);
const stepChildren = findChildren(
children,
WizardSubComponentProperties.Step,
Expand Down Expand Up @@ -47,11 +54,16 @@ export const Wizard = CompoundComponent(
[setActiveStep, stepChildren.length],
);

/**
* NB: We're intentionally do _not_ wrap the `Wizard` (or `WizardStep`) component in a container element.
* This is done to ensure the Wizard is flexible, and can be rendered in any containing layout.
*/
return (
<WizardProvider
activeStep={activeStep}
updateStep={updateStep}
totalSteps={stepChildren.length}
lgIds={lgIds}
>
{stepChildren.map((child, i) => (i === activeStep ? child : null))}
</WizardProvider>
Expand Down
4 changes: 3 additions & 1 deletion packages/wizard/src/Wizard/Wizard.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ReactNode } from 'react';

export interface WizardProps {
import { LgIdProps } from '@leafygreen-ui/lib';

export interface WizardProps extends LgIdProps {
/**
* The current active step index (0-based).
*
Expand Down
12 changes: 11 additions & 1 deletion packages/wizard/src/WizardContext/WizardContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import React, { createContext, PropsWithChildren, useContext } from 'react';

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

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

export interface WizardContextData {
/**
* Defines whether the consuming component is within a Wizard context.
Expand Down Expand Up @@ -28,23 +32,28 @@ export interface WizardContextData {
* Internally sets the number of steps in the Wizard
*/
totalSteps: number;
lgIds: GetLgIdsReturnType;
}

export const WizardContext = createContext<WizardContextData>({
isWizardContext: false,
activeStep: 0,
totalSteps: 0,
updateStep: () => {},
lgIds: getLgIds(),
});

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

export const WizardProvider = ({
children,
activeStep,
updateStep,
totalSteps,
lgIds = getLgIds(),
}: WizardProviderProps) => {
return (
<WizardContext.Provider
Expand All @@ -53,6 +62,7 @@ export const WizardProvider = ({
activeStep,
updateStep,
totalSteps,
lgIds,
}}
>
{children}
Expand Down
11 changes: 9 additions & 2 deletions packages/wizard/src/WizardFooter/WizardFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export const WizardFooter = CompoundSubComponent(
className,
...rest
}: WizardFooterProps) => {
const { isWizardContext, activeStep, updateStep } = useWizardContext();
const { isWizardContext, activeStep, updateStep, lgIds } =
useWizardContext();
const { isAcknowledged, requiresAcknowledgement } = useWizardStepContext();
const isPrimaryButtonDisabled =
(requiresAcknowledgement && !isAcknowledged) ||
Expand Down Expand Up @@ -48,16 +49,22 @@ export const WizardFooter = CompoundSubComponent(
<FormFooter
{...rest}
className={className}
data-lgid={lgIds.footer}
backButtonProps={
activeStep > 0
? {
'data-lgid': lgIds.footerBackButton,
...backButtonProps,
onClick: handleBackButtonClick,
}
: undefined
}
cancelButtonProps={cancelButtonProps}
cancelButtonProps={{
'data-lgid': lgIds.footerCancelButton,
...cancelButtonProps,
}}
primaryButtonProps={{
'data-lgid': lgIds.footerPrimaryButton,
...primaryButtonProps,
disabled: isPrimaryButtonDisabled,
onClick: handlePrimaryButtonClick,
Expand Down
12 changes: 8 additions & 4 deletions packages/wizard/src/WizardStep/WizardStep.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import React from 'react';
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { getLgIds as getFooterLgIds } from '@leafygreen-ui/form-footer';

import { getTestUtils } from '../testing';
import { Wizard } from '../Wizard/Wizard';
import { useWizardContext } from '../WizardContext';
import { WizardFooter } from '../WizardFooter';

import { useWizardStepContext, WizardStep } from '.';

const wizardUtils = getTestUtils();

describe('packages/wizard-step', () => {
test('does not render outside WizardContext', () => {
const { container } = render(
Expand Down Expand Up @@ -132,9 +134,11 @@ describe('packages/wizard-step', () => {
<Wizard>
<Wizard.Step requiresAcknowledgement>
<TestComponent />
<WizardFooter primaryButtonProps={{ children: 'Continue' }} />
</Wizard.Step>
<Wizard.Step requiresAcknowledgement>
<TestComponent />
<WizardFooter primaryButtonProps={{ children: 'Finish' }} />
</Wizard.Step>
</Wizard>,
);
Expand All @@ -145,14 +149,14 @@ describe('packages/wizard-step', () => {
userEvent.click(getByRole('button', { name: 'Acknowledge' }));
expect(getByTestId('is-ack')).toHaveTextContent('true');

// TODO: replace with Wizard test harness
const primaryBtn = getByTestId(getFooterLgIds().primaryButton);
const primaryBtn = wizardUtils.getPrimaryButton();
userEvent.click(primaryBtn);

await waitFor(() => {
expect(getByTestId('step-1')).toBeInTheDocument();
});

// Step 2: Acknowledgement is reset
expect(getByTestId('is-ack')).toHaveTextContent('false');
userEvent.click(getByRole('button', { name: 'Acknowledge' }));
expect(getByTestId('is-ack')).toHaveTextContent('true');
Expand Down
4 changes: 4 additions & 0 deletions packages/wizard/src/WizardStep/WizardStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export const WizardStep = CompoundSubComponent(
WizardSubComponentProperties.Footer,
]);

/**
* NB: We're intentionally do _not_ wrap the WizardStep` component in a container element.
* This is done to ensure the Wizard is flexible, and can be rendered in any containing layout.
*/
return (
<WizardStepProvider
stepId={stepId}
Expand Down
1 change: 1 addition & 0 deletions packages/wizard/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { WizardSubComponentProperties } from './constants';
export { Wizard, type WizardProps } from './Wizard';
export {
useWizardContext,
Expand Down
Loading
Loading