Skip to content

Commit

Permalink
Merge pull request #5359 from matuzalemsteles/issue-5332
Browse files Browse the repository at this point in the history
fix: fixes overlay errors when closing Modal and the page is inaccessible when closed by Overlay with isModal enabled
  • Loading branch information
matuzalemsteles committed Feb 7, 2023
2 parents b5c42f7 + 653fc4e commit 5b00bcb
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 27 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ module.exports = {
},
'./packages/clay-shared/src/': {
branches: 20,
functions: 15,
functions: 14,
lines: 36,
statements: 39,
},
Expand Down
4 changes: 2 additions & 2 deletions packages/clay-modal/src/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import {ClayPortal, IPortalBaseProps} from '@clayui/shared';
import {hideOthers} from 'aria-hidden';
import {suppressOthers} from 'aria-hidden';
import classNames from 'classnames';
import React, {useEffect, useMemo, useRef} from 'react';
import warning from 'warning';
Expand Down Expand Up @@ -128,7 +128,7 @@ const ClayModal = ({
useEffect(() => {
if (modalElementRef.current && show) {
// Hide everything from ARIA except the Modal Body
return hideOthers(modalElementRef.current);
return suppressOthers(modalElementRef.current);
}
}, [show]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ exports[`ModalProvider -> IncrementalInteractions renders a modal when dispatchi
<div
aria-hidden="true"
class="modal-backdrop fade show"
data-aria-hidden="true"
data-suppressed="true"
/>
<div
class="fade modal d-block show"
Expand Down Expand Up @@ -96,7 +96,7 @@ exports[`ModalProvider -> IncrementalInteractions renders a modal when dispatchi
<button
aria-hidden="true"
class="btn btn-primary"
data-aria-hidden="true"
data-suppressed="true"
data-testid="button"
type="button"
>
Expand All @@ -105,7 +105,7 @@ exports[`ModalProvider -> IncrementalInteractions renders a modal when dispatchi
</div>
<div
aria-hidden="true"
data-aria-hidden="true"
data-suppressed="true"
/>
</body>
`;
32 changes: 16 additions & 16 deletions packages/clay-modal/src/__tests__/__snapshots__/index.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ exports[`ClayModal renders 1`] = `
<div
aria-hidden="true"
class="modal-backdrop fade show"
data-aria-hidden="true"
data-suppressed="true"
/>
<div
class="fade modal d-block show"
Expand Down Expand Up @@ -80,7 +80,7 @@ exports[`ClayModal renders 1`] = `
</div>
<div
aria-hidden="true"
data-aria-hidden="true"
data-suppressed="true"
/>
</body>
`;
Expand All @@ -93,7 +93,7 @@ exports[`ClayModal renders Header w/ low-level API 1`] = `
<div
aria-hidden="true"
class="modal-backdrop fade show"
data-aria-hidden="true"
data-suppressed="true"
/>
<div
class="fade modal d-block show"
Expand Down Expand Up @@ -149,7 +149,7 @@ exports[`ClayModal renders Header w/ low-level API 1`] = `
</div>
<div
aria-hidden="true"
data-aria-hidden="true"
data-suppressed="true"
/>
</body>
`;
Expand All @@ -162,7 +162,7 @@ exports[`ClayModal renders a body component with url 1`] = `
<div
aria-hidden="true"
class="modal-backdrop fade show"
data-aria-hidden="true"
data-suppressed="true"
/>
<div
class="fade modal d-block show"
Expand Down Expand Up @@ -191,7 +191,7 @@ exports[`ClayModal renders a body component with url 1`] = `
</div>
<div
aria-hidden="true"
data-aria-hidden="true"
data-suppressed="true"
/>
</body>
`;
Expand All @@ -204,7 +204,7 @@ exports[`ClayModal renders a footer component with buttons 1`] = `
<div
aria-hidden="true"
class="modal-backdrop fade show"
data-aria-hidden="true"
data-suppressed="true"
/>
<div
class="fade modal d-block show"
Expand Down Expand Up @@ -259,7 +259,7 @@ exports[`ClayModal renders a footer component with buttons 1`] = `
</div>
<div
aria-hidden="true"
data-aria-hidden="true"
data-suppressed="true"
/>
</body>
`;
Expand All @@ -272,7 +272,7 @@ exports[`ClayModal renders with Header 1`] = `
<div
aria-hidden="true"
class="modal-backdrop fade show"
data-aria-hidden="true"
data-suppressed="true"
/>
<div
class="fade modal d-block show"
Expand Down Expand Up @@ -317,7 +317,7 @@ exports[`ClayModal renders with Header 1`] = `
</div>
<div
aria-hidden="true"
data-aria-hidden="true"
data-suppressed="true"
/>
</body>
`;
Expand All @@ -330,7 +330,7 @@ exports[`ClayModal renders with center 1`] = `
<div
aria-hidden="true"
class="modal-backdrop fade show"
data-aria-hidden="true"
data-suppressed="true"
/>
<div
class="fade modal d-block show"
Expand All @@ -350,7 +350,7 @@ exports[`ClayModal renders with center 1`] = `
</div>
<div
aria-hidden="true"
data-aria-hidden="true"
data-suppressed="true"
/>
</body>
`;
Expand All @@ -363,7 +363,7 @@ exports[`ClayModal renders with size 1`] = `
<div
aria-hidden="true"
class="modal-backdrop fade show"
data-aria-hidden="true"
data-suppressed="true"
/>
<div
class="fade modal d-block show"
Expand All @@ -383,7 +383,7 @@ exports[`ClayModal renders with size 1`] = `
</div>
<div
aria-hidden="true"
data-aria-hidden="true"
data-suppressed="true"
/>
</body>
`;
Expand All @@ -396,7 +396,7 @@ exports[`ClayModal renders with status 1`] = `
<div
aria-hidden="true"
class="modal-backdrop fade show"
data-aria-hidden="true"
data-suppressed="true"
/>
<div
class="fade modal d-block show"
Expand All @@ -416,7 +416,7 @@ exports[`ClayModal renders with status 1`] = `
</div>
<div
aria-hidden="true"
data-aria-hidden="true"
data-suppressed="true"
/>
</body>
`;
44 changes: 39 additions & 5 deletions packages/clay-shared/src/Overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ type Props = {
triggerRef: React.RefObject<HTMLElement>;
};

const overlayStack: Array<React.RefObject<Element>> = [];

/**
* Overlay component is used for components like dialog and modal.
* For example, Autocomplete, DatePicker, ColorPicker, DropDown are components
Expand All @@ -46,6 +48,15 @@ export function Overlay({
}: Props) {
const unsuppressCallbackRef = useRef<Undo | null>(null);

const onHide = useCallback(
(action: 'escape' | 'blur') => {
if (overlayStack[overlayStack.length - 1] === menuRef) {
onClose(action);
}
},
[onClose]
);

useEvent(
'focus',
useCallback(
Expand All @@ -56,21 +67,24 @@ export function Overlay({
triggerRef.current &&
!triggerRef.current.contains(event.target as Node)
) {
onClose('blur');
onHide('blur');
}
},
[onClose]
[onHide]
),
isOpen,
true,
[isOpen, onClose]
[isOpen, onHide]
);

useEvent(
'keydown',
useCallback(
(event: KeyboardEvent) => {
if (event.key === Keys.Esc) {
if (
event.key === Keys.Esc &&
overlayStack[overlayStack.length - 1] === menuRef
) {
event.stopImmediatePropagation();
event.preventDefault();

Expand Down Expand Up @@ -99,12 +113,32 @@ export function Overlay({
useInteractOutside({
isDisabled: isOpen ? !isCloseOnInteractOutside : true,
onInteract: () => {
onClose('blur');
onHide('blur');
},
onInteractStart: (event) => {
if (overlayStack[overlayStack.length - 1] === menuRef && isModal) {
event.stopPropagation();
event.preventDefault();
}
},
ref: portalRef ?? menuRef,
triggerRef,
});

useEffect(() => {
if (isOpen) {
overlayStack.push(menuRef);
}

return () => {
const index = overlayStack.indexOf(menuRef);

if (index >= 0) {
overlayStack.splice(index, 1);
}
};
}, [isOpen, menuRef]);

useEffect(() => {
if (menuRef.current && isOpen) {
const elements = suppress
Expand Down
5 changes: 5 additions & 0 deletions packages/clay-shared/src/useInteractOutside.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import React, {useEffect, useRef} from 'react';
type Props = {
isDisabled?: boolean;
onInteract?: (event: Event) => void;
onInteractStart?: (event: Event) => void;
ref: React.RefObject<HTMLElement>;
triggerRef: React.RefObject<HTMLElement>;
};
Expand All @@ -19,6 +20,7 @@ type Props = {
export function useInteractOutside({
isDisabled = false,
onInteract,
onInteractStart,
ref,
triggerRef,
}: Props) {
Expand All @@ -39,6 +41,9 @@ export function useInteractOutside({

const onPointerDown = (event: Event) => {
if (isValidEvent(event, ref, triggerRef) && state.onInteract) {
if (onInteractStart) {
onInteractStart(event);
}
state.isPointerDown = true;
}
};
Expand Down

0 comments on commit 5b00bcb

Please sign in to comment.