Skip to content

Commit

Permalink
[🚑️][Dialog]: Hotfix an issue that caused the focus trap to not work …
Browse files Browse the repository at this point in the history
…properly (#90)
  • Loading branch information
mimshins authored Apr 27, 2024
1 parent c26c76a commit 6d3d033
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 36 deletions.
48 changes: 28 additions & 20 deletions lib/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import * as React from "react";
import Portal from "../Portal";
import {
FocusTrap,
SystemError,
SystemKeys,
resolvePropWithRenderContext,
} from "../internals";
import type { MergeElementProps, PropWithRenderContext } from "../types";
import {
componentWithForwardedRef,
getScrollingState,
useDeterministicId,
useEventListener,
useIsMounted,
Expand Down Expand Up @@ -97,7 +99,9 @@ const DialogBase = (props: Props, ref: React.Ref<HTMLDivElement>) => {
previouslyFocusedElement.current =
document.activeElement as HTMLElement | null;

disablePageScroll();
const isBodyScrolling = getScrollingState(document.body).vertical;

if (isBodyScrolling) disablePageScroll();
} else {
enablePageScroll();

Expand Down Expand Up @@ -127,9 +131,11 @@ const DialogBase = (props: Props, ref: React.Ref<HTMLDivElement>) => {
target: document,
eventType: "keydown",
handler: event => {
event.preventDefault();
if (event.key === SystemKeys.ESCAPE) {
event.preventDefault();

if (event.key === SystemKeys.ESCAPE) emitClose();
emitClose();
}
},
},
open,
Expand All @@ -140,26 +146,28 @@ const DialogBase = (props: Props, ref: React.Ref<HTMLDivElement>) => {

return (
<Portal>
<div
data-slot="Portal:Root"
role="presentation"
tabIndex={-1}
aria-hidden={!open}
style={{ position: "absolute", top: 0, left: 0, right: 0 }}
>
<FocusTrap enabled={open}>
<div
{...otherProps}
id={id}
ref={ref}
className={className}
data-slot={RootSlot}
data-open={open ? "" : undefined}
data-slot="Portal:Root"
role="presentation"
tabIndex={-1}
aria-hidden={!open}
style={{ position: "absolute", top: 0, left: 0, right: 0 }}
>
<DialogContext.Provider value={context}>
{children}
</DialogContext.Provider>
<div
{...otherProps}
id={id}
ref={ref}
className={className}
data-slot={RootSlot}
data-open={open ? "" : undefined}
>
<DialogContext.Provider value={context}>
{children}
</DialogContext.Provider>
</div>
</div>
</div>
</FocusTrap>
</Portal>
);
};
Expand Down
25 changes: 11 additions & 14 deletions lib/Dialog/components/Content.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as React from "react";
import { logger } from "../../internals";
import FocusTrap from "../../internals/FocusTrap";
import type { MergeElementProps } from "../../types";
import { componentWithForwardedRef, useDeterministicId } from "../../utils";
import { DialogContext } from "../context";
Expand Down Expand Up @@ -39,19 +38,17 @@ const ContentBase = (props: Props, ref: React.Ref<HTMLDivElement>) => {
}

return (
<FocusTrap enabled={ctx.open}>
<div
{...otherProps}
id={id}
ref={ref}
className={className}
role={ctx.role}
data-slot={ContentRootSlot}
aria-modal="true"
>
{children}
</div>
</FocusTrap>
<div
{...otherProps}
id={id}
ref={ref}
className={className}
role={ctx.role}
data-slot={ContentRootSlot}
aria-modal="true"
>
{children}
</div>
);
};

Expand Down
3 changes: 2 additions & 1 deletion lib/Menu/BaseMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const BaseMenuBase = (props: Props, ref: React.Ref<HTMLDivElement>) => {
</div>
);

if (trapFocus)
if (trapFocus) {
return (
<FocusTrap
enabled={open}
Expand All @@ -69,6 +69,7 @@ const BaseMenuBase = (props: Props, ref: React.Ref<HTMLDivElement>) => {
{renderMenu()}
</FocusTrap>
);
}

return renderMenu();
};
Expand Down
31 changes: 31 additions & 0 deletions lib/utils/get-scrolling-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const getScrollingState = (element: HTMLElement) => {
const isScrollable = (node: HTMLElement) => {
const overflow = getComputedStyle(node).getPropertyValue("overflow");

return overflow.includes("auto") || overflow.includes("scroll");
};

const getScrollParent = (element: HTMLElement) => {
let current = element.parentNode as HTMLElement | null;

while (current) {
if (!(element instanceof HTMLElement)) break;
if (!(element instanceof SVGElement)) break;

if (isScrollable(current)) return current;

current = current.parentNode as HTMLElement | null;
}

return document.scrollingElement || document.documentElement;
};

const scrollParent = getScrollParent(element);

return {
vertical: scrollParent.scrollHeight > scrollParent.clientHeight,
horizontal: scrollParent.scrollWidth > scrollParent.clientWidth,
};
};

export default getScrollingState;
1 change: 1 addition & 0 deletions lib/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export { default as createVirtualElement } from "./create-virtual-element";
export { default as dispatchDiscreteCustomEvent } from "./dispatch-discrete-custom-event";
export * from "./dom";
export { default as forkRefs } from "./fork-refs";
export { default as getScrollingState } from "./get-scrolling-state";
export * from "./is";
export * from "./math";
export { default as requestFormSubmit } from "./request-form-submit";
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@styleless-ui/react",
"version": "1.0.0-rc.1",
"version": "1.0.0-rc.2",
"description": "Completely unstyled, headless and accessible React UI components.",
"author": "mimshins <[email protected]>",
"license": "MIT",
Expand Down

0 comments on commit 6d3d033

Please sign in to comment.