feat: abhinavchadaga/reusable-popup-prompt (#148)
* feat: some work on popup prompt * feat: add some stuff * feat: reusable prompt component Takes a title, description, and button children. * fix: pr feedback * fix: import ReactElement
This commit is contained in:
105
src/stories/components/Prompt.stories.tsx
Normal file
105
src/stories/components/Prompt.stories.tsx
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { Button } from '@views/components/common/Button/Button';
|
||||||
|
import type { PromptDialogProps } from '@views/components/common/Prompt/Prompt';
|
||||||
|
import PromptDialog from '@views/components/common/Prompt/Prompt';
|
||||||
|
import Text from '@views/components/common/Text/Text';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Components/Common/Prompt',
|
||||||
|
component: PromptDialog,
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
},
|
||||||
|
tags: ['autodocs'],
|
||||||
|
argTypes: {
|
||||||
|
isOpen: { control: 'boolean' },
|
||||||
|
title: { control: 'object' },
|
||||||
|
content: { control: 'object' },
|
||||||
|
children: { control: 'object' },
|
||||||
|
},
|
||||||
|
} satisfies Meta<typeof PromptDialog>;
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
const PromptDialogWithButton = ({ children, ...args }: PromptDialogProps) => {
|
||||||
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
|
const handleOpen = () => setIsOpen(true);
|
||||||
|
const handleClose = () => setIsOpen(false);
|
||||||
|
const { title, content } = args;
|
||||||
|
|
||||||
|
const childrenWithHandleClose: React.ReactElement[] = children.map(child => {
|
||||||
|
if (child.type === Button) {
|
||||||
|
return React.cloneElement(child, { onClick: () => handleClose() } as React.HTMLAttributes<HTMLElement>);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='h-screen w-screen flex items-center justify-center'>
|
||||||
|
<Button variant='filled' color='ut-burntorange' onClick={handleOpen}>
|
||||||
|
Open Prompt
|
||||||
|
</Button>
|
||||||
|
<PromptDialog {...args} isOpen={isOpen} onClose={handleClose} title={title} content={content}>
|
||||||
|
{childrenWithHandleClose}
|
||||||
|
</PromptDialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AreYouSure: StoryObj<PromptDialogProps> = {
|
||||||
|
render: args => <PromptDialogWithButton {...args} />,
|
||||||
|
args: {
|
||||||
|
title: <Text variant='h2'>Are you sure?</Text>,
|
||||||
|
content: (
|
||||||
|
<Text variant='p'>
|
||||||
|
Deleting Main Schedule is permanent and will remove all added courses and schedules.
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
<Button key='yes' variant='single' color='ut-burntorange'>
|
||||||
|
Yes
|
||||||
|
</Button>,
|
||||||
|
<Button key='no' variant='single' color='ut-black'>
|
||||||
|
No
|
||||||
|
</Button>,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const YouHave10ActiveSchedules: StoryObj<PromptDialogProps> = {
|
||||||
|
render: args => <PromptDialogWithButton {...args} />,
|
||||||
|
args: {
|
||||||
|
title: <Text variant='h2'>You have 10 active schedules!</Text>,
|
||||||
|
content: (
|
||||||
|
<Text variant='p'>
|
||||||
|
To encourage organization, please consider removing some unused schedules you may have.
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
<Button key='yes' variant='single' color='ut-black'>
|
||||||
|
I understand
|
||||||
|
</Button>,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WelcomeToUTRPV2: StoryObj<PromptDialogProps> = {
|
||||||
|
render: args => <PromptDialogWithButton {...args} />,
|
||||||
|
args: {
|
||||||
|
title: <Text variant='h2'>Welcome to UTRP V2!</Text>,
|
||||||
|
content: (
|
||||||
|
<Text variant='p'>
|
||||||
|
You may have already began planning your Summer or Fall schedule. To migrate your courses into v2.0
|
||||||
|
please select “Migrate,” or start fresh.
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
<Button key='migrate' variant='single' color='ut-black'>
|
||||||
|
Don't Migrate
|
||||||
|
</Button>,
|
||||||
|
<Button key='start-fresh' variant='single' color='ut-burntorange'>
|
||||||
|
Migrate
|
||||||
|
</Button>,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
65
src/views/components/common/Prompt/Prompt.tsx
Normal file
65
src/views/components/common/Prompt/Prompt.tsx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { Dialog, Transition } from '@headlessui/react';
|
||||||
|
import type { ReactElement } from 'react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import type { Button } from '../Button/Button';
|
||||||
|
import type Text from '../Text/Text';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Props for the PromptDialog component.
|
||||||
|
*/
|
||||||
|
export interface PromptDialogProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
title: ReactElement<typeof Text>;
|
||||||
|
content: ReactElement<typeof Text>;
|
||||||
|
children?: ReactElement<typeof Button>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reusable dialog component that can be used to display a prompt to the user.
|
||||||
|
* @param {PromptDialogProps} props.isOpen - Whether the dialog is open or not.
|
||||||
|
* @param {Function} props.onClose - A function to call when the user exits the dialog.
|
||||||
|
* @param {React.ReactElement<typeof Text>} props.title - The title of the dialog.
|
||||||
|
* @param {React.ReactElement<typeof Text>} props.content - The content of the dialog.
|
||||||
|
* @param {React.ReactElement<typeof Button>[]} props.children - The buttons to display in the dialog.
|
||||||
|
*/
|
||||||
|
function PromptDialog({ isOpen, onClose, title, content, children }: PromptDialogProps) {
|
||||||
|
return (
|
||||||
|
<Transition appear show={isOpen} as={React.Fragment}>
|
||||||
|
<Dialog as='div' onClose={onClose} className='relative z-50'>
|
||||||
|
<Transition.Child
|
||||||
|
as={React.Fragment}
|
||||||
|
enter='ease-out duration-200'
|
||||||
|
enterFrom='opacity-0'
|
||||||
|
enterTo='opacity-100'
|
||||||
|
leave='ease-in duration-200'
|
||||||
|
leaveFrom='opacity-100'
|
||||||
|
leaveTo='opacity-0'
|
||||||
|
>
|
||||||
|
<div className='fixed inset-0 bg-black bg-opacity-50' aria-hidden='true' />
|
||||||
|
</Transition.Child>
|
||||||
|
|
||||||
|
<Transition.Child
|
||||||
|
as={React.Fragment}
|
||||||
|
enter='ease-out duration-200'
|
||||||
|
enterFrom='opacity-0 scale-95'
|
||||||
|
enterTo='opacity-100 scale-100'
|
||||||
|
leave='ease-in duration-200'
|
||||||
|
leaveFrom='opacity-100 scale-100'
|
||||||
|
leaveTo='opacity-0 scale-95'
|
||||||
|
>
|
||||||
|
<div className='fixed inset-0 w-screen flex items-center justify-center'>
|
||||||
|
<Dialog.Panel className='h-[200] w-[431px] flex flex-col rounded bg-white p-6'>
|
||||||
|
<Dialog.Title className='mb-[10px]'>{title}</Dialog.Title>
|
||||||
|
<Dialog.Description className='mb-[13px]'>{content}</Dialog.Description>
|
||||||
|
<div className='flex items-center justify-end gap-2'>{children}</div>
|
||||||
|
</Dialog.Panel>
|
||||||
|
</div>
|
||||||
|
</Transition.Child>
|
||||||
|
</Dialog>
|
||||||
|
</Transition>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PromptDialog;
|
||||||
Reference in New Issue
Block a user