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

frontend: add confirm action component via context #3230

Closed
wants to merge 8 commits into from
Closed
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
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: 1 addition & 1 deletion frontend/packages/core/src/Contexts/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export { ApplicationContext, useAppContext } from "./app-context";
export { ShortLinkContext, useShortLinkContext } from "./short-link-context";
export { WizardContext, useWizardContext } from "./wizard-context";
export type { WizardNavigationProps } from "./wizard-context";
export type { WizardNavigationProps, ConfirmActionProps } from "./wizard-context";
export { WorkflowStorageContext, useWorkflowStorageContext } from "./workflow-storage-context";
export type {
WorkflowRemoveDataFn,
Expand Down
9 changes: 9 additions & 0 deletions frontend/packages/core/src/Contexts/wizard-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ export interface WizardNavigationProps {
keepSearch?: boolean;
}

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

export interface ContextProps {
displayWarnings: (warnings: string[]) => void;
onBack: (params?: WizardNavigationProps) => void;
Expand All @@ -14,6 +22,7 @@ export interface ContextProps {
setIsLoading: (isLoading: boolean) => void;
setHasError: (hasError: boolean) => void;
setIsComplete?: (isComplete: boolean) => void;
showConfirmAction?: (props: ConfirmActionProps) => void;
}

const WizardContext = React.createContext<() => ContextProps>(undefined);
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/core/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export type {
WorkflowRetrieveDataFn,
WorkflowStoreDataFn,
WizardNavigationProps,
ConfirmActionProps,
} from "./Contexts";
export type { ClutchError } from "./Network/errors";
export type { TypographyProps } from "./typography";
Expand Down
64 changes: 64 additions & 0 deletions frontend/packages/wizard/src/confirm-action.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from "react";
import {
Button,
ConfirmActionProps,
Dialog,
DialogActions,
DialogContent,
TextField,
Typography,
} from "@clutch-sh/core";

interface ConfirmActionDialogProps extends ConfirmActionProps {
open: boolean;
onCancel: () => void;
}

const ConfirmAction: React.FC<ConfirmActionDialogProps> = ({
open,
title,
description,
confirmationText,
actionLabel,
onConfirm,
onCancel,
}) => {
const [input, setInput] = React.useState("");

React.useEffect(() => setInput(""), [open]);

const handleConfirm = () => {
if (input === confirmationText) {
onConfirm();
}
Comment on lines +31 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll probably want to leave this to the implementer as well. We will likely want a generic reason field versus something specific, better auditing that way.

};

return (
<Dialog title={title} open={open} onClose={onCancel}>
<DialogContent>
{typeof description === "string" ? (
<Typography variant="body1">{description}</Typography>
) : (
description
)}
<TextField
label="Confirmation"
value={input}
onChange={e => setInput(e.target.value)}
fullWidth
/>
Comment on lines +44 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to leave this as part of the description. We may prefer to have to some workflows with a confirmation but not necessarily requiring an input, so that should be left up to the implementer.

</DialogContent>
<DialogActions>
<Button text="Cancel" onClick={onCancel} variant="neutral" />
<Button
text={actionLabel || "Confirm"}
onClick={handleConfirm}
variant="danger"
disabled={input !== confirmationText}
/>
</DialogActions>
</Dialog>
);
};

export default ConfirmAction;
27 changes: 25 additions & 2 deletions frontend/packages/wizard/src/wizard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import type { WizardNavigationProps } from "@clutch-sh/core";
import React, { useState } from "react";
import type { ConfirmActionProps, WizardNavigationProps } from "@clutch-sh/core";
import {
Button,
ButtonGroup,
Expand Down Expand Up @@ -27,6 +27,7 @@ import type {
} from "@mui/material";
import { alpha, Container as MuiContainer, Theme } from "@mui/material";

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

Expand Down Expand Up @@ -124,6 +125,13 @@ const Wizard = ({
const [state, dispatch] = useWizardState();
const [wizardStepData, setWizardStepData] = React.useState<WizardStepData>({});
const [globalWarnings, setGlobalWarnings] = React.useState<string[]>([]);
const [confirmActionOpen, setConfirmActionOpen] = useState(false);
const [confirmActionConfig, setConfirmActionConfig] = useState({
title: "",
description: "",
confirmationText: "",
onConfirm: () => {},
} as ConfirmActionProps);
const dataLayoutManager = useDataLayoutManager(dataLayout);
const [, setSearchParams] = useSearchParams();
const locationState = useLocation().state as { origin?: string };
Expand Down Expand Up @@ -186,6 +194,10 @@ const Wizard = ({
onNext: (params: WizardNavigationProps) => {
handleNavigation(params, WizardActionType.NEXT);
},
showConfirmAction: (props: ConfirmActionProps) => {
setConfirmActionConfig(props);
setConfirmActionOpen(true);
},
};
};

Expand Down Expand Up @@ -299,6 +311,17 @@ const Wizard = ({
{error}
</Toast>
))}
<ConfirmAction
open={confirmActionOpen}
title={confirmActionConfig.title}
description={confirmActionConfig.description}
confirmationText={confirmActionConfig.confirmationText}
onConfirm={() => {
setConfirmActionOpen(false);
confirmActionConfig.onConfirm();
}}
onCancel={() => setConfirmActionOpen(false)}
/>
</Container>
);
};
Expand Down
Loading