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(Dialog): add prop contentOverflow to control overflow behaviour #1251

Merged
merged 9 commits into from
Jan 18, 2024
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
amje marked this conversation as resolved.
Show resolved Hide resolved
);
}

&_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-dividers {
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;
hasDividers?: boolean;
amje marked this conversation as resolved.
Show resolved Hide resolved
}

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

return <div className={b(null, className)}>{props.children}</div>;
return <div className={b({'has-dividers': hasDividers}, 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
29 changes: 17 additions & 12 deletions src/components/Modal/Modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,29 @@ $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, #{variables.$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);
&_has-scroll {
overflow-y: auto;
max-height: calc(
100vh - calc(var(--g-modal-margin, #{variables.$modal-default-margin})) * 2
amje marked this conversation as resolved.
Show resolved Hide resolved
);
}
}

&,
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
2 changes: 2 additions & 0 deletions src/components/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ $s-height: 24px;
$m-height: 28px;
$l-height: 36px;
$xl-height: 44px;
$modal-default-border-radius: 5px;
amje marked this conversation as resolved.
Show resolved Hide resolved
$modal-default-margin: 20px;
Loading