Skip to content

Commit

Permalink
frontend: add confirm action component via prop (#3231)
Browse files Browse the repository at this point in the history
This PR adds a `ConfirmAction` component, rendered alongside the child
steps of `Wizard` when `confirmActionSettings` is set.

The component intercepts the `onSubmit` action and displays a modal
which can be customised in its content an behaviour such as disabling
the submit button, passing a component as its description, and
customisable `onCancel` and `onConfirm` callbacks.

<img width="495" alt="image"
src="https://github.com/user-attachments/assets/077e0f63-c849-4827-929b-c51062f21957"
/>

### Implementation sample

```
const Sample = () => {
  return (
    <Wizard {...}>
      <FirstStep
        name="First"
        confirmActionSettings={{
          title: "Confirm Action",
          description: "Are you sure you want to continue?",
        }}
      />
      <SecondStep name="Second" />
    </Wizard>
  );
};
```
### Wizard Component Enhancements:

* Added a new `ConfirmAction` component to handle confirmation dialogs
within the wizard. This component includes properties for title,
description, action labels, and handlers for confirm and cancel actions.
* Updated the `WizardContext` to include state and setter for
`confirmActionOpen` to manage the visibility of the confirmation dialog.
* Modified the `Wizard` component to integrate the `ConfirmAction`
component and manage its state. This includes adding
`confirmActionSettings` to wizard child props and updating the
`onSubmit` handler to trigger the confirmation dialog if required.
[[1]](diffhunk://#diff-fb5a2ff0894b9e7ddb45ec4d09e32f61843c333aff5fbc7ebf30371eaeefa373R52)
[[2]](diffhunk://#diff-fb5a2ff0894b9e7ddb45ec4d09e32f61843c333aff5fbc7ebf30371eaeefa373R129)
[[3]](diffhunk://#diff-fb5a2ff0894b9e7ddb45ec4d09e32f61843c333aff5fbc7ebf30371eaeefa373R169-R175)
[[4]](diffhunk://#diff-fb5a2ff0894b9e7ddb45ec4d09e32f61843c333aff5fbc7ebf30371eaeefa373R197-R200)
[[5]](diffhunk://#diff-fb5a2ff0894b9e7ddb45ec4d09e32f61843c333aff5fbc7ebf30371eaeefa373R230-R232)

### AppLayout Component Updates:

* Refactored the `styled` import to use a local styled module instead of
`@emotion/styled` in `frontend/packages/core/src/AppLayout/index.tsx`.
  • Loading branch information
jecr authored Feb 17, 2025
1 parent 6ed09ed commit e02a57c
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 3 deletions.
4 changes: 2 additions & 2 deletions frontend/packages/core/src/AppLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from "react";
import { Outlet } from "react-router-dom";
import styled from "@emotion/styled";
import { Grid as MuiGrid } from "@mui/material";

import Loadable from "../loading";
import styled from "../styled";
import type { AppConfiguration } from "../Types";

import Drawer from "./drawer";
Expand All @@ -22,7 +22,7 @@ const ContentGrid = styled(MuiGrid)<{ $isFullScreen: boolean }>(
})
);

const MainContent = styled.div({ overflowY: "auto", width: "100%" });
const MainContent = styled("div")({ overflowY: "auto", width: "100%" });

interface AppLayoutProps {
isLoading?: boolean;
Expand Down
2 changes: 2 additions & 0 deletions frontend/packages/core/src/Contexts/wizard-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export interface ContextProps {
setIsLoading: (isLoading: boolean) => void;
setHasError: (hasError: boolean) => void;
setIsComplete?: (isComplete: boolean) => void;
confirmActionOpen?: boolean;
setConfirmActionOpen?: (open: boolean) => void;
}

const WizardContext = React.createContext<() => ContextProps>(undefined);
Expand Down
67 changes: 67 additions & 0 deletions frontend/packages/wizard/src/confirm-action.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from "react";
import {
Button,
Dialog,
DialogActions,
DialogContent,
Typography,
useWizardContext,
} from "@clutch-sh/core";

export interface ConfirmActionProps {
title: string;
description: React.ReactNode | string;
actionLabel?: string;
onConfirm?: () => void;
onCancel?: () => void;
submitDisabled?: boolean;
}

const ConfirmAction: React.FC<ConfirmActionProps> = ({
title,
description,
actionLabel,
onConfirm,
onCancel,
submitDisabled = false,
}) => {
const { confirmActionOpen: open, setConfirmActionOpen: setOpen, onSubmit } = useWizardContext();

const handleConfirm = () => {
onSubmit();
setOpen(false);
if (onConfirm) {
onConfirm();
}
};

const handleCancel = () => {
setOpen(false);
if (onCancel) {
onCancel();
}
};

return (
<Dialog title={title} open={open} onClose={handleCancel}>
<DialogContent>
{typeof description === "string" ? (
<Typography variant="body1">{description}</Typography>
) : (
description
)}
</DialogContent>
<DialogActions>
<Button text="Cancel" onClick={handleCancel} variant="neutral" />
<Button
text={actionLabel || "Confirm"}
onClick={handleConfirm}
variant="danger"
disabled={submitDisabled}
/>
</DialogActions>
</Dialog>
);
};

export default ConfirmAction;
17 changes: 16 additions & 1 deletion frontend/packages/wizard/src/wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type {
} from "@mui/material";
import { alpha, Container as MuiContainer, Theme } from "@mui/material";

import ConfirmAction, { ConfirmActionProps } from "./confirm-action";
import { useWizardState, WizardActionType } from "./state";
import type { WizardStepProps } from "./step";

Expand All @@ -48,6 +49,7 @@ export interface WizardChild {
startOver: boolean;
startOverText?: string;
};
confirmActionSettings?: ConfirmActionProps;
}

interface WizardChildren extends JSX.Element {
Expand Down Expand Up @@ -124,6 +126,7 @@ const Wizard = ({
const [state, dispatch] = useWizardState();
const [wizardStepData, setWizardStepData] = React.useState<WizardStepData>({});
const [globalWarnings, setGlobalWarnings] = React.useState<string[]>([]);
const [confirmDialogOpen, setConfirmDialogOpen] = React.useState(false);
const dataLayoutManager = useDataLayoutManager(dataLayout);
const [, setSearchParams] = useSearchParams();
const locationState = useLocation().state as { origin?: string };
Expand Down Expand Up @@ -163,8 +166,13 @@ const Wizard = ({
};

const context = (child: JSX.Element) => {
const confirmActionSettings = child.props?.confirmActionSettings;

return {
onSubmit: wizardStepData?.[child.type.name]?.onSubmit || handleNext,
onSubmit:
confirmActionSettings && !confirmDialogOpen
? () => setConfirmDialogOpen(true)
: wizardStepData?.[child.type.name]?.onSubmit || handleNext,
setOnSubmit: (f: (...args: any[]) => void) => {
updateStepData(child.type.name, { onSubmit: f(handleNext) });
},
Expand All @@ -186,6 +194,10 @@ const Wizard = ({
onNext: (params: WizardNavigationProps) => {
handleNavigation(params, WizardActionType.NEXT);
},
confirmActionOpen: confirmDialogOpen,
setConfirmActionOpen: (open: boolean) => {
setConfirmDialogOpen(open);
},
};
};

Expand Down Expand Up @@ -215,6 +227,9 @@ const Wizard = ({
<WizardContext.Provider value={() => context(child)}>
<Grid container direction="column" justifyContent="center" alignItems="center">
{child}
{child.props?.confirmActionSettings && (
<ConfirmAction {...(child.props.confirmActionSettings as ConfirmActionProps)} />
)}
</Grid>
</WizardContext.Provider>
</DataLayoutContext.Provider>
Expand Down

0 comments on commit e02a57c

Please sign in to comment.