Skip to content

Commit b548c27

Browse files
committed
Adds onCancel/onDelete handlers
1 parent b2e919b commit b548c27

File tree

7 files changed

+204
-58
lines changed

7 files changed

+204
-58
lines changed

.changeset/delete-wizard.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
Initial release of `DeleteWizard`.
66

77
```tsx
8-
<DeleteWizard>
8+
<DeleteWizard
9+
onStepChange={...}
10+
onCancel={...}
11+
onDelete={...}
12+
>
913
<DeleteWizard.Header
1014
pageTitle="Demo Delete Wizard"
1115
/>
@@ -14,9 +18,9 @@ Initial release of `DeleteWizard`.
1418
<div>Step 1 contents<div>
1519
</DeleteWizard.StepContent>
1620
<DeleteWizard.Footer
17-
primaryButtonProps={{
18-
children: 'Continue to next step',
19-
}}
21+
backButtonText="Go back"
22+
cancelButtonText="Cancel flow"
23+
primaryButtonText='Continue to next step'
2024
/>
2125
</DeleteWizard.Step>
2226

@@ -25,12 +29,9 @@ Initial release of `DeleteWizard`.
2529
<div>Step 2 contents<div>
2630
</DeleteWizard.StepContent>
2731
<DeleteWizard.Footer
28-
primaryButtonProps={{
29-
leftGlyph: <TrashIcon />,
30-
variant: 'danger',
31-
children: 'Delete my thing',
32-
onClick: handleDelete,
33-
}}
32+
backButtonText="Go back"
33+
cancelButtonText="Cancel flow"
34+
primaryButtonText='Delete my thing'
3435
/>
3536
</DeleteWizard.Step>
3637
</DeleteWizard>
@@ -41,7 +42,6 @@ Establishes a context, and only renders the `activeStep` (managed internally, or
4142

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

44-
4545
### DeleteWizard.Header
4646
A convenience wrapper around `CanvasHeader`
4747

@@ -54,4 +54,11 @@ Like the basic `Wizard.Step`, of `requiresAcknowledgement` is true, the step mu
5454
A styled `div` for use inside a `DeleteWizard.Step` to ensure proper page scrolling and footer positioning
5555

5656
### DeleteWizard.Footer
57-
A wrapper around Wizard.Footer with embedded styles for the DeleteWizard template
57+
A wrapper around `Wizard.Footer` with embedded styles and convenience props for the DeleteWizard template.
58+
`DeleteWizard.Footer` accepts optional `backButtonText`, `cancelButtonText` and `primaryButtonText` props for simpler wizard creation.
59+
The primary button variant is defined based on the `activeStep`— `"danger"` for the final steps, and `"primary"` for all preceding steps.
60+
Also defines the `leftGlyph` to <TrashIcon /> for the final step.
61+
62+
You can override this behavior by providing the button props object (see FormFooter).
63+
64+
Use the top level `onDelete`, `onCancel` and `onStepChange` callbacks to handle footer button clicks.

templates/delete-wizard/src/DeleteWizard.stories.tsx

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { StoryObj } from '@storybook/react';
55

66
import { css } from '@leafygreen-ui/emotion';
77
import BeakerIcon from '@leafygreen-ui/icon/Beaker';
8-
import TrashIcon from '@leafygreen-ui/icon/Trash';
98
import { BackLink, Body } from '@leafygreen-ui/typography';
109

1110
import { ExampleStepContent } from './testUtils/ExampleStepContent';
@@ -50,6 +49,10 @@ export const LiveExample: StoryObj<typeof DeleteWizard> = {
5049
window.location.reload();
5150
};
5251

52+
const handleStepChange = step => {
53+
console.log('[STORYBOOK] step changed to ', step);
54+
};
55+
5356
return (
5457
<div
5558
className={css`
@@ -58,6 +61,9 @@ export const LiveExample: StoryObj<typeof DeleteWizard> = {
5861
>
5962
<DeleteWizard
6063
activeStep={args.activeStep}
64+
onStepChange={handleStepChange}
65+
onCancel={handleCancel}
66+
onDelete={handleDelete}
6167
className={css`
6268
height: 100vh;
6369
width: 100vw;
@@ -83,13 +89,8 @@ export const LiveExample: StoryObj<typeof DeleteWizard> = {
8389
/>
8490
</DeleteWizard.StepContent>
8591
<DeleteWizard.Footer
86-
cancelButtonProps={{
87-
children: 'Cancel wizard',
88-
onClick: handleCancel,
89-
}}
90-
primaryButtonProps={{
91-
children: 'Continue to next step',
92-
}}
92+
cancelButtonText="Cancel wizard"
93+
primaryButtonText="Continue to next step"
9394
/>
9495
</DeleteWizard.Step>
9596

@@ -104,16 +105,9 @@ export const LiveExample: StoryObj<typeof DeleteWizard> = {
104105
/>
105106
</DeleteWizard.StepContent>
106107
<DeleteWizard.Footer
107-
cancelButtonProps={{
108-
children: 'Cancel wizard',
109-
onClick: handleCancel,
110-
}}
111-
primaryButtonProps={{
112-
leftGlyph: <TrashIcon />,
113-
variant: 'danger',
114-
children: 'Delete my thing',
115-
onClick: handleDelete,
116-
}}
108+
backButtonText="Go back"
109+
cancelButtonText="Cancel wizard"
110+
primaryButtonText="Delete the prop"
117111
/>
118112
</DeleteWizard.Step>
119113
</DeleteWizard>

templates/delete-wizard/src/DeleteWizard/DeleteWizard.tsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,40 @@ import {
55
findChild,
66
} from '@leafygreen-ui/compound-component';
77
import { cx } from '@leafygreen-ui/emotion';
8-
import { useWizardContext, Wizard } from '@leafygreen-ui/wizard';
8+
import { Wizard } from '@leafygreen-ui/wizard';
99

1010
import { DeleteWizardSubComponentKeys } from './compoundComponentProperties';
1111
import { wizardWrapperStyles } from './DeleteWizard.styles';
1212
import { DeleteWizardProps } from './DeleteWizard.types';
13+
import { DeleteWizardContextProvider } from './DeleteWizardContext';
1314
import { DeleteWizardFooter } from './DeleteWizardFooter';
1415
import { DeleteWizardHeader } from './DeleteWizardHeader';
1516
import { DeleteWizardStep } from './DeleteWizardStep';
1617
import { DeleteWizardStepContent } from './DeleteWizardStepContents';
1718

18-
/**
19-
* A re-export of `useWizardContext` specifically for this DeleteWizard
20-
*/
21-
export const useDeleteWizardContext = useWizardContext;
22-
2319
/**
2420
* The parent DeleteWizard component.
2521
* Pass a `DeleteWizard.Header` and any number of `DeleteWizard.Step`s as children
2622
*/
2723
export const DeleteWizard = CompoundComponent(
28-
({ activeStep, onStepChange, children, className }: DeleteWizardProps) => {
24+
({
25+
activeStep,
26+
children,
27+
className,
28+
onCancel,
29+
onDelete,
30+
onStepChange,
31+
}: DeleteWizardProps) => {
2932
const header = findChild(children, DeleteWizardSubComponentKeys.Header);
3033

3134
return (
3235
<div className={cx(wizardWrapperStyles, className)}>
3336
{header}
34-
<Wizard activeStep={activeStep} onStepChange={onStepChange}>
35-
{children}
36-
</Wizard>
37+
<DeleteWizardContextProvider onCancel={onCancel} onDelete={onDelete}>
38+
<Wizard activeStep={activeStep} onStepChange={onStepChange}>
39+
{children}
40+
</Wizard>
41+
</DeleteWizardContextProvider>
3742
</div>
3843
);
3944
},
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import { ComponentProps } from 'react';
1+
import { ComponentProps, MouseEventHandler } from 'react';
22

33
import { WizardProps } from '@leafygreen-ui/wizard';
44

55
export interface DeleteWizardProps
66
extends WizardProps,
7-
Omit<ComponentProps<'div'>, 'children'> {}
7+
Omit<ComponentProps<'div'>, 'children'> {
8+
onCancel?: MouseEventHandler<HTMLButtonElement>;
9+
onDelete?: MouseEventHandler<HTMLButtonElement>;
10+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React, { createContext, PropsWithChildren, useContext } from 'react';
2+
3+
import { useWizardContext, WizardContextData } from '@leafygreen-ui/wizard';
4+
5+
import { DeleteWizardProps } from './DeleteWizard.types';
6+
7+
export interface DeleteWizardContextData {
8+
onCancel?: DeleteWizardProps['onCancel'];
9+
onDelete?: DeleteWizardProps['onDelete'];
10+
}
11+
12+
export const DeleteWizardContext = createContext<DeleteWizardContextData>({});
13+
14+
export const DeleteWizardContextProvider = ({
15+
children,
16+
onCancel,
17+
onDelete,
18+
}: PropsWithChildren<DeleteWizardContextData>) => {
19+
return (
20+
<DeleteWizardContext.Provider
21+
value={{
22+
onCancel,
23+
onDelete,
24+
}}
25+
>
26+
{children}
27+
</DeleteWizardContext.Provider>
28+
);
29+
};
30+
31+
/**
32+
* A re-export of `useWizardContext` specifically for this DeleteWizard
33+
*/
34+
export const useDeleteWizardContext = (): DeleteWizardContextData &
35+
WizardContextData => {
36+
const wizardContext = useWizardContext();
37+
const deleteWizardContext = useContext(DeleteWizardContext);
38+
39+
return {
40+
...wizardContext,
41+
...deleteWizardContext,
42+
};
43+
};
Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1-
import React from 'react';
1+
import React, { MouseEventHandler } from 'react';
22

3+
import { Variant } from '@leafygreen-ui/button';
4+
import { CompoundSubComponent } from '@leafygreen-ui/compound-component';
35
import { css, cx } from '@leafygreen-ui/emotion';
6+
import TrashIcon from '@leafygreen-ui/icon/Trash';
7+
import { Either } from '@leafygreen-ui/lib';
48
import { breakpoints } from '@leafygreen-ui/tokens';
5-
import { Wizard, WizardFooterProps } from '@leafygreen-ui/wizard';
9+
import {
10+
Wizard,
11+
WizardFooterProps,
12+
WizardSubComponentProperties,
13+
} from '@leafygreen-ui/wizard';
14+
15+
import { useDeleteWizardContext } from './DeleteWizardContext';
616

717
const footerStyles = css`
818
position: sticky;
@@ -14,19 +24,97 @@ const footerContentStyles = css`
1424
max-width: ${breakpoints.XLDesktop}px;
1525
`;
1626

27+
type DeleteWizardFooterProps = Either<
28+
WizardFooterProps & {
29+
/**
30+
* Sets the Back button text (for steps > 1)
31+
*/
32+
backButtonText?: string;
33+
/**
34+
* Sets the Cancel button text.
35+
* The Cancel button will not render if no text is provided
36+
*/
37+
cancelButtonText?: string;
38+
39+
/**
40+
* Sets the Primary button text.
41+
*
42+
* The Primary button icon and variant are set automatically based on the activeStep index.
43+
* Provide a `primaryButtonProps` to override this behavior
44+
*/
45+
primaryButtonText?: string;
46+
},
47+
'primaryButtonProps' | 'primaryButtonText'
48+
>;
49+
1750
/**
1851
* A wrapper around Wizard.Footer with embedded styles for the DeleteWizard template
1952
*/
20-
export const DeleteWizardFooter = ({
21-
className,
22-
contentClassName,
23-
...props
24-
}: WizardFooterProps) => {
25-
return (
26-
<Wizard.Footer
27-
className={cx(footerStyles, className)}
28-
contentClassName={cx(footerContentStyles, contentClassName)}
29-
{...props}
30-
/>
31-
);
32-
};
53+
export const DeleteWizardFooter = CompoundSubComponent(
54+
({
55+
className,
56+
contentClassName,
57+
primaryButtonText,
58+
cancelButtonText,
59+
backButtonText,
60+
primaryButtonProps,
61+
cancelButtonProps,
62+
backButtonProps,
63+
...props
64+
}: DeleteWizardFooterProps) => {
65+
const { activeStep, totalSteps, onCancel, onDelete } =
66+
useDeleteWizardContext();
67+
const isLastStep = activeStep === totalSteps - 1;
68+
69+
const handlePrimaryButtonClick: MouseEventHandler<
70+
HTMLButtonElement
71+
> = e => {
72+
primaryButtonProps?.onClick?.(e);
73+
74+
if (isLastStep) {
75+
onDelete?.(e);
76+
}
77+
};
78+
79+
const handleCancelButtonClick: MouseEventHandler<HTMLButtonElement> = e => {
80+
cancelButtonProps?.onClick?.(e);
81+
onCancel?.(e);
82+
};
83+
84+
const _primaryButtonProps = {
85+
children: primaryButtonText ?? '',
86+
variant: isLastStep ? Variant.Danger : Variant.Primary,
87+
leftGlyph: isLastStep ? <TrashIcon /> : undefined,
88+
...primaryButtonProps,
89+
// we define `onClick` after spreading props so this handler will always take precedence
90+
onClick: handlePrimaryButtonClick,
91+
};
92+
93+
const _backButtonProps = {
94+
children: backButtonText,
95+
...backButtonProps,
96+
};
97+
98+
const _cancelButtonProps = {
99+
children: cancelButtonText,
100+
...cancelButtonProps,
101+
// we define `onClick` after spreading props so this handler will always take precedence
102+
onClick: handleCancelButtonClick,
103+
};
104+
105+
return (
106+
<Wizard.Footer
107+
className={cx(footerStyles, className)}
108+
contentClassName={cx(footerContentStyles, contentClassName)}
109+
primaryButtonProps={_primaryButtonProps}
110+
cancelButtonProps={_cancelButtonProps}
111+
backButtonProps={_backButtonProps}
112+
{...props}
113+
/>
114+
);
115+
},
116+
{
117+
displayName: 'DeleteWizardFooter',
118+
key: WizardSubComponentProperties.Footer,
119+
},
120+
);
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1-
export { DeleteWizard, useDeleteWizardContext } from './DeleteWizard';
1+
export { DeleteWizard } from './DeleteWizard';
22
export { type DeleteWizardProps } from './DeleteWizard.types';
3+
export {
4+
DeleteWizardContext,
5+
type DeleteWizardContextData,
6+
DeleteWizardContextProvider,
7+
useDeleteWizardContext,
8+
} from './DeleteWizardContext';
39
export { useDeleteWizardStepContext } from './DeleteWizardStep';

0 commit comments

Comments
 (0)