Skip to content

Commit

Permalink
feat(reac, modal-wizard): add renderFinishButton prop (#3890)
Browse files Browse the repository at this point in the history
  • Loading branch information
agong-coveo committed Sep 6, 2024
1 parent 07b8dfa commit 2cd1c0e
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 19 deletions.
41 changes: 25 additions & 16 deletions packages/react/src/components/modalWizard/ModalWizard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import classNames from 'clsx';
import {ReactNode, FunctionComponent, ReactElement, useState, Children, isValidElement} from 'react';
import {Children, FunctionComponent, isValidElement, ReactElement, ReactNode, useState} from 'react';
import {useDispatch} from 'react-redux';

import {IDispatch, TooltipPlacement} from '../../utils';
Expand Down Expand Up @@ -83,6 +83,10 @@ export interface ModalWizardProps
* A callback function that is executed when the user clicks on the cancel button
*/
onCancel?: () => unknown;
/**
* Finish button to display on the last step
*/
renderFinishButton?: (isValid: boolean, close: () => void) => React.ReactElement;
children?: ReactNode;
}

Expand All @@ -104,6 +108,7 @@ export const ModalWizard: FunctionComponent<ModalWizardProps> = ({
onNext,
onPrevious,
onCancel,
renderFinishButton,
...modalProps
}) => {
const [currentStep, setCurrentStep] = useState(0);
Expand Down Expand Up @@ -153,21 +158,25 @@ export const ModalWizard: FunctionComponent<ModalWizardProps> = ({
}}
enabled
/>
<Button
primary
name={isLastStep ? finishButtonLabel : nextButtonLabel}
enabled={isValid}
onClick={() => {
if (isLastStep) {
onFinish?.(close);
} else {
onNext?.();
setCurrentStep(currentStep + 1);
}
}}
tooltip={message}
tooltipPlacement={TooltipPlacement.Top}
/>
{!!renderFinishButton && isLastStep ? (
renderFinishButton?.(isValid, close)
) : (
<Button
primary
name={isLastStep ? finishButtonLabel : nextButtonLabel}
enabled={isValid}
onClick={() => {
if (isLastStep) {
onFinish?.(close);
} else {
onNext?.();
setCurrentStep(currentStep + 1);
}
}}
tooltip={message}
tooltipPlacement={TooltipPlacement.Top}
/>
)}
</>
}
onAfterOpen={() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {render, screen, waitForElementToBeRemoved} from '@test-utils';
import userEvent from '@testing-library/user-event';
import {PureComponent} from 'react';
import {render, screen, waitForElementToBeRemoved} from '@test-utils';

import {Button} from '../../button';
import {ModalWizard} from '../ModalWizard';

describe('ModalWizard', () => {
Expand Down Expand Up @@ -167,4 +168,25 @@ describe('ModalWizard', () => {
expect(screen.queryByText(/footer children 1/i)).not.toBeInTheDocument();
expect(screen.getByText(/footer children 2/i)).toBeVisible();
});

it('replaces the finish button on the last step if a function was provided for renderFinishButton', async () => {
const user = userEvent.setup();
render(
<ModalWizard
id="🧙‍♂️"
renderFinishButton={(isValid: boolean, close: () => void) => (
<Button primary name="We're at the last step!" enabled={isValid} onClick={() => onFinish(close)} />
)}
>
<div>Step 1</div>
<div>Step 2</div>
</ModalWizard>,
{initialState: {modals: [{id: '🧙‍♂️', isOpened: true}]}},
);

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

expect(screen.queryByText(/finish/i)).not.toBeInTheDocument();
expect(screen.getByText(/we\'re at the last step\!/i)).toBeVisible();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
Button,
Form,
IDispatch,
InputConnected,
InputSelectors,
Label,
ModalWizard,
openModal,
PlasmaState,
Radio,
RadioSelectConnected,
RadioSelectSelectors,
} from '@coveord/plasma-react';
import {useDispatch, useSelector} from 'react-redux';

const Demo = () => {
const dispatch: IDispatch = useDispatch();
const selectedOption = useSelector((state: PlasmaState) =>
RadioSelectSelectors.getValue(state, {id: 'radio-step-1'}),
);
const inputTwoValue = useSelector(
(state: PlasmaState) => InputSelectors.getValue(state, {id: 'input-step-2'}) || '',
);

const validateStep = (currentStep: number, numberOfSteps: number) => {
if (currentStep === 0) {
return {
isValid: !!selectedOption,
message: !selectedOption && 'Select an option to continue.',
};
} else if (currentStep === numberOfSteps - 1) {
if (!inputTwoValue.trim()) {
return {isValid: false, message: 'The input must have some value to continue.'};
}
return {isValid: true};
}
return {isValid: true};
};

const onFinish = (close) => {
alert('Congratulations! You completed the wizard');
close();
};

return (
<>
<Button
name="Open wizard"
enabled
primary
onClick={() => dispatch(openModal('wizard-with-provided-render-finish-button'))}
/>
<ModalWizard
id="wizard-with-provided-render-finish-button"
title="Wizard 🧙‍♂️"
onFinish={onFinish}
validateStep={validateStep}
isDirty={!!selectedOption || !!inputTwoValue}
renderFinishButton={(isValid: boolean, close: () => void) => (
<Button primary name="We're at the last step!" enabled={isValid} onClick={() => onFinish(close)} />
)}
>
<Form title="Step 1" mods={['mod-form-top-bottom-padding', 'mod-header-padding']}>
<RadioSelectConnected id="radio-step-1">
<Radio id="path1" name="wizardPath" value="heal">
<Label>Pick the healing potion</Label>
</Radio>
<Radio id="path2" name="wizardPath" value="strength">
<Label>Pick the strenghtening potion</Label>
</Radio>
</RadioSelectConnected>
</Form>
<Form title="Step 2" mods={['mod-form-top-bottom-padding', 'mod-header-padding']}>
<InputConnected
id="input-step-2"
autoComplete="off"
validateOnChange
validate={(value: string) => !!value.trim()}
labelTitle={<Label invalidMessage="Cannot be left empty">Enter something to continue</Label>}
/>
</Form>
</ModalWizard>
</>
);
};
export default Demo;
9 changes: 7 additions & 2 deletions packages/website/src/pages/legacy/layout/ModalWizard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {ModalWizardLegacyMetadata} from '@coveord/plasma-components-props-analyzer';
import ModalWizardDemo from '@examples/legacy/layout/ModalWizard/ModalWizard.demo?demo';
import ModalWizardWithProvidedFinishButtonDemo from '@examples/legacy/layout/ModalWizard/ModalWizardWithProvidedFinishButton.demo?demo';
import ModalWizardWithValidationIdsDemo from '@examples/legacy/layout/ModalWizard/ModalWizardWithValidationIds.demo?demo';

import {PageLayout} from '../../../building-blocs/PageLayout';

export const ModalWizardDemos = () => (
Expand All @@ -13,7 +13,12 @@ export const ModalWizardDemos = () => (
sourcePath="/packages/react/src/components/modalWizard/ModalWizard.tsx"
demo={<ModalWizardDemo />}
propsMetadata={ModalWizardLegacyMetadata}
examples={{withValidationIds: <ModalWizardWithValidationIdsDemo title="Using validation ids" />}}
examples={{
withValidationIds: <ModalWizardWithValidationIdsDemo title="Using validation ids" />,
withProvidedFinishButton: (
<ModalWizardWithProvidedFinishButtonDemo title="Using provided finishButton prop" />
),
}}
/>
);

Expand Down

0 comments on commit 2cd1c0e

Please sign in to comment.