Skip to content

Commit

Permalink
(PC-34260)[PRO] feat: Add title and footer props to Dialog and preven…
Browse files Browse the repository at this point in the history
…t closing when clicking on an eleemnt on top of the content.
  • Loading branch information
gmeigniez-pass committed Feb 7, 2025
1 parent 374b04a commit 0f2f53d
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 7 deletions.
19 changes: 18 additions & 1 deletion pro/src/ui-kit/DialogBuilder/DialogBuilder.module.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@use "styles/mixins/_rem.scss" as rem;
@use "styles/variables/_z-index.scss" as zIndex;
@use "styles/mixins/_size.scss" as size;
@use "styles/mixins/_fonts.scss" as fonts;

$drawer-width: rem.torem(684px);
$animation-duration: 0.15s;
Expand All @@ -26,6 +27,22 @@ $animation-timing-function: ease-in-out;
}
}

.dialog-builder-title {
@include fonts.title3;
}

.dialog-builder-section {
min-height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}

.dialog-builder-footer {
border-top: rem.torem(1px) solid var(--color-grey-medium);
padding-top: rem.torem(24px);
}

.dialog-builder-overlay {
background-color: rgb(0 0 0 / 33%);
display: grid;
Expand Down Expand Up @@ -61,10 +78,10 @@ $animation-timing-function: ease-in-out;

.dialog-builder-overlay-drawer > .dialog-builder-content {
border-radius: 0;
height: 100vh;
width: $drawer-width;
max-width: 100vw;
margin: 0;
height: 100%;

@media not (prefers-reduced-motion) {
&[data-state="open"] {
Expand Down
76 changes: 76 additions & 0 deletions pro/src/ui-kit/DialogBuilder/DialogBuilder.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as Dialog from '@radix-ui/react-dialog'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

import { Button } from 'ui-kit/Button/Button'
import { ButtonVariant } from 'ui-kit/Button/types'

import { DialogBuilder, DialogBuilderProps } from './DialogBuilder'

const defaultProps: DialogBuilderProps = {
children: <p>Dialog content</p>,
title: 'Dialog title',
trigger: <Button>Open the dialog</Button>,
footer: (
<div>
<Dialog.Close asChild>
<Button variant={ButtonVariant.SECONDARY}>Annuler</Button>
</Dialog.Close>
<Dialog.Close asChild>
<Button>Continuer</Button>
</Dialog.Close>
</div>
),
}

function renderDialogBuilder(props = defaultProps) {
return render(<DialogBuilder {...props}>{props.children}</DialogBuilder>)
}

describe('DialogBuilder', () => {
it('should open a dialog with a title and a footer', async () => {
renderDialogBuilder()

await userEvent.click(
screen.getByRole('button', { name: 'Open the dialog' })
)
expect(
screen.getByRole('heading', { name: 'Dialog title' })
).toBeInTheDocument()
expect(
screen.getByRole('button', { name: 'Continuer' })
).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'Annuler' })).toBeInTheDocument()
expect(
screen.getByRole('button', { name: 'Fermer la modale' })
).toBeInTheDocument()
})

it('should close the dialog when clicking the close button', async () => {
renderDialogBuilder()

await userEvent.click(
screen.getByRole('button', { name: 'Open the dialog' })
)

await userEvent.click(
screen.getByRole('button', { name: 'Fermer la modale' })
)
expect(
screen.queryByRole('heading', { name: 'Dialog title' })
).not.toBeInTheDocument()
})

it('should close the dialog when clicking outside the dialog', async () => {
renderDialogBuilder()

await userEvent.click(
screen.getByRole('button', { name: 'Open the dialog' })
)

await userEvent.click(screen.getByTestId('dialog-overlay'))
expect(
screen.queryByRole('heading', { name: 'Dialog title' })
).not.toBeInTheDocument()
})
})
82 changes: 79 additions & 3 deletions pro/src/ui-kit/DialogBuilder/DialogBuilder.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Dialog from '@radix-ui/react-dialog'
import type { StoryObj } from '@storybook/react'

import { Button } from 'ui-kit/Button/Button'
import { ButtonVariant } from 'ui-kit/Button/types'

import { DialogBuilder } from './DialogBuilder'

Expand All @@ -28,13 +29,88 @@ export const Drawer: StoryObj<typeof DialogBuilder> = {
args: {
variant: 'drawer',
trigger: <Button>Cliquez ici!</Button>,
title: 'Dialog title',
footer: (
<div style={{ display: 'flex', gap: '24px' }}>
<Dialog.Close asChild>
<Button variant={ButtonVariant.SECONDARY}>Annuler</Button>
</Dialog.Close>
<Dialog.Close asChild>
<Button>Continuer</Button>
</Dialog.Close>
</div>
),
children: (
<>
<Dialog.Title asChild>
<h1>Dialog title</h1>
</Dialog.Title>
<p>lorem ipsum dolor sit amet</p>
</>
),
},
}

export const DrawerWithLongContent: StoryObj<typeof DialogBuilder> = {
args: {
variant: 'drawer',
trigger: <Button>Cliquez ici!</Button>,
title: 'Dialog title',
footer: (
<div style={{ display: 'flex', gap: '1.5rem' }}>
<Dialog.Close asChild>
<Button variant={ButtonVariant.SECONDARY}>Annuler</Button>
</Dialog.Close>
<Dialog.Close asChild>
<Button>Continuer</Button>
</Dialog.Close>
</div>
),
children: (
<div style={{ padding: '1rem 0' }}>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
blandit blandit purus, at suscipit arcu euismod lacinia. Suspendisse
sed porttitor arcu, nec consequat sem. Integer eget tincidunt lacus.
Nam luctus varius est a aliquam. Maecenas congue turpis a libero
pellentesque commodo a in nulla. Aliquam erat volutpat. Morbi non arcu
elit. Maecenas dictum at nunc nec dapibus. Donec sollicitudin nisi sed
cursus lobortis. Ut arcu odio, varius at elit a, pellentesque
scelerisque elit. Suspendisse potenti. Nullam malesuada dolor non
iaculis euismod. Praesent vel enim ac eros consectetur tempus. Donec
eget nibh nisl.
</p>
<p>
Praesent tempor elementum enim vitae dapibus. Nunc consectetur finibus
orci, vel venenatis nisi commodo lacinia. Donec suscipit nibh non
pharetra porttitor. In nec arcu ultrices, consequat ex vel,
scelerisque eros. Integer porttitor ipsum et urna suscipit, vel
hendrerit orci lacinia. In ut facilisis tortor. Suspendisse luctus
neque a odio vulputate malesuada ut sit amet nulla. Nam nec ipsum non
mauris mollis rutrum. Phasellus vel sem volutpat, pellentesque augue
ut, rhoncus eros. Quisque eu tellus ac lorem ornare iaculis.
</p>
<p>
Morbi accumsan maximus justo, eu varius nunc posuere a. Fusce sed
lacus a neque rutrum laoreet. Fusce varius bibendum interdum. Nunc
consequat ex ac nisi porta ultricies. Curabitur sodales cursus purus,
id efficitur orci condimentum eget. Etiam luctus massa id velit
volutpat tincidunt. Ut at risus arcu. Nunc semper lacus in sem rhoncus
elementum. Cras imperdiet vitae magna scelerisque ornare. Nam nec
cursus urna.
</p>
<p>
Aliquam dictum luctus lectus ac dignissim. Cras elementum interdum
scelerisque. Cras consectetur purus ex, vel rutrum ante tincidunt non.
Sed sapien purus, dignissim non odio sed, efficitur convallis libero.
Proin tristique tellus fermentum nisl posuere, sed accumsan dui
vulputate. Curabitur consequat rhoncus lacus sit amet consequat.
Aliquam maximus, massa quis auctor lacinia, ipsum magna finibus quam,
id hendrerit tortor tortor sit amet tortor. Mauris sit amet
scelerisque dolor. Nulla facilisi. Cras a sapien vitae dolor euismod
posuere in ut sem. Suspendisse vel nibh tortor. Quisque tempus a ex
vitae auctor. Donec in viverra leo. Nam eu gravida quam, et feugiat
metus. Proin egestas ipsum eget augue porttitor lobortis. Donec vitae
quam dui.
</p>
</div>
),
},
}
51 changes: 48 additions & 3 deletions pro/src/ui-kit/DialogBuilder/DialogBuilder.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import * as Dialog from '@radix-ui/react-dialog'
import cn from 'classnames'
import { useRef } from 'react'

import styles from './DialogBuilder.module.scss'
import { DialogBuilderCloseButton } from './DialogBuilderCloseButton'

export type DialogVariant = 'default' | 'drawer'
type DialogVariant = 'default' | 'drawer'

/**
* Props for the DialogBuilder component.
*/
type DialogBuilderProps = {
export type DialogBuilderProps = {
/**
* The trigger element that opens the dialog.
*/
trigger?: React.ReactNode
/**
* The heading title of the dialog.
*/
title?: string
/**
* The content to be displayed inside the dialog.
*/
children: React.ReactNode
/**
* The content to be displayed at the bottom of the dialog, after the separator.
*/
footer?: React.ReactNode
/**
* Determines if the dialog is open by default.
* @default false
Expand Down Expand Up @@ -64,14 +73,17 @@ type DialogBuilderProps = {
*/
export function DialogBuilder({
trigger,
title,
children,
footer,
defaultOpen = false,
onOpenChange,
open,
closeButtonClassName,
className,
variant = 'default',
}: DialogBuilderProps) {
const contentRef = useRef<HTMLDivElement>(null)
return (
<Dialog.Root
defaultOpen={defaultOpen}
Expand All @@ -85,15 +97,48 @@ export function DialogBuilder({
styles['dialog-builder-overlay'],
styles[`dialog-builder-overlay-${variant}`]
)}
data-testid="dialog-overlay"
>
<Dialog.Content
className={cn(styles['dialog-builder-content'], className)}
aria-describedby={undefined}
ref={contentRef}
onPointerDownOutside={(e) => {
if (!contentRef.current) {
return
}
const contentRect = contentRef.current.getBoundingClientRect()
// Detect if click actually happened within the bounds of content.
// This can happen if click was on an absolutely positioned element overlapping content,
// such as a click on the scroll bar (https://github.com/radix-ui/primitives/issues/1280)
const actuallyClickedInside =
e.detail.originalEvent.clientX > contentRect.left &&
e.detail.originalEvent.clientX <
contentRect.left + contentRect.width &&
e.detail.originalEvent.clientY > contentRect.top &&
e.detail.originalEvent.clientY <
contentRect.top + contentRect.height
if (actuallyClickedInside) {
e.preventDefault()
}
}}
>
<DialogBuilderCloseButton
closeButtonClassName={closeButtonClassName}
/>
<section>{children}</section>
<section className={styles['dialog-builder-section']}>
<div>
{title && (
<Dialog.Title asChild>
<h1 className={styles['dialog-builder-title']}>{title}</h1>
</Dialog.Title>
)}
{children}
</div>
{footer && (
<div className={styles['dialog-builder-footer']}>{footer}</div>
)}
</section>
</Dialog.Content>
</Dialog.Overlay>
</Dialog.Portal>
Expand Down

0 comments on commit 0f2f53d

Please sign in to comment.