Skip to content

Commit

Permalink
feat(DeleteModal): make more general and reflect current DeleteModal …
Browse files Browse the repository at this point in the history
…usage
  • Loading branch information
adamviktora committed Sep 26, 2024
1 parent 5f46c9a commit 5f3639c
Show file tree
Hide file tree
Showing 5 changed files with 316 additions and 213 deletions.
108 changes: 21 additions & 87 deletions packages/module/patternfly-docs/content/examples/Basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,21 @@ export const DeleteModalBasic: React.FunctionComponent = () => {
const [isModalDestructiveOpen, setIsModalDestructiveOpen] = React.useState(false);
const [isModalExtraDestructiveOpen, setIsModalExtraDestructiveOpen] = React.useState(false);

const [isModalMultiExtraDestructiveOpen, setIsModalMultiExtraDestructiveOpen] = React.useState(false);

const [isModalModelRegistryOpen, setIsModalModelRegistryOpen] = React.useState(false);
const [isModalPipelineServerOpen, setIsModalPipelineServerOpen] = React.useState(false);

const handleModalRecoverableToggle = (_event: KeyboardEvent | React.MouseEvent) => {
const handleModalRecoverableToggle = (_event: KeyboardEvent | React.MouseEvent | undefined) => {
setIsModalRecoverableOpen(!isModalRecoverableOpen);
};

const handleModalDestructiveToggle = (_event: KeyboardEvent | React.MouseEvent) => {
const handleModalDestructiveToggle = (_event: KeyboardEvent | React.MouseEvent | undefined) => {
setIsModalDestructiveOpen(!isModalDestructiveOpen);
};

const handleModalExtraDestructiveToggle = (_event: KeyboardEvent | React.MouseEvent) => {
const handleModalExtraDestructiveToggle = (_event: KeyboardEvent | React.MouseEvent | undefined) => {
setIsModalExtraDestructiveOpen(!isModalExtraDestructiveOpen);
};

const handleModalMultiExtraDestructiveToggle = (_event: KeyboardEvent | React.MouseEvent) => {
setIsModalMultiExtraDestructiveOpen(!isModalMultiExtraDestructiveOpen);
};

const handleModalModelRegistryToggle = (_event: KeyboardEvent | React.MouseEvent) => {
setIsModalModelRegistryOpen(!isModalModelRegistryOpen);
};

const handleModalPipelineServerToggle = (_event: KeyboardEvent | React.MouseEvent) => {
setIsModalPipelineServerOpen(!isModalPipelineServerOpen);
};

return (
<>
<Stack hasGutter style={{ marginBottom: '2rem' }}>
<div>Modals with one item to delete</div>
<StackItem>
<Button variant="primary" onClick={handleModalRecoverableToggle}>
Show delete modal (Easily recoverable)
Expand All @@ -57,84 +39,36 @@ export const DeleteModalBasic: React.FunctionComponent = () => {
</StackItem>
</Stack>

<Stack hasGutter style={{ marginBottom: '2rem' }}>
<div>Modals with multiple items to delete</div>
<StackItem>
<Button variant="primary" onClick={handleModalMultiExtraDestructiveToggle}>
Show delete modal - multiple items (Extra destructive)
</Button>
</StackItem>
</Stack>

<Stack hasGutter>
<div>Modals with custom delete messages</div>
<StackItem>
<Button variant="primary" onClick={handleModalModelRegistryToggle}>
Show delete modal - Model registry
</Button>
</StackItem>
<StackItem>
<Button variant="primary" onClick={handleModalPipelineServerToggle}>
Show delete modal - Pipeline server
</Button>
</StackItem>
</Stack>

<DeleteModal
title="Delete user group?"
deleteName="123"
onDelete={() => handleModalRecoverableToggle(undefined)}
deleteVariant="easily-recoverable"
item="user group"
itemName="my-team-abc"
isOpen={isModalRecoverableOpen}
onClose={handleModalRecoverableToggle}
/>
>
User group 123 will be deleted.
</DeleteModal>
<DeleteModal
title="Delete experiment?"
deleteName="cool-exp"
onDelete={() => handleModalDestructiveToggle(undefined)}
deleteVariant="destructive"
item="pipeline"
itemName="pipeline_456"
isOpen={isModalDestructiveOpen}
onClose={handleModalDestructiveToggle}
/>
>
Experiment cool-exp will be deleted.
</DeleteModal>
<DeleteModal
title="Delete project?"
deleteName="RedHatAwesome"
onDelete={() => handleModalExtraDestructiveToggle(undefined)}
deleteVariant="extra-destructive"
item="project"
itemName="super-123-project"
isOpen={isModalExtraDestructiveOpen}
onClose={handleModalExtraDestructiveToggle}
/>

{/* Modals with multiple items to delete */}
<DeleteModal
deleteVariant="extra-destructive"
items="projects"
itemNames={['random-project-101', 'super-123-project', 'anotherOne']}
isOpen={isModalMultiExtraDestructiveOpen}
onClose={handleModalMultiExtraDestructiveToggle}
/>

{/* Modals with custom delete messages */}
<DeleteModal
deleteVariant="extra-destructive"
item="model registry"
itemName="registry-123"
message={{
toDelete: ', its default group, and any permissions associated with it',
endNote: 'Data located in the database connected to the registry will be unaffected.'
}}
isOpen={isModalModelRegistryOpen}
onClose={handleModalModelRegistryToggle}
/>
<DeleteModal
deleteVariant="extra-destructive"
item="pipeline server"
itemName="abc-server"
message={{
resourcesToDelete: ', including pipelines and runs,',
endNote:
'You will not be able to create new pipelines or pipeline runs until you create a new pipeline server.'
}}
isOpen={isModalPipelineServerOpen}
onClose={handleModalPipelineServerToggle}
/>
>
Project RedHatAwesome will be deleted.
</DeleteModal>
</>
);
};
140 changes: 140 additions & 0 deletions packages/module/patternfly-docs/content/examples/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,143 @@ import { DeleteModal } from "@patternfly/ai-infra-ui-components";
```js file="./Basic.tsx" isFullscreen

```

### DeleteModal implementation

```ts
import React from 'react';
import {
Alert,
AlertProps,
Button,
Flex,
FlexItem,
Modal,
ModalBody,
ModalFooter,
ModalHeader,
ModalProps,
Stack,
StackItem,
TextInput,
TextInputProps
} from '@patternfly/react-core';

export type DeleteModalProps = ModalProps & {
/** Content rendered inside the modal header title. */
title: React.ReactNode;
/** Delete variant. Destructive and extra-destructive variants will show a warning icon and danger button. For extra-destructive variant, text input confirmation is needed. */
deleteVariant?: 'extra-destructive' | 'destructive' | 'easily-recoverable';
/** Text which the user should type in to confirm deletion (only for extra-destructive delete variant) */
deleteName: string;
/** Message describing what should the user type in to confirm deletion (only for extra-destructive delete variant) */
confirmationMessage?: (deleteName: string) => React.ReactNode;
/** Text of the delete button */
deleteButtonText?: string;
/** Text of the cancel button */
cancelButtonText?: string;
/** Callback on clicking the delete button */
onDelete: () => void;
/** Flag indicating that deletion is currently in progress */
isDeleting?: boolean;
/** Error indicating deletion has failed */
error?: Error;
/** Id of the modal for testing purposes (defaults to "delete-modal") */
testId?: string;
/** Additional props for confirmation text input (only for extra-destructive delete variant) */
textInputProps?: TextInputProps;
/** Additional props for error alert */
errorAlertProps?: AlertProps;
/** Modal ref */
ref?: React.RefObject<Modal>;
};

export const DeleteModal: React.FunctionComponent<DeleteModalProps> = ({
children,
title,
deleteVariant = 'extra-destructive',
deleteName,
confirmationMessage = (deleteName) => (
<>
Type <strong>{deleteName}</strong> to confirm deletion:
</>
),
deleteButtonText = 'Delete',
cancelButtonText = 'Cancel',
onDelete,
isDeleting,
error,
testId,
textInputProps,
errorAlertProps,
onClose,
isOpen,
...props
}: DeleteModalProps) => {
const [confirmationText, setConfirmationText] = React.useState('');
const confirmed = deleteVariant === 'extra-destructive' ? confirmationText.trim() === deleteName : true;

React.useEffect(() => {
if (!isOpen) {
setConfirmationText('');
}
}, [isOpen]);

return (
<Modal variant="small" onClose={onClose} isOpen={isOpen} data-testid={testId || 'delete-modal'} {...props}>
<ModalHeader title={title} titleIconVariant={deleteVariant !== 'easily-recoverable' ? 'warning' : undefined} />
<ModalBody>
<Stack hasGutter>
<StackItem>{children}</StackItem>
{deleteVariant === 'extra-destructive' && (
<StackItem>
<Flex direction={{ default: 'column' }} spaceItems={{ default: 'spaceItemsSm' }}>
<FlexItem>{confirmationMessage(deleteName)}</FlexItem>
<TextInput
id={textInputProps?.id ?? 'delete-modal-input'}
data-testid={textInputProps?.id ?? 'delete-modal-input'}
aria-label={textInputProps?.['aria-label'] ?? 'Delete modal input'}
value={confirmationText}
onChange={(_e, value) => setConfirmationText(value)}
onKeyDown={(event) => {
if (event.key === 'Enter' && confirmed && !isDeleting) {
event.preventDefault();
onDelete();
}
}}
/>
</Flex>
</StackItem>
)}
{error && (
<StackItem>
<Alert
data-testid={errorAlertProps?.id ?? 'delete-modal-error-message-alert'}
title={errorAlertProps?.title ?? `Error deleting ${deleteName}`}
isInline
variant="danger"
>
{error.message}
</Alert>
</StackItem>
)}
</Stack>
</ModalBody>
<ModalFooter>
<Button
variant={deleteVariant !== 'easily-recoverable' ? 'danger' : 'primary'}
isDisabled={isDeleting || !confirmed}
isLoading={isDeleting}
onClick={() => onDelete()}
>
{deleteButtonText}
</Button>
<Button variant="secondary" onClick={onClose}>
{cancelButtonText}
</Button>
</ModalFooter>
</Modal>
);
};

```
Loading

0 comments on commit 5f3639c

Please sign in to comment.