Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/strange-yaks-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet-react": patch
---

**Popover:** No longer closes with "Escape" key if a Dialog element is open above the popover
48 changes: 48 additions & 0 deletions packages/react/src/components/dropdown/dropdown.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,51 @@ WithNestedDialog.play = async (ctx) => {
await expect(dialog).toBeInTheDocument();
await waitFor(() => expect(dialog).toBeVisible());
};

export const WithAdjacentDialog: StoryFn<typeof Dropdown> = (args) => {
return (
<>
<Dropdown.TriggerContext>
<Dropdown.Trigger data-color={args['data-color']}>
Dropdown
</Dropdown.Trigger>
<Dropdown {...args}>
<Dropdown.List>
<Dropdown.Item>
<Dropdown.Button command='show-modal' commandFor='adjacent-modal'>
Dialog
</Dropdown.Button>
</Dropdown.Item>
</Dropdown.List>
</Dropdown>
</Dropdown.TriggerContext>
<Dialog id='adjacent-modal'>Min dialog</Dialog>
</>
);
};

export const WithNestedDropdown: StoryFn<typeof Dropdown> = (args) => {
return (
<Dropdown.TriggerContext>
<Dropdown.Trigger data-color={args['data-color']}>
Dropdown
</Dropdown.Trigger>
<Dropdown {...args}>
<Dropdown.List>
<Dropdown.Item>
<Dropdown.TriggerContext>
<Dropdown.Trigger variant='tertiary'>Dropdown</Dropdown.Trigger>
<Dropdown>
<Dropdown.List>
<Dropdown.Item>
<Dropdown.Button>Nested</Dropdown.Button>
</Dropdown.Item>
</Dropdown.List>
</Dropdown>
</Dropdown.TriggerContext>
</Dropdown.Item>
</Dropdown.List>
</Dropdown>
</Dropdown.TriggerContext>
);
};
34 changes: 23 additions & 11 deletions packages/react/src/components/popover/popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ export type PopoverProps = MergeRight<
* Content
* </Popover>
*/

export const Popover = forwardRef<HTMLDivElement, PopoverProps>(
function Popover(
{
Expand Down Expand Up @@ -118,16 +117,12 @@ export const Popover = forwardRef<HTMLDivElement, PopoverProps>(
};

const handleKeydown = (event: KeyboardEvent) => {
if (event.key !== 'Escape' || !controlledOpen) return;
const isOpen =
popoverRef.current?.matches(':popover-open') ||
popoverRef.current?.classList.contains(':popover-open'); // Polyfill support

if (!isOpen) return;
event.preventDefault(); // Prevent closing fullscreen in Safari
document.querySelector<HTMLElement>(trigger)?.focus?.(); // Move focus back to trigger since `popoover="manual"` doesn't do this
setInternalOpen(false);
onClose?.();
if (event.key === 'Escape' && controlledOpen && isTopLayer(popover)) {
event.preventDefault(); // Prevent closing fullscreen in Safari
document.querySelector<HTMLElement>(trigger)?.focus?.(); // Move focus back to trigger since `popover="manual"` doesn't do this
setInternalOpen(false);
onClose?.();
}
Comment thread
eirikbacker marked this conversation as resolved.
};

popover?.togglePopover?.(controlledOpen);
Expand Down Expand Up @@ -163,3 +158,20 @@ export const Popover = forwardRef<HTMLDivElement, PopoverProps>(
);
},
);

// NOTE: This is not able to check if the popover is the most recently added #topLayer,
// so we need another method in time, or remove the controlled popover="manual" in a v2
const isTopLayer = (checkElement?: Element | null) => {
if (!checkElement) return false;
const { x, y, width, height } = checkElement.getBoundingClientRect();
const topElement = document.elementFromPoint(x + width / 2, y + height / 2);

// If the element on top is on browser #top-layer but not same as provided element, then provided element is not on top
for (let el = topElement; el; el = el.parentElement) {
if (checkElement === el) return true; // If the topElement is same as provided element, it's on top
if (el instanceof HTMLDialogElement && el.open) return false; // Check for open dialog
Comment thread
eirikbacker marked this conversation as resolved.
if (el.classList.contains(':popover-open')) return false; // Polyfill support
if (el.matches(':popover-open')) return false; // Native support
}
return false;
};
Loading