Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(APP-3725): Update Dialog component for 'size' prop #391

Merged
merged 18 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Added

- Add `size` prop to `<Dialog />` core component with `max-w` t-shirt sizing

### Changed

- Update minor and patch NPM dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,24 +54,9 @@ export const Default: Story = {
export const ScrollableContent: Story = {
args: {
children: (
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla in aliquet nibh. Vestibulum pellentesque
urna eget aliquam tristique. Proin justo nisl, suscipit ac aliquet et, congue id enim. Quisque sed
lacinia nulla. Nullam cursus eros quis sapien lobortis, pulvinar laoreet ipsum ornare. Nullam
condimentum molestie nunc vel iaculis. Cras dignissim libero et efficitur rhoncus. Donec ut turpis enim.
Vestibulum cursus mollis turpis et vehicula. In sit amet odio metus. Morbi elementum leo sit amet
sagittis ullamcorper. Nulla pellentesque odio vel mi dignissim sodales. Vestibulum ante ipsum primis in
faucibus orci luctus et ultrices posuere cubilia curae; Curabitur venenatis interdum dolor nec blandit.
Fusce eu leo non dolor convallis porttitor. Pellentesque feugiat tincidunt iaculis. Lorem ipsum dolor
sit amet, consectetur adipiscing elit. Nulla in aliquet nibh. Vestibulum pellentesque urna eget aliquam
tristique. Proin justo nisl, suscipit ac aliquet et, congue id enim. Quisque sed lacinia nulla. Nullam
cursus eros quis sapien lobortis, pulvinar laoreet ipsum ornare. Nullam condimentum molestie nunc vel
iaculis. Cras dignissim libero et efficitur rhoncus. Donec ut turpis enim. Vestibulum cursus mollis
turpis et vehicula. In sit amet odio metus. Morbi elementum leo sit amet sagittis ullamcorper. Nulla
pellentesque odio vel mi dignissim sodales. Vestibulum ante ipsum primis in faucibus orci luctus et
ultrices posuere cubilia curae; Curabitur venenatis interdum dolor nec blandit. Fusce eu leo non dolor
convallis porttitor. Pellentesque feugiat tincidunt iaculis.
</p>
<div className="flex h-screen w-full items-center justify-center border border-dashed border-info-300 bg-info-100">
Overflowing content
</div>
),
},
render: (props) => <ControlledComponent {...props} />,
Expand Down
61 changes: 61 additions & 0 deletions src/core/components/dialogs/dialog/dialogRoot/dialogRoot.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { ComponentPropsWithoutRef, ReactNode } from 'react';

export type DialogSize = 'sm' | 'md' | 'lg' | 'xl';

export interface IDialogRootProps extends ComponentPropsWithoutRef<'div'> {
/**
* Children of the component.
*/
children?: ReactNode;
/**
* Additional CSS class names for custom styling of the dialog's content container.
*/
containerClassName?: string;
/**
* Size of the dialog.
* @default md
*/
size?: DialogSize;
/**
* Determines whether interactions with elements outside of the dialog will be disabled.
* @default true
*/
modal?: boolean;
/**
* Manages the visibility state of the dialog.
*/
open?: boolean;
/**
* Additional CSS class names for custom styling of the overlay behind the dialog.
*/
overlayClassName?: string;
/**
* Handler called when focus moves to the trigger after closing
*/
onCloseAutoFocus?: (e: Event) => void;
/**
* Handler called when the escape key is pressed while the dialog is opened. Closes the dialog by default.
*/
onEscapeKeyDown?: (e: KeyboardEvent) => void;
/**
* Handler called when an interaction (pointer or focus event) happens outside the bounds of the component
*/
onInteractOutside?: (e: Event) => void;
/**
* Handler called when focus moves into the component after opening
*/
onOpenAutoFocus?: (e: Event) => void;
/**
* Callback function invoked when the open state of the dialog changes.
*/
onOpenChange?: (open: boolean) => void;
/**
* Handler called when a pointer event occurs outside the bounds of the component
*/
onPointerDownOutside?: (e: Event) => void;
/**
* Keeps the focus inside the Dialog when set to true.
* @default true
*/
useFocusTrap?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ type Story = StoryObj<typeof Dialog.Root>;
* Default usage of the `Dialog.Root` component
*/
export const Default: Story = {
args: {},
render: (props) => {
const [open, setOpen] = useState(false);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { render, screen } from '@testing-library/react';
import { DialogHeader } from '../dialogHeader';
import { DialogRoot, type IDialogRootProps } from './dialogRoot';
import { DialogRoot } from './dialogRoot';
import type { IDialogRootProps } from './dialogRoot.api';

describe('<Dialog.Root/> component', () => {
const createTestComponent = (rootProps?: Partial<IDialogRootProps>) => {
Expand Down
66 changes: 11 additions & 55 deletions src/core/components/dialogs/dialog/dialogRoot/dialogRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,23 @@ import { Content, Overlay, Portal, Root } from '@radix-ui/react-dialog';
import { FocusScope } from '@radix-ui/react-focus-scope';
import classNames from 'classnames';
import { AnimatePresence, motion } from 'framer-motion';
import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
import { dialogContentAnimationVariants, dialogOverlayAnimationVariants } from '../../dialogUtils';
import type { DialogSize, IDialogRootProps } from './dialogRoot.api';

export interface IDialogRootProps extends ComponentPropsWithoutRef<'div'> {
/**
* Children of the component.
*/
children?: ReactNode;
/**
* Additional CSS class names for custom styling of the dialog's content container.
*/
containerClassName?: string;
/**
* Determines whether interactions with elements outside of the dialog will be disabled.
* @default true
*/
modal?: boolean;
/**
* Manages the visibility state of the dialog.
*/
open?: boolean;
/**
* Additional CSS class names for custom styling of the overlay behind the dialog.
*/
overlayClassName?: string;
/**
* Handler called when focus moves to the trigger after closing
*/
onCloseAutoFocus?: (e: Event) => void;
/**
* Handler called when the escape key is pressed while the dialog is opened. Closes the dialog by default.
*/
onEscapeKeyDown?: (e: KeyboardEvent) => void;
/**
* Handler called when an interaction (pointer or focus event) happens outside the bounds of the component
*/
onInteractOutside?: (e: Event) => void;
/**
* Handler called when focus moves into the component after opening
*/
onOpenAutoFocus?: (e: Event) => void;
/**
* Callback function invoked when the open state of the dialog changes.
*/
onOpenChange?: (open: boolean) => void;
/**
* Handler called when a pointer event occurs outside the bounds of the component
*/
onPointerDownOutside?: (e: Event) => void;
/**
* Keeps the focus inside the Dialog when set to true.
* @default true
*/
useFocusTrap?: boolean;
}
const sizeToClassNames: Record<DialogSize, string> = {
sm: 'max-w-[400px]',
md: 'max-w-[480px]',
lg: 'max-w-[640px]',
xl: 'max-w-[880px]',
};

/**
* `Dialog.Root` component.
*/
export const DialogRoot: React.FC<IDialogRootProps> = (props) => {
const {
children,
size = 'md',
containerClassName,
overlayClassName,
onCloseAutoFocus,
Expand All @@ -82,9 +37,10 @@ export const DialogRoot: React.FC<IDialogRootProps> = (props) => {
);

const containerClassNames = classNames(
'fixed inset-x-2 bottom-2 mx-auto max-h-[calc(100vh-80px)] lg:bottom-auto lg:top-[120px] lg:max-h-[calc(100vh-200px)]',
'flex max-w-[480px] flex-col rounded-xl border border-neutral-100 bg-neutral-0 shadow-neutral-md md:min-w-[480px]',
'fixed inset-x-2 bottom-2 mx-auto max-h-[calc(100vh-80px)] overflow-auto md:inset-x-6 md:bottom-6 lg:bottom-auto lg:top-12 lg:max-h-[calc(100vh-200px)]',
'flex flex-col rounded-xl border border-neutral-100 bg-neutral-0 shadow-neutral-md',
'z-[var(--guk-dialog-content-z-index)]',
sizeToClassNames[size],
containerClassName,
);

Expand Down
3 changes: 2 additions & 1 deletion src/core/components/dialogs/dialog/dialogRoot/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { DialogRoot, type IDialogRootProps } from './dialogRoot';
export { DialogRoot } from './dialogRoot';
export { type DialogSize, type IDialogRootProps } from './dialogRoot.api';
Loading