Skip to content

Commit

Permalink
[core] feat(Overlay): add shouldReturnFocusOnClose prop (#4904)
Browse files Browse the repository at this point in the history
  • Loading branch information
adidahiya committed Sep 14, 2021
1 parent 9a4debd commit 2c71d6a
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 31 deletions.
30 changes: 1 addition & 29 deletions packages/core/src/components/drawer/drawer.tsx
Expand Up @@ -66,14 +66,6 @@ export interface IDrawerProps extends OverlayableProps, IBackdropProps, Props {
*/
position?: Position;

/**
* Whether the application should return focus to the last active element in the
* document after this drawer closes.
*
* @default true
*/
shouldReturnFocusOnClose?: boolean;

/**
* CSS size of the drawer. This sets `width` if `vertical={false}` (default)
* and `height` otherwise.
Expand Down Expand Up @@ -123,7 +115,6 @@ export class Drawer extends AbstractPureComponent2<DrawerProps> {
public static defaultProps: DrawerProps = {
canOutsideClickClose: true,
isOpen: false,
shouldReturnFocusOnClose: true,
style: {},
vertical: false,
};
Expand All @@ -137,8 +128,6 @@ export class Drawer extends AbstractPureComponent2<DrawerProps> {
/** @deprecated use DrawerSize.LARGE */
public static readonly SIZE_LARGE = DrawerSize.LARGE;

private lastActiveElementBeforeOpened: Element | null | undefined;

public render() {
// eslint-disable-next-line deprecation/deprecation
const { size, style, position, vertical } = this.props;
Expand All @@ -161,12 +150,7 @@ export class Drawer extends AbstractPureComponent2<DrawerProps> {
[(realPosition ? isPositionHorizontal(realPosition) : vertical) ? "height" : "width"]: size,
};
return (
<Overlay
{...this.props}
className={Classes.OVERLAY_CONTAINER}
onOpening={this.handleOpening}
onClosed={this.handleClosed}
>
<Overlay {...this.props} className={Classes.OVERLAY_CONTAINER}>
<div className={classes} style={styleProp}>
{this.maybeRenderHeader()}
{this.props.children}
Expand Down Expand Up @@ -226,16 +210,4 @@ export class Drawer extends AbstractPureComponent2<DrawerProps> {
</div>
);
}

private handleOpening = (node: HTMLElement) => {
this.lastActiveElementBeforeOpened = document.activeElement;
this.props.onOpening?.(node);
};

private handleClosed = (node: HTMLElement) => {
if (this.props.shouldReturnFocusOnClose && this.lastActiveElementBeforeOpened instanceof HTMLElement) {
this.lastActiveElementBeforeOpened.focus();
}
this.props.onClosed?.(node);
};
}
24 changes: 22 additions & 2 deletions packages/core/src/components/overlay/overlay.tsx
Expand Up @@ -65,6 +65,14 @@ export interface IOverlayableProps extends IOverlayLifecycleProps {
*/
lazy?: boolean;

/**
* Whether the application should return focus to the last active element in the
* document after this overlay closes.
*
* @default true
*/
shouldReturnFocusOnClose?: boolean;

/**
* Indicates how long (in milliseconds) the overlay's enter/leave transition takes.
* This is used by React `CSSTransition` to know when a transition completes and must match
Expand Down Expand Up @@ -206,6 +214,7 @@ export class Overlay extends AbstractPureComponent2<OverlayProps, IOverlayState>
hasBackdrop: true,
isOpen: false,
lazy: true,
shouldReturnFocusOnClose: true,
transitionDuration: 300,
transitionName: Classes.OVERLAY,
usePortal: true,
Expand All @@ -222,6 +231,8 @@ export class Overlay extends AbstractPureComponent2<OverlayProps, IOverlayState>

private static getLastOpened = () => Overlay.openStack[Overlay.openStack.length - 1];

private lastActiveElementBeforeOpened: Element | null | undefined;

public state: IOverlayState = {
hasEverOpened: this.props.isOpen,
};
Expand Down Expand Up @@ -371,7 +382,7 @@ export class Overlay extends AbstractPureComponent2<OverlayProps, IOverlayState>
) : (
<span className={Classes.OVERLAY_CONTENT}>{child}</span>
);
const { onOpening, onOpened, onClosing, onClosed, transitionDuration, transitionName } = this.props;
const { onOpening, onOpened, onClosing, transitionDuration, transitionName } = this.props;

// a breaking change in react-transition-group types requires us to be explicit about the type overload here,
// using a technique similar to Select.ofType() in @blueprintjs/select
Expand All @@ -385,7 +396,7 @@ export class Overlay extends AbstractPureComponent2<OverlayProps, IOverlayState>
onEntering={onOpening}
onEntered={onOpened}
onExiting={onClosing}
onExited={onClosed}
onExited={this.handleTransitionExited}
timeout={transitionDuration}
addEndListener={this.handleTransitionAddEnd}
>
Expand Down Expand Up @@ -545,8 +556,17 @@ export class Overlay extends AbstractPureComponent2<OverlayProps, IOverlayState>
// add a class to the body to prevent scrolling of content below the overlay
document.body.classList.add(Classes.OVERLAY_OPEN);
}

this.lastActiveElementBeforeOpened = document.activeElement;
}

private handleTransitionExited = (node: HTMLElement) => {
if (this.props.shouldReturnFocusOnClose && this.lastActiveElementBeforeOpened instanceof HTMLElement) {
this.lastActiveElementBeforeOpened.focus();
}
this.props.onClosed?.(node);
};

private handleBackdropMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
const { backdropProps, canOutsideClickClose, enforceFocus, onClose } = this.props;
if (canOutsideClickClose) {
Expand Down

1 comment on commit 2c71d6a

@blueprint-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[core] feat(Overlay): add shouldReturnFocusOnClose prop (#4904)

Previews: documentation | landing | table

Please sign in to comment.