Skip to content

Commit

Permalink
feat(Dialog): add prop contentOverflow to control overflow behaviour (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
DenisVershkov authored and amje committed Feb 1, 2024
1 parent 949d528 commit 608891b
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 45 deletions.
9 changes: 9 additions & 0 deletions src/components/Dialog/Dialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ $block: '.#{variables.$ns}dialog';

#{$block} {
position: relative;
display: flex;
flex-direction: column;

&_has-scroll {
overflow-y: auto;
max-height: calc(
100vh - calc(var(--g-modal-margin, #{variables.$modal-default-margin}) * 2)
);
}

&_size {
&_s {
Expand Down
14 changes: 13 additions & 1 deletion src/components/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ interface DialogOwnProps {
disableFocusTrap?: boolean;
disableAutoFocus?: boolean;
restoreFocusRef?: React.RefObject<HTMLElement>;
contentOverflow?: 'visible' | 'auto';
}

interface DialogDefaultProps {
Expand Down Expand Up @@ -78,6 +79,7 @@ export class Dialog extends React.Component<DialogInnerProps> {
restoreFocusRef,
keepMounted,
size,
contentOverflow = 'visible',
className,
modalClassName,
hasCloseButton,
Expand All @@ -97,6 +99,7 @@ export class Dialog extends React.Component<DialogInnerProps> {
return (
<Modal
open={open}
contentOverflow={contentOverflow}
disableBodyScrollLock={disableBodyScrollLock}
disableEscapeKeyDown={disableEscapeKeyDown}
disableOutsideClick={disableOutsideClick}
Expand All @@ -118,7 +121,16 @@ export class Dialog extends React.Component<DialogInnerProps> {
container={container}
qa={qa}
>
<div className={b({size, 'has-close': hasCloseButton}, className)}>
<div
className={b(
{
size,
'has-close': hasCloseButton,
'has-scroll': contentOverflow === 'auto',
},
className,
)}
>
{children}
{hasCloseButton && <ButtonClose onClose={this.handleCloseButtonClick} />}
</div>
Expand Down
8 changes: 7 additions & 1 deletion src/components/Dialog/DialogBody/DialogBody.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@ $block: '.#{variables.$ns}dialog-body';

#{$block} {
padding: var(--yc-dialog-body-padding);
flex: 1 1 100%;
flex: 1 1 auto;
overflow-y: auto;

&_has-borders {
border-block-start: 1px solid var(--g-color-line-generic);
border-block-end: 1px solid var(--g-color-line-generic);
}
}
5 changes: 3 additions & 2 deletions src/components/Dialog/DialogBody/DialogBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ const b = block('dialog-body');
export interface DialogBodyProps {
children: React.ReactNode;
className?: string;
hasBorders?: boolean;
}

export function DialogBody(props: DialogBodyProps) {
const {className} = props;
const {className, hasBorders = false} = props;

return <div className={b(null, className)}>{props.children}</div>;
return <div className={b({'has-borders': hasBorders}, className)}>{props.children}</div>;
}
53 changes: 27 additions & 26 deletions src/components/Dialog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,33 @@ Dialog component

### PropTypes

| Name | Type | Required | Default | Description |
| :-------------------- | :--------------------------------------------------------------------------------------------- | :------: | :------ | :---------------------------------------------------------------------------------- |
| open | `Boolean` || | Current dialog state |
| onEscapeKeyDown | `(event: KeyboardEvent) => void` | | | Escape key down event handler |
| onEnterKeyDown | `(event: KeyboardEvent) => void` | | | Enter key down event handler |
| onOutsideClick | `(event: MouseEvent) => void` | | | Event handler on outside dialog mouse click |
| onClose | `(event: MouseEvent or KeyboardEvent, reason: LayerCloseReason or "closeButtonClick") => void` || | On dialog close handler |
| className | `String` | | | ClassName of dialog content wrapper |
| modalClassName | `String` | | | ClassName of modal box, in which dialog is disposed |
| size | `s` `m` `l` | | | Dialog size |
| disableBodyScrollLock | `Boolean` | | `False` | Should body scroll be locked |
| disableEscapeKeyDown | `Boolean` | | `False` | Should escape key down be disabled |
| disableOutsideClick | `Boolean` | | `False` | Should outside click be disabled |
| disableFocusTrap | `Boolean` | | | If true, the modal will not prevent focus from leaving the modal while open |
| disableAutoFocus | `Boolean` | | | If true, the modal will not automatically shift focus to itself when it opens |
| onTransitionEnter | `() => void` | | | On start open dialog animation |
| onTransitionEntered | `() => void` | | | On finish open dialog animation |
| onTransitionExit | `() => void` | | | On start close dialog animation |
| onTransitionExited | `() => void` | | | On finish close dialog animation |
| restoreFocusRef | `React.RefObject` | | | Element to receive focus when the dialog closes |
| keepMounted | `Boolean` | | `False` | Should dialog be kept mounted |
| hasCloseButton | `Boolean` | | `True` | Cross icon in top right corner of dialog presence |
| aria-labelledby | `String` | | | Id of <Dialog/> caption. Use `id` props of `<Dialog.Header/>` to set id for caption |
| aria-label | `String` | | | Dialog label for a11y. Prefer `aria-labelledby` if caption is visible to user |
| container | `HTMLElement` | | | Container element for the dialog box |
| qa | `String` | | | Data-qa attribute value of modal box, in which dialog is disposed |
| Name | Type | Required | Default | Description |
| :-------------------- | :--------------------------------------------------------------------------------------------- | :------: | :-------- | :---------------------------------------------------------------------------------- |
| open | `Boolean` || | Current dialog state |
| onEscapeKeyDown | `(event: KeyboardEvent) => void` | | | Escape key down event handler |
| onEnterKeyDown | `(event: KeyboardEvent) => void` | | | Enter key down event handler |
| onOutsideClick | `(event: MouseEvent) => void` | | | Event handler on outside dialog mouse click |
| onClose | `(event: MouseEvent or KeyboardEvent, reason: LayerCloseReason or "closeButtonClick") => void` || | On dialog close handler |
| className | `String` | | | ClassName of dialog content wrapper |
| modalClassName | `String` | | | ClassName of modal box, in which dialog is disposed |
| size | `s` `m` `l` | | | Dialog size |
| disableBodyScrollLock | `Boolean` | | `False` | Should body scroll be locked |
| disableEscapeKeyDown | `Boolean` | | `False` | Should escape key down be disabled |
| disableOutsideClick | `Boolean` | | `False` | Should outside click be disabled |
| disableFocusTrap | `Boolean` | | | If true, the modal will not prevent focus from leaving the modal while open |
| disableAutoFocus | `Boolean` | | | If true, the modal will not automatically shift focus to itself when it opens |
| onTransitionEnter | `() => void` | | | On start open dialog animation |
| onTransitionEntered | `() => void` | | | On finish open dialog animation |
| onTransitionExit | `() => void` | | | On start close dialog animation |
| onTransitionExited | `() => void` | | | On finish close dialog animation |
| restoreFocusRef | `React.RefObject` | | | Element to receive focus when the dialog closes |
| keepMounted | `Boolean` | | `False` | Should dialog be kept mounted |
| hasCloseButton | `Boolean` | | `True` | Cross icon in top right corner of dialog presence |
| aria-labelledby | `String` | | | Id of <Dialog/> caption. Use `id` props of `<Dialog.Header/>` to set id for caption |
| aria-label | `String` | | | Dialog label for a11y. Prefer `aria-labelledby` if caption is visible to user |
| container | `HTMLElement` | | | Container element for the dialog box |
| qa | `String` | | | Data-qa attribute value of modal box, in which dialog is disposed |
| contentOverflow | `visible` `auto` | | `visible` | Define whether `Dialog` has a scroll indicator inside or grows with the content |

### Examples

Expand Down
32 changes: 20 additions & 12 deletions src/components/Modal/Modal.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@use '../variables';

$block: '.#{variables.$ns}modal';
$modal-default-border-radius: 5px;

#{$block} {
display: none;
Expand All @@ -16,24 +17,31 @@ $block: '.#{variables.$ns}modal';
-webkit-overflow-scrolling: touch;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);

&__table {
display: table;
width: 100%;
height: 100%;
&__content-aligner {
min-width: 100%;
min-height: 100%;

display: inline-flex;
align-items: center;
justify-content: center;
}

&__cell {
display: table-cell;
text-align: center;
vertical-align: middle;
&__content-wrapper {
margin: var(--g-modal-margin, #{variables.$modal-default-margin});
border-radius: var(--g-modal-border-radius, #{$modal-default-border-radius});
overflow-x: hidden;
}

&__content {
display: inline-block;
margin: var(--g-modal-margin, 20px);
text-align: start;
background-color: var(--g-color-base-modal);
border-radius: var(--g-modal-border-radius, 5px);
border-radius: var(--g-modal-border-radius, #{$modal-default-border-radius});

&_has-scroll {
overflow-y: auto;
max-height: calc(
100vh - calc(var(--g-modal-margin, #{variables.$modal-default-margin}) * 2)
);
}
}

&,
Expand Down
12 changes: 9 additions & 3 deletions src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export interface ModalProps extends DOMProps, LayerExtendableProps, QAProps {
onTransitionEntered?: VoidFunction;
onTransitionExit?: VoidFunction;
onTransitionExited?: VoidFunction;
contentOverflow?: 'visible' | 'auto';
}

export type ModalCloseReason = LayerCloseReason;
Expand Down Expand Up @@ -69,6 +70,7 @@ export function Modal({
onTransitionExited,
children,
style,
contentOverflow = 'visible',
className,
contentClassName,
'aria-labelledby': ariaLabelledBy,
Expand Down Expand Up @@ -129,8 +131,8 @@ export function Modal({
}}
>
<div ref={containerRef} style={style} className={b({open}, className)} data-qa={qa}>
<div className={b('table')}>
<div className={b('cell')}>
<div className={b('content-aligner')}>
<div className={b('content-wrapper')}>
<FocusTrap
enabled={!disableFocusTrap && focusTrap && open && !inTransition}
autoFocus={!disableAutoFocus && autoFocus}
Expand All @@ -142,7 +144,11 @@ export function Modal({
aria-modal={open}
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
className={b('content', contentClassName)}
className={b(
'content',
{'has-scroll': contentOverflow === 'auto'},
contentClassName,
)}
{...containerProps}
>
{children}
Expand Down
1 change: 1 addition & 0 deletions src/components/Modal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const [open, setOpen] = React.useState(false);
| style | HTML `style` atribute for root node | `string` | |
| aria-label | HTML `aria-label` attribute to describe the `Modal` | `string` | |
| aria-labelledby | Id of the visible `Modal` caption element | `string` | |
| contentOverflow | Define whether `Modal` has a scroll indicator inside or grows with the content | `visible` `auto` | `visible` |

## CSS API

Expand Down
1 change: 1 addition & 0 deletions src/components/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ $s-height: 24px;
$m-height: 28px;
$l-height: 36px;
$xl-height: 44px;
$modal-default-margin: 20px;

0 comments on commit 608891b

Please sign in to comment.