-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
1 parent
7760e3a
commit 44af9e1
Showing
2 changed files
with
170 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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>, | ||
], | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |