diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayloadAdvanced.js b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayloadAdvanced.js new file mode 100644 index 00000000000..65260bb3c12 --- /dev/null +++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayloadAdvanced.js @@ -0,0 +1,235 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs'; +import Button from '@mui/material/Button'; +import LoadingButton from '@mui/lab/LoadingButton'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import TextField from '@mui/material/TextField'; +import Stack from '@mui/material/Stack'; +import MenuItem from '@mui/material/MenuItem'; +import { FormContext, useFormContext } from './TransferFormContext'; + +function mockTransfer(formData) { + return new Promise((resolve) => { + setTimeout(() => { + console.log('FormData', formData); + const uuid = crypto.randomUUID(); + resolve(uuid); + }, 1000); + }); +} + +function TransactionDialog({ payload, open, onClose }) { + const [loading, setLoading] = React.useState(false); + const { formData } = useFormContext(); + + const isValidCsrf = React.useMemo(() => { + if (payload.data) { + return true; + } + return false; + }, [payload.data]); + + return ( + onClose(null)}> + Confirm transfer + {payload.component} + + { + setLoading(true); + try { + const uuid = await mockTransfer(formData); + onClose(uuid); + } finally { + setLoading(false); + } + }} + > + Submit + + + + ); +} + +TransactionDialog.propTypes = { + /** + * A function to call when the dialog should be closed. If the dialog has a return + * value, it should be passed as an argument to this function. You should use the promise + * that is returned to show a loading state while the dialog is performing async actions + * on close. + * @param result The result to return from the dialog. + * @returns A promise that resolves when the dialog can be fully closed. + */ + onClose: PropTypes.func.isRequired, + /** + * Whether the dialog is open. + */ + open: PropTypes.bool.isRequired, + /** + * The payload that was passed when the dialog was opened. + */ + payload: PropTypes.shape({ + component: PropTypes.node, + data: PropTypes.string.isRequired, + }).isRequired, +}; + +function PayloadIndia() { + const { formData, setFormData } = useFormContext(); + + return ( + + + setFormData({ ...formData, amount: event.target.value }) + } + placeholder="Enter amount in rupees" + slotProps={{ + input: { + endAdornment: '₹', + }, + }} + /> + + setFormData({ ...formData, ifscCode: event.target.value }) + } + placeholder="Enter 11-character IFSC code" + helperText="Bank branch identifier code (e.g. HDFC0001234)" + /> + + setFormData({ ...formData, accountNumber: event.target.value }) + } + placeholder="Enter bank account number" + helperText="Please verify the account number carefully" + /> + + ); +} + +function PayloadUSA() { + const { formData, setFormData } = useFormContext(); + + return ( + + + setFormData({ ...formData, amount: event.target.value }) + } + placeholder="Enter amount in dollars" + slotProps={{ + input: { + endAdornment: '$', + }, + }} + /> + + setFormData({ ...formData, routingNumber: event.target.value }) + } + placeholder="123456789" + helperText="ABA routing transit number (e.g. 021000021)" + /> + + setFormData({ ...formData, accountNumber: event.target.value }) + } + placeholder="Enter 10-12 digit account number" + helperText="US bank account number (typically 10-12 digits)" + /> + { + console.log('changing', event.target.value); + setFormData({ + ...formData, + accountType: event.target.value, + }); + }} + defaultValue="checking" + placeholder="Select account type" + > + Checking + Savings + + + ); +} + +function DemoContent() { + const dialogs = useDialogs(); + const [country, setCountry] = React.useState('usa'); + const csrfToken = crypto.randomUUID(); + + return ( + + setCountry(event.target.value)} + sx={{ minWidth: 200 }} + > + United States + India + + + + + ); +} + +export default function CustomDialogWithPayloadAdvanced() { + const [formData, setFormData] = React.useState({ + amount: '', + accountNumber: '', + }); + + const contextValue = React.useMemo(() => ({ formData, setFormData }), [formData]); + + return ( + + + + + + ); +} diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayloadAdvanced.tsx b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayloadAdvanced.tsx new file mode 100644 index 00000000000..1f6688f5cfc --- /dev/null +++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayloadAdvanced.tsx @@ -0,0 +1,219 @@ +import * as React from 'react'; +import { DialogsProvider, useDialogs, DialogProps } from '@toolpad/core/useDialogs'; +import Button from '@mui/material/Button'; +import LoadingButton from '@mui/lab/LoadingButton'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import TextField from '@mui/material/TextField'; +import Stack from '@mui/material/Stack'; +import MenuItem from '@mui/material/MenuItem'; +import { + FormContext, + useFormContext, + type TransferFormData, +} from './TransferFormContext'; + +function mockTransfer(formData: TransferFormData): Promise { + return new Promise((resolve) => { + setTimeout(() => { + console.log('FormData', formData); + const uuid = crypto.randomUUID(); + resolve(uuid); + }, 1000); + }); +} + +function TransactionDialog({ + payload, + open, + onClose, +}: DialogProps<{ component: React.ReactNode; data: string }, string | null>) { + const [loading, setLoading] = React.useState(false); + const { formData } = useFormContext(); + + const isValidCsrf = React.useMemo(() => { + if (payload.data) { + return true; + } + return false; + }, [payload.data]); + + return ( + onClose(null)}> + Confirm transfer + {payload.component} + + { + setLoading(true); + try { + const uuid = await mockTransfer(formData); + onClose(uuid); + } finally { + setLoading(false); + } + }} + > + Submit + + + + ); +} + +function PayloadIndia() { + const { formData, setFormData } = useFormContext(); + + return ( + + + setFormData({ ...formData, amount: event.target.value }) + } + placeholder="Enter amount in rupees" + slotProps={{ + input: { + endAdornment: '₹', + }, + }} + /> + + setFormData({ ...formData, ifscCode: event.target.value }) + } + placeholder="Enter 11-character IFSC code" + helperText="Bank branch identifier code (e.g. HDFC0001234)" + /> + + setFormData({ ...formData, accountNumber: event.target.value }) + } + placeholder="Enter bank account number" + helperText="Please verify the account number carefully" + /> + + ); +} + +function PayloadUSA() { + const { formData, setFormData } = useFormContext(); + + return ( + + + setFormData({ ...formData, amount: event.target.value }) + } + placeholder="Enter amount in dollars" + slotProps={{ + input: { + endAdornment: '$', + }, + }} + /> + + setFormData({ ...formData, routingNumber: event.target.value }) + } + placeholder="123456789" + helperText="ABA routing transit number (e.g. 021000021)" + /> + + setFormData({ ...formData, accountNumber: event.target.value }) + } + placeholder="Enter 10-12 digit account number" + helperText="US bank account number (typically 10-12 digits)" + /> + { + console.log('changing', event.target.value); + setFormData({ + ...formData, + accountType: event.target.value as 'checking' | 'savings', + }); + }} + defaultValue="checking" + placeholder="Select account type" + > + Checking + Savings + + + ); +} + +function DemoContent() { + const dialogs = useDialogs(); + const [country, setCountry] = React.useState('usa'); + const csrfToken = crypto.randomUUID(); + + return ( + + setCountry(event.target.value)} + sx={{ minWidth: 200 }} + > + United States + India + + + + + ); +} + +export default function CustomDialogWithPayloadAdvanced() { + const [formData, setFormData] = React.useState({ + amount: '', + accountNumber: '', + }); + + const contextValue = React.useMemo(() => ({ formData, setFormData }), [formData]); + + return ( + + + + + + ); +} diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayloadAdvanced.tsx.preview b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayloadAdvanced.tsx.preview new file mode 100644 index 00000000000..fc942d7a7db --- /dev/null +++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayloadAdvanced.tsx.preview @@ -0,0 +1,4 @@ +const uuid = await dialogs.open(TransactionDialog, { + component: country === 'usa' ? : , + data: csrfToken, +}); \ No newline at end of file diff --git a/docs/data/toolpad/core/components/use-dialogs/TransferFormContext.js b/docs/data/toolpad/core/components/use-dialogs/TransferFormContext.js new file mode 100644 index 00000000000..172d1c9d9f5 --- /dev/null +++ b/docs/data/toolpad/core/components/use-dialogs/TransferFormContext.js @@ -0,0 +1,13 @@ +import * as React from 'react'; + +// Create context with initial value +export const FormContext = React.createContext(null); + +// Custom hook to use the form context +export function useFormContext() { + const context = React.useContext(FormContext); + if (!context) { + throw new Error('useFormContext must be used within a FormProvider'); + } + return context; +} diff --git a/docs/data/toolpad/core/components/use-dialogs/TransferFormContext.tsx b/docs/data/toolpad/core/components/use-dialogs/TransferFormContext.tsx new file mode 100644 index 00000000000..c9979355a6d --- /dev/null +++ b/docs/data/toolpad/core/components/use-dialogs/TransferFormContext.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; + +export interface TransferFormData { + amount: string; + accountNumber: string; + // USA specific + routingNumber?: string; + accountType?: 'checking' | 'savings'; + // India specific + ifscCode?: string; +} + +// Create context with initial value +export const FormContext = React.createContext<{ + formData: TransferFormData; + setFormData: (data: TransferFormData) => void; +} | null>(null); + +// Custom hook to use the form context +export function useFormContext() { + const context = React.useContext(FormContext); + if (!context) { + throw new Error('useFormContext must be used within a FormProvider'); + } + return context; +} diff --git a/docs/data/toolpad/core/components/use-dialogs/use-dialogs.md b/docs/data/toolpad/core/components/use-dialogs/use-dialogs.md index 58504d097fd..4e04a4350cc 100644 --- a/docs/data/toolpad/core/components/use-dialogs/use-dialogs.md +++ b/docs/data/toolpad/core/components/use-dialogs/use-dialogs.md @@ -103,6 +103,14 @@ Analog to [`window.prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Win {{"demo": "PromptDialog.js"}} +## Advanced Examples + +### Complex payload + +The `payload` can be an object which contains anything - including a React Component. This allows you to pass arbitrary data and components to your dialog. For instance, to render a form with location-dependent fields inside a dialog: + +{{"demo": "CustomDialogWithPayloadAdvanced.js"}} + ## Hook API - [`useDialogs()`](/toolpad/core/react-use-dialogs/api/)