From e536a52815a9f19f2392b8f61be12f97cfd8cc3a Mon Sep 17 00:00:00 2001 From: Matthew Warman Date: Tue, 4 Mar 2025 13:41:45 -0500 Subject: [PATCH 1/5] #57 Dialog compound pattern --- src/common/components/Dialog/Dialog.tsx | 290 +++++++++++++++--- src/common/components/Dialog/DialogButton.tsx | 53 ---- .../components/Dialog/DialogButtons.tsx | 32 -- .../components/Dialog/DialogContent.tsx | 31 -- .../components/Dialog/DialogHeading.tsx | 31 -- .../Dialog/__tests__/DiaglogButtons.test.tsx | 47 --- .../Dialog/__tests__/Dialog.test.tsx | 224 +++++++++++--- .../Dialog/__tests__/DialogButton.test.tsx | 53 ---- .../Dialog/__tests__/DialogContent.test.tsx | 34 -- .../Dialog/__tests__/DialogHeading.test.tsx | 34 -- .../components/Delete/TaskDeleteDialog.tsx | 116 ++++--- .../__tests__/TaskDeleteDialog.test.tsx | 78 ++--- .../Tasks/components/TaskDetailLayout.tsx | 75 +---- .../__tests__/TaskDetailLayout.test.tsx | 81 +---- 14 files changed, 569 insertions(+), 610 deletions(-) delete mode 100644 src/common/components/Dialog/DialogButton.tsx delete mode 100644 src/common/components/Dialog/DialogButtons.tsx delete mode 100644 src/common/components/Dialog/DialogContent.tsx delete mode 100644 src/common/components/Dialog/DialogHeading.tsx delete mode 100644 src/common/components/Dialog/__tests__/DiaglogButtons.test.tsx delete mode 100644 src/common/components/Dialog/__tests__/DialogButton.test.tsx delete mode 100644 src/common/components/Dialog/__tests__/DialogContent.test.tsx delete mode 100644 src/common/components/Dialog/__tests__/DialogHeading.test.tsx diff --git a/src/common/components/Dialog/Dialog.tsx b/src/common/components/Dialog/Dialog.tsx index a1e7b7c..60afd84 100644 --- a/src/common/components/Dialog/Dialog.tsx +++ b/src/common/components/Dialog/Dialog.tsx @@ -1,63 +1,114 @@ -import { PropsWithChildren, useEffect, useState } from 'react'; +import { createContext, PropsWithChildren, ReactNode, useContext, useState } from 'react'; +import noop from 'lodash/noop'; +import { cva, VariantProps } from 'class-variance-authority'; -import { cn } from 'common/utils/css'; import { BaseComponentProps } from 'common/utils/types'; +import { cn } from 'common/utils/css'; import Backdrop from '../Backdrop/Backdrop'; +import Divider, { DividerProps } from '../Divider/Divider'; +import { default as CommonButton, ButtonProps as CommonButtonProps } from '../Button/Button'; + +/** + * Defines the properties of the `DialogContext` value. + */ +type DialogContextValue = { + isHidden: boolean; + setIsHidden: (isHidden: boolean) => void; +}; + +/** + * The `DialogContext` instance. + */ +const DialogContext = createContext({ + isHidden: true, + setIsHidden: noop, +}); /** - * Properties for the `Dialog` component. - * @param {boolean} [isOpen] - Indicates if the Dialog should be displayed. - * @param {function} [onClose] - A function called when the Dialog closes. - * @see {@link BaseComponentProps} - * @see {@link PropsWithChildren} + * Defines the properties of the `Dialog` render prop function context. + * @param close - Close the dialog. */ -export interface DialogProps extends BaseComponentProps, PropsWithChildren { - isOpen?: boolean; - onClose?: () => void | Promise; +type DialogRenderFnContext = { + close: () => void; +}; + +/** + * The `Dialog` render prop function signature. + */ +type DialogRenderFn = (ctx: DialogRenderFnContext) => ReactNode; + +export interface DialogProps extends BaseComponentProps { + children: ReactNode | DialogRenderFn; } /** * A `Dialog` is a modal window that displays on top of the main content, * typically asking the user to take an action or confirm a decision. - * @param {DialogProps} props - Component properties. - * @returns {JSX.Element} JSX */ -const Dialog = ({ +const Dialog = ({ children, className, testId = 'dialog' }: DialogProps): JSX.Element => { + const [isHidden, setIsHidden] = useState(true); + + const close = () => { + setIsHidden(true); + }; + + return ( +
+ + {typeof children === 'function' ? children({ close }) : children} + +
+ ); +}; + +/** + * The `Trigger` renders a clickable element used to open a `Dialog`. There + * should be 1 `Trigger` within a `Dialog`. + */ +const Trigger = ({ children, className, - isOpen = false, - onClose, - testId = 'dialog', -}: DialogProps): JSX.Element => { - const [isDialogOpen, setIsDialogOpen] = useState(isOpen); - - useEffect(() => { - setIsDialogOpen(isOpen); - }, [isOpen]); - - const closeDialog = (): void => { - setIsDialogOpen(false); - onClose?.(); - }; + testId = 'dialog-trigger', +}: BaseComponentProps & PropsWithChildren): JSX.Element => { + const { isHidden, setIsHidden } = useContext(DialogContext); - const handleBackdropClick = (): void => { - closeDialog(); - }; + return ( +
setIsHidden(!isHidden)} + data-testid={testId} + > + {children} +
+ ); +}; +Dialog.Trigger = Trigger; - const handleDialogClick = (e: React.MouseEvent): void => { - e.stopPropagation(); - }; +/** + * The `Content` component wraps the contents of a `Dialog` including the + * header, body, and footer. There should be 1 `Content` within a `Dialog`. + */ +const Content = ({ + children, + className, + testId = 'dialog-content', +}: BaseComponentProps & PropsWithChildren): JSX.Element => { + const { isHidden, setIsHidden } = useContext(DialogContext); return ( -
+
setIsHidden(true)} testId={`${testId}-backdrop`} >
e.stopPropagation()} + data-testid={`${testId}-content`} > {children}
@@ -65,5 +116,170 @@ const Dialog = ({
); }; +Dialog.Content = Content; + +/** + * The `Header` is a block within a dialog `Content` and may contain a `Title` + * and `Subtitle`. + */ +const Header = ({ + children, + className, + testId = 'dialog-header', +}: BaseComponentProps & PropsWithChildren): JSX.Element => { + return ( +
+ {children} +
+ ); +}; +Dialog.Header = Header; + +/** + * The `Body` is a block which encloses the main content of the `Dialog`. + */ +const Body = ({ + children, + className, + testId = 'dialog-body', +}: BaseComponentProps & PropsWithChildren): JSX.Element => { + return ( +
+ {children} +
+ ); +}; +Dialog.Body = Body; + +/** + * The `Footer` is a block within a dialog `Content` and contains values such as + * a `ButtonBar`, `Button`, or any components which are located at the bottom of + * the `Dialog`. + */ +const Footer = ({ + children, + className, + testId = 'dialog-footer', +}: BaseComponentProps & PropsWithChildren): JSX.Element => { + return ( +
+ {children} +
+ ); +}; +Dialog.Footer = Footer; + +/** + * A `Title` for the `Dialog`. The title is optional. When present, the `Title` + * is typically located within the dialog `Header`. + */ +const Title = ({ + children, + className, + testId = 'dialog-title', +}: BaseComponentProps & PropsWithChildren): JSX.Element => { + return ( +
+ {children} +
+ ); +}; +Dialog.Title = Title; + +/** + * A `Subtitle` for the `Dialog`. The subtitle is optional. When present, the + * `Subtitle` is typically located within the dialog `Header`, immediately after + * the `Title`. + */ +const Subtitle = ({ + children, + className, + testId = 'dialog-subtitle', +}: BaseComponentProps & PropsWithChildren): JSX.Element => { + return ( +
+ {children} +
+ ); +}; +Dialog.Subtitle = Subtitle; + +/** + * A `ButtonBar` organizes one to many dialog `Button` components in a horizontal + * row. The buttons are right justified within the bar. + */ +const ButtonBar = ({ + children, + className, + testId = 'dialog-button-bar', +}: BaseComponentProps & PropsWithChildren): JSX.Element => { + return ( +
+ {children} +
+ ); +}; +Dialog.ButtonBar = ButtonBar; + +/** + * Define the `Button` component base and variant styles. + */ +const buttonVariants = cva('', { + variants: { + variant: { + danger: 'font-bold text-red-600', + primary: 'font-bold text-blue-600 dark:text-blue-400', + secondary: '', + }, + }, + defaultVariants: { variant: 'secondary' }, +}); + +/** + * The variant attributes of the `Button` component. + */ +type ButtonVariants = VariantProps; + +/** + * Properties for the `Button` component. + */ +export interface ButtonProps extends Omit, ButtonVariants {} + +/** + * A dialog `Button` is a button which is styles for presentation within a + * `Dialog`. + */ +const Button = ({ + className, + variant = 'secondary', + testId = 'dialog-footer', + ...props +}: ButtonProps): JSX.Element => { + return ( + + ); +}; +Dialog.Button = Button; + +/** + * The `Separator` component renders a horizontal divider. + * This is useful to organize and separate content. + */ +const Separator = ({ className, testId = 'dialog-separator' }: DividerProps): JSX.Element => { + return ; +}; +Dialog.Separator = Separator; export default Dialog; diff --git a/src/common/components/Dialog/DialogButton.tsx b/src/common/components/Dialog/DialogButton.tsx deleted file mode 100644 index 953661c..0000000 --- a/src/common/components/Dialog/DialogButton.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { cva, VariantProps } from 'class-variance-authority'; - -import { cn } from 'common/utils/css'; -import Button, { ButtonProps } from '../Button/Button'; - -/** - * Define the component base and variant styles. - */ -const variants = cva('', { - variants: { - variant: { - danger: 'font-bold text-red-600', - primary: 'font-bold text-blue-600 dark:text-blue-400', - secondary: '', - }, - }, - defaultVariants: { variant: 'secondary' }, -}); - -/** - * The variant attributes of the DialogButton component. - */ -type DialogButtonVariants = VariantProps; - -/** - * Properties for the `DialogButton` component. - */ -export interface DialogButtonProps extends Omit, DialogButtonVariants {} - -/** - * The `DialogButton` is a type of `Button` specifically styled for use - * within a `Dialog`. - * @param {DialogButtonProps} props - Component properties. - * @returns {JSX.Element} JSX - */ -const DialogButton = ({ - className, - variant, - testId = 'dialog-button', - ...buttonProps -}: DialogButtonProps): JSX.Element => { - return ( - - + {task && ( + + )} + {task && } + + + + Are you sure? + Deletion cannot be undone. + + + Delete task 18 Use React. + + + + + Cancel + Delete + + + ), }; -export const Info: Story = { +export const LoremIpsum: Story = { render: (args) => ( - Bob Smith - -
Manager of Stories
-
Raleigh, NC
-
+1 123-456-7890
-
- - Close - + {({ close }) => ( + <> + + + + + + Amet at duis deserunt ad ad ornare. + + Nam cupidatat duis dolore magna aute posuere. Esse arcu morbi quis consequat + facilisis lorem pulvinar. + + + +
+ Cupidatat excepteur mi magna nisi sint. Sint officia donec duis egestas cupidatat + quam consectetur fermentum. Egestas incididunt esse magna ex occaecat nunc arcu. + Adipiscing sit laborum adipiscing aliqua tempor amet. Tempus laboris lorem deserunt + aute sint ullamco magna pariatur. Ipsum elit justo cupidatat ea a exercitation + pariatur. +
+
+ Mi tempor incididunt duis ullamco morbi minim. Proin condimentum adipiscing officia + et ipsum nunc. Est et est quam labore sit velit. Eiusmod laboris facilisis aliquip + nulla aliqua occaecat quam mi. Ligula aute fermentum tempus dolore qui dolore culpa. + Quis quam ligula nulla exercitation exercitation nibh. Bibendum laboris mollit vitae + sunt elit aenean a non. Neque dui proident cupidatat proident morbi sunt sapien. +
+
+ + + + close()}>Cancel + close()} variant="primary"> + Accept + + + +
+ + )}
), }; diff --git a/src/common/components/Dialog/__stories__/DialogButton.stories.tsx b/src/common/components/Dialog/__stories__/DialogButton.stories.tsx deleted file mode 100644 index 8253281..0000000 --- a/src/common/components/Dialog/__stories__/DialogButton.stories.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import DialogButton from '../DialogButton'; - -const meta = { - title: 'Common/Dialog/DialogButton', - component: DialogButton, - parameters: { - layout: 'centered', - }, - tags: ['autodocs'], - argTypes: { - children: { description: 'The content.' }, - className: { description: 'Additional CSS classes.' }, - testId: { description: 'The test identifier.' }, - variant: { description: 'The variant.' }, - }, - args: { - children: 'Label', - variant: 'primary', - }, -} satisfies Meta; - -export default meta; - -type Story = StoryObj; - -export const Default: Story = {}; - -export const Primary: Story = { - args: { - children: 'Primary', - }, -}; - -export const Secondary: Story = { - args: { - children: 'Secondary', - variant: 'secondary', - }, -}; - -export const Danger: Story = { - args: { - children: 'Danger', - variant: 'danger', - }, -}; diff --git a/src/common/components/Dialog/__stories__/DialogHeading.stories.tsx b/src/common/components/Dialog/__stories__/DialogHeading.stories.tsx deleted file mode 100644 index 84cbd29..0000000 --- a/src/common/components/Dialog/__stories__/DialogHeading.stories.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import DialogHeading from '../DialogHeading'; - -const meta = { - title: 'Common/Dialog/DialogHeading', - component: DialogHeading, - parameters: { - layout: 'centered', - }, - tags: ['autodocs'], - argTypes: { - children: { description: 'The content.' }, - className: { description: 'Additional CSS classes.' }, - testId: { description: 'The test identifier.' }, - }, - args: { - children: 'Are you sure?', - }, -} satisfies Meta; - -export default meta; - -type Story = StoryObj; - -export const Confirm: Story = {}; - -export const Info: Story = { - args: { - children: ( - <> - About: Widgets - - ), - }, -}; From 880aa67f3318fdf9364fa0524f61d53ee726a19b Mon Sep 17 00:00:00 2001 From: Matthew Warman Date: Tue, 4 Mar 2025 14:39:36 -0500 Subject: [PATCH 3/5] #57 DialogComponents page --- src/common/components/Router/Router.tsx | 5 + src/pages/Components/ComponentsPage.tsx | 3 + .../components/DialogComponents.tsx | 229 ++++++++++++++++++ .../__tests__/DialogComponents.test.tsx | 32 +++ 4 files changed, 269 insertions(+) create mode 100644 src/pages/Components/components/DialogComponents.tsx create mode 100644 src/pages/Components/components/__tests__/DialogComponents.test.tsx diff --git a/src/common/components/Router/Router.tsx b/src/common/components/Router/Router.tsx index ab23907..c9abb73 100644 --- a/src/common/components/Router/Router.tsx +++ b/src/common/components/Router/Router.tsx @@ -20,6 +20,7 @@ import AvatarComponents from 'pages/Components/components/AvatarComponents'; import BadgeComponents from 'pages/Components/components/BadgeComponents'; import ButtonComponents from 'pages/Components/components/ButtonComponents'; import CardComponents from 'pages/Components/components/CardComponents'; +import DialogComponents from 'pages/Components/components/DialogComponents'; import DropdownComponents from 'pages/Components/components/DropdownComponents'; import SearchInputComponents from 'pages/Components/components/SearchInputComponents'; import TabsComponents from 'pages/Components/components/TabsComponents'; @@ -107,6 +108,10 @@ export const routes: RouteObject[] = [ path: 'card', element: , }, + { + path: 'dialog', + element: , + }, { path: 'dropdown', element: , diff --git a/src/pages/Components/ComponentsPage.tsx b/src/pages/Components/ComponentsPage.tsx index be6d611..dfbe49b 100644 --- a/src/pages/Components/ComponentsPage.tsx +++ b/src/pages/Components/ComponentsPage.tsx @@ -35,6 +35,9 @@ const ComponentsPage = (): JSX.Element => { Card + + Dialog + Dropdown diff --git a/src/pages/Components/components/DialogComponents.tsx b/src/pages/Components/components/DialogComponents.tsx new file mode 100644 index 0000000..9048852 --- /dev/null +++ b/src/pages/Components/components/DialogComponents.tsx @@ -0,0 +1,229 @@ +import { createColumnHelper } from '@tanstack/react-table'; + +import { BaseComponentProps } from 'common/utils/types'; +import { ComponentProperty } from '../model/components'; +import Table from 'common/components/Table/Table'; +import CodeSnippet from 'common/components/Text/CodeSnippet'; +import Heading from 'common/components/Text/Heading'; +import Dialog from 'common/components/Dialog/Dialog'; +import Button from 'common/components/Button/Button'; + +/** + * The `DialogComponents` React component renders a set of examples illustrating + * the use of the `Dialog` component. + */ +const DialogComponents = ({ + className, + testId = 'components-dialog', +}: BaseComponentProps): JSX.Element => { + const data: ComponentProperty[] = [ + { + name: 'children', + description: 'The content to be displayed. May be a ReactNode or a render prop function.', + }, + { + name: 'className', + description: 'Optional. Additional CSS class names.', + }, + { + name: 'testId', + description: 'Optional. Identifier for testing.', + }, + ]; + const columnHelper = createColumnHelper(); + const columns = [ + columnHelper.accessor('name', { + cell: (info) => ( + {info.getValue()} + ), + header: () => 'Name', + }), + columnHelper.accessor('description', { + cell: (info) => info.renderValue(), + header: () => 'Description', + }), + ]; + + return ( +
+ + Dialog Component + + +
+ The Dialog is a modal window that displays on + top of the main content, typically asking the user to take an action or confirm a decision. + Compose a Dialog using combinations of: Trigger, Content,{' '} + Header, Title, Subtitle, Body,{' '} + Footer, ButtonBar, and Button. +
+ +
+ + Properties + + data={data} columns={columns} /> +
+ + Examples + + Dialog with ReactNode children +
+
+ + + + + + + Are you sure? + Deletion is permanent and cannot be undone. + + + Delete issue:{' '} + + 987 Use dialog to confirm task delete + + + + + + Cancel + Delete + + + + +
+ + + + + + + Are you sure? + Deletion is permanent and cannot be undone. + + + Delete issue:{' '} + + 987 Use dialog to confirm task delete + + + + + + Cancel + Delete + + + +`} + /> +
+ + Dialog with render props +
+
+ + {({ close }) => ( + <> + + + + + + Terms and Conditions + + Nam cupidatat duis dolore magna aute posuere. Esse arcu morbi quis consequat + facilisis lorem pulvinar. + + + +
+ Dolor proident aliqua ornare consectetur. Sapien est do quam labore qos + veniam. Aute sint sunt commodo ea e. Ligula anim amet nulla morbi nulla. + Laborum duis sunt exercitation justo. Quis aliquip posuere incididunt et + consectetur. Nostrud incididunt laborum pulvinar ea ut ex at. +
+
+ Pariatur anim veniam morbi dui consectetur officia. Occaecat e dolore mi enim + morbi aliquip e. Condimentum adipiscing sunt commodo proident enim laborum mi. + Dui labore posuere ex exercitation justo morbi. A consequat pulvinar aliqua do + duis ligula. Ullamco enim condimentum pariatur dolor nulla ad quam. Nam + egestas laboris laoreet et ipsum deserunt ligula. +
+
+ + + + close()} testId="dialog-button-decline"> + Decline + + close()} + testId="dialog-button-accept" + > + Accept + + + +
+ + )} +
+
+ + {({ close }) => ( + <> + + + + + + Terms and Conditions + + Nam cupidatat duis dolore magna aute posuere. Esse arcu morbi quis consequat + facilisis lorem pulvinar. + + + +
+ Dolor proident aliqua ornare consectetur. Sapien est do quam labore qos + veniam. Aute sint sunt commodo ea e. Ligula anim amet nulla morbi nulla. + Laborum duis sunt exercitation justo. Quis aliquip posuere incididunt et + consectetur. Nostrud incididunt laborum pulvinar ea ut ex at. +
+
+ Pariatur anim veniam morbi dui consectetur officia. Occaecat e dolore mi enim + morbi aliquip e. Condimentum adipiscing sunt commodo proident enim laborum mi. + Dui labore posuere ex exercitation justo morbi. A consequat pulvinar aliqua do + duis ligula. Ullamco enim condimentum pariatur dolor nulla ad quam. Nam + egestas laboris laoreet et ipsum deserunt ligula. +
+
+ + + + close()}>Decline + close()}> + Accept + + + +
+ + )} +`} + /> +
+
+ ); +}; + +export default DialogComponents; diff --git a/src/pages/Components/components/__tests__/DialogComponents.test.tsx b/src/pages/Components/components/__tests__/DialogComponents.test.tsx new file mode 100644 index 0000000..2130aa5 --- /dev/null +++ b/src/pages/Components/components/__tests__/DialogComponents.test.tsx @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'vitest'; + +import { render, screen } from 'test/test-utils'; + +import DialogComponents from '../DialogComponents'; +import userEvent from '@testing-library/user-event'; + +describe('DialogComponents', () => { + it('should render successfully', async () => { + // ARRANGE + render(); + await screen.findByTestId('components-dialog'); + + // ASSERT + expect(screen.getByTestId('components-dialog')).toBeDefined(); + }); + + it('should click items', async () => { + // ARRANGE + const user = userEvent.setup(); + render(); + await screen.findByTestId('components-dialog'); + + // ACT + /* exercising click handlers; normally would assert handler function called */ + await user.click(screen.getByTestId('dialog-button-decline')); + await user.click(screen.getByTestId('dialog-button-accept')); + + // ASSERT + expect(screen.getByTestId('components-dialog')).toBeDefined(); + }); +}); From bb8fce45d9cc68e3f30b7fe4571a8779ecd9ca0e Mon Sep 17 00:00:00 2001 From: Matthew Warman Date: Wed, 5 Mar 2025 06:15:12 -0500 Subject: [PATCH 4/5] #57 pr fixes --- src/common/components/Dialog/Dialog.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/components/Dialog/Dialog.tsx b/src/common/components/Dialog/Dialog.tsx index f40b735..f3e04b6 100644 --- a/src/common/components/Dialog/Dialog.tsx +++ b/src/common/components/Dialog/Dialog.tsx @@ -25,8 +25,8 @@ const DialogContext = createContext({ }); /** - * Defines the properties of the `Dialog` render prop function context. - * @param close - Close the dialog. + * Defines the properties of the `Dialog` render prop function context object. + * @param close - Closes the dialog. */ type DialogRenderFnContext = { close: () => void; @@ -258,7 +258,7 @@ export interface ButtonProps extends Omit, ButtonV const Button = ({ className, variant = 'secondary', - testId = 'dialog-footer', + testId = 'dialog-button', ...props }: ButtonProps): JSX.Element => { return ( From 20c2b18a425b87d9fe95e77309c50b0bd867717d Mon Sep 17 00:00:00 2001 From: Matthew Warman Date: Wed, 5 Mar 2025 06:26:00 -0500 Subject: [PATCH 5/5] #57 pr fixes --- .../components/Delete/TaskDeleteDialog.tsx | 11 ++++----- .../__tests__/TaskDeleteDialog.test.tsx | 24 +++++++++++++++---- .../Tasks/components/TaskDetailLayout.tsx | 6 ++++- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/pages/Tasks/components/Delete/TaskDeleteDialog.tsx b/src/pages/Tasks/components/Delete/TaskDeleteDialog.tsx index 70bcc7e..2ce606c 100644 --- a/src/pages/Tasks/components/Delete/TaskDeleteDialog.tsx +++ b/src/pages/Tasks/components/Delete/TaskDeleteDialog.tsx @@ -1,18 +1,18 @@ -import { Task } from 'pages/Tasks/api/useGetUserTasks'; +import { PropsWithChildren } from 'react'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { BaseComponentProps } from 'common/utils/types'; +import { Task } from 'pages/Tasks/api/useGetUserTasks'; import { useDeleteTask } from 'pages/Tasks/api/useDeleteTask'; import { useToasts } from 'common/hooks/useToasts'; import Dialog from 'common/components/Dialog/Dialog'; -import FAIcon from 'common/components/Icon/FAIcon'; import ErrorAlert from 'common/components/Alert/ErrorAlert'; /** * Properties for the `TaskDeleteDialog` component. */ -interface TaskDeleteDialogProps extends BaseComponentProps { +interface TaskDeleteDialogProps extends BaseComponentProps, PropsWithChildren { task: Task; } @@ -21,6 +21,7 @@ interface TaskDeleteDialogProps extends BaseComponentProps { * of a `Task`. */ const TaskDeleteDialog = ({ + children, className, task, testId = 'dialog-task-delete', @@ -52,9 +53,7 @@ const TaskDeleteDialog = ({ {({ close }) => ( <> - - - + {children} Are you sure? diff --git a/src/pages/Tasks/components/Delete/__tests__/TaskDeleteDialog.test.tsx b/src/pages/Tasks/components/Delete/__tests__/TaskDeleteDialog.test.tsx index 495b1cf..1c1516e 100644 --- a/src/pages/Tasks/components/Delete/__tests__/TaskDeleteDialog.test.tsx +++ b/src/pages/Tasks/components/Delete/__tests__/TaskDeleteDialog.test.tsx @@ -12,7 +12,11 @@ describe('TaskDeleteDialog', () => { it('should render successfully', async () => { // ARRANGE const task = todosFixture[0]; - render(); + render( + + Open + , + ); await screen.findByTestId('dialog'); // ASSERT @@ -23,7 +27,11 @@ describe('TaskDeleteDialog', () => { // ARRANGE const user = userEvent.setup(); const task = todosFixture[0]; - render(); + render( + + Open + , + ); await screen.findByTestId('dialog'); // ACT @@ -50,7 +58,11 @@ describe('TaskDeleteDialog', () => { toasts: [], } as unknown as ToastsContextValue); - render(); + render( + + Open + , + ); await screen.findByTestId('dialog'); // ACT @@ -65,7 +77,11 @@ describe('TaskDeleteDialog', () => { const user = userEvent.setup(); const task = { ...todosFixture[0], id: 999999 }; - render(); + render( + + Open + , + ); await screen.findByTestId('dialog'); // ACT diff --git a/src/pages/Tasks/components/TaskDetailLayout.tsx b/src/pages/Tasks/components/TaskDetailLayout.tsx index c0602d0..e54be11 100644 --- a/src/pages/Tasks/components/TaskDetailLayout.tsx +++ b/src/pages/Tasks/components/TaskDetailLayout.tsx @@ -63,7 +63,11 @@ const TaskDetailLayout = ({ )} - {task && } + {task && ( + + + + )}