Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] feat(MultistepDialog): allow custom next/back button props per step #4610

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 packages/core/src/components/dialog/dialogStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import { polyfill } from "react-lifecycles-compat";

import { AbstractPureComponent2, Classes } from "../../common";
import { DISPLAYNAME_PREFIX, HTMLDivProps, IProps } from "../../common/props";
import { IButtonProps } from "../button/buttons";

export type DialogStepId = string | number;
export type DialogStepButtonProps = Partial<Pick<IButtonProps, "disabled" | "text">>;

export interface IDialogStepProps extends IProps, Omit<HTMLDivProps, "id" | "title" | "onClick"> {
/**
Expand All @@ -43,6 +45,16 @@ export interface IDialogStepProps extends IProps, Omit<HTMLDivProps, "id" | "tit
* Content of step title element, rendered in a list left of the active panel.
*/
title?: React.ReactNode;

/**
* Props for the back button.
*/
backButtonProps?: DialogStepButtonProps;

/**
* Props for the next button.
*/
nextButtonProps?: DialogStepButtonProps;
}

@polyfill
Expand Down
16 changes: 11 additions & 5 deletions packages/core/src/components/dialog/multistepDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ import { AbstractPureComponent2, Classes, Utils } from "../../common";
import { DISPLAYNAME_PREFIX } from "../../common/props";
import { Button, IButtonProps } from "../button/buttons";
import { Dialog, IDialogProps } from "./dialog";
import { DialogStep, DialogStepId, IDialogStepProps } from "./dialogStep";
import { DialogStep, DialogStepId, IDialogStepProps, DialogStepButtonProps } from "./dialogStep";

type DialogStepElement = React.ReactElement<IDialogStepProps & { children: React.ReactNode }>;

export interface IMultistepDialogProps extends IDialogProps {
/**
* Props for the back button.
*/
backButtonProps?: Partial<Pick<IButtonProps, "disabled" | "text">>;
backButtonProps?: DialogStepButtonProps;

/**
* Props for the button to display on the final step.
Expand All @@ -40,7 +40,7 @@ export interface IMultistepDialogProps extends IDialogProps {
/**
* Props for the next button.
*/
nextButtonProps?: Partial<Pick<IButtonProps, "disabled" | "text">>;
nextButtonProps?: DialogStepButtonProps;

/**
* A callback that is invoked when the user selects a different step by clicking on back, next, or a step itself.
Expand Down Expand Up @@ -164,28 +164,34 @@ export class MultistepDialog extends AbstractPureComponent2<IMultistepDialogProp

private renderButtons() {
const { selectedIndex } = this.state;
const steps = this.getDialogStepChildren();
const buttons = [];

if (this.state.selectedIndex > 0) {
const backButtonProps = steps[selectedIndex].props.backButtonProps ?? this.props.backButtonProps;

buttons.push(
<Button
key="back"
onClick={this.getDialogStepChangeHandler(selectedIndex - 1)}
text="Back"
{...this.props.backButtonProps}
{...backButtonProps}
/>,
);
}

if (selectedIndex === this.getDialogStepChildren().length - 1) {
buttons.push(<Button intent="primary" key="final" text="Submit" {...this.props.finalButtonProps} />);
} else {
const nextButtonProps = steps[selectedIndex].props.nextButtonProps ?? this.props.nextButtonProps;

buttons.push(
<Button
intent="primary"
key="next"
onClick={this.getDialogStepChangeHandler(selectedIndex + 1)}
text="Next"
{...this.props.nextButtonProps}
{...nextButtonProps}
/>,
);
}
Expand Down
37 changes: 37 additions & 0 deletions packages/core/test/multistep-dialog/multistepDialogTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,43 @@ describe("<MultistepDialog>", () => {
assert.strictEqual(dialog.find(NEXT_BUTTON).prop("disabled"), true);
dialog.unmount();
});

it("disables next for second step when disabled on nextButtonProps is set to true", () => {
const dialog = mount(
<MultistepDialog isOpen={true} usePortal={false}>
<DialogStep id="one" title="Step 1" panel={<Panel />} />
<DialogStep id="two" title="Step 2" panel={<Panel />} nextButtonProps={{ disabled: true }} />
<DialogStep id="three" title="Step 3" panel={<Panel />} />
</MultistepDialog>,
);

assert.strictEqual(dialog.state("selectedIndex"), 0);
assert.strictEqual(dialog.find(NEXT_BUTTON).prop("disabled"), undefined);
dialog.find(NEXT_BUTTON).simulate("click");
assert.strictEqual(dialog.state("selectedIndex"), 1);
assert.strictEqual(dialog.find(NEXT_BUTTON).prop("disabled"), true);
dialog.find(NEXT_BUTTON).simulate("click");
assert.strictEqual(dialog.state("selectedIndex"), 1);
dialog.unmount();
});

it("disables back for second step when disabled on backButtonProps is set to true", () => {
const dialog = mount(
<MultistepDialog isOpen={true} usePortal={false}>
<DialogStep id="one" title="Step 1" panel={<Panel />} />
<DialogStep id="two" title="Step 2" panel={<Panel />} backButtonProps={{ disabled: true }} />
<DialogStep id="three" title="Step 3" panel={<Panel />} />
</MultistepDialog>,
);

assert.strictEqual(dialog.state("selectedIndex"), 0);
dialog.find(NEXT_BUTTON).simulate("click");
assert.strictEqual(dialog.state("selectedIndex"), 1);
assert.strictEqual(dialog.find(BACK_BUTTON).prop("disabled"), true);
dialog.find(BACK_BUTTON).simulate("click");
assert.strictEqual(dialog.state("selectedIndex"), 1);
dialog.unmount();
});
});

const Panel: React.FunctionComponent = () => <strong> panel</strong>;