Skip to content

Commit

Permalink
🐛 Modal: Bedre støtte for Tooltip i Modal (#2429)
Browse files Browse the repository at this point in the history
  • Loading branch information
HalvorHaugan authored Oct 27, 2023
1 parent 0bb339e commit 0c7fc04
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 9 deletions.
6 changes: 6 additions & 0 deletions .changeset/wise-weeks-shop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@navikt/ds-react": patch
"@navikt/ds-css": patch
---

:bug: Modal: Bedre støtte for Tooltip i Modal
5 changes: 4 additions & 1 deletion @navikt/core/css/modal.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
.navds-modal--polyfilled {
top: 50%;
transform: translate(0, -50%);
overflow: auto;

/* From polyfill (dialog-polyfill/dist/dialog-polyfill.css): */
left: 0;
Expand All @@ -33,6 +32,10 @@
margin: auto;
}

.navds-modal--polyfilled .navds-modal--polyfilled {
overflow: auto;
}

.navds-modal--polyfilled:not([open]) {
display: none; /* from polyfill */
}
Expand Down
6 changes: 6 additions & 0 deletions @navikt/core/react/src/modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
if (needPolyfill && modalRef.current && portalNode) {
dialogPolyfill.registerDialog(modalRef.current);
}
// We set autofocus on the dialog element to prevent the default behavior where first focusable element gets focus when modal is opened.
// This is mainly to fix an edge case where having a Tooltip as the first focusable element would make it activate when you open the modal.
// We have to use JS because it doesn't work to set it with a prop (React bug?)
// Currently doesn't seem to work in Chrome. See also Tooltip.tsx
if (modalRef.current && portalNode) modalRef.current.autofocus = true;
}, [modalRef, portalNode]);

useEffect(() => {
Expand Down Expand Up @@ -156,6 +161,7 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
<ModalContext.Provider
value={{
closeHandler: getCloseHandler(modalRef, header, onBeforeClose),
ref: modalRef,
}}
>
{header && (
Expand Down
1 change: 1 addition & 0 deletions @navikt/core/react/src/modal/ModalContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ import React from "react";

interface ModalContextProps {
closeHandler?: React.MouseEventHandler<HTMLButtonElement>;
ref: React.RefObject<HTMLDialogElement>;
}
export const ModalContext = React.createContext<ModalContextProps | null>(null);
29 changes: 27 additions & 2 deletions @navikt/core/react/src/modal/modal.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useRef, useState } from "react";
import { FileIcon } from "@navikt/aksel-icons";
import { BodyLong, Button, Heading } from "..";
import React, { useRef, useState } from "react";
import { BodyLong, Button, Heading, Tooltip } from "..";
import Modal from "./Modal";

export default {
Expand Down Expand Up @@ -165,3 +165,28 @@ export const MediumWithPortal = () => (
<Modal.Body>Lorem ipsum dolor sit amet.</Modal.Body>
</Modal>
);

export const WithTooltip = () => {
const ref = useRef<HTMLDialogElement>(null);

return (
<div>
<Button onClick={() => ref.current?.showModal()}>Open Modal</Button>
<Modal
open={ref.current ? undefined : true /* initially open */}
ref={ref}
>
<Modal.Body>
<div style={{ marginBottom: "1rem" }}>
<Tooltip content="This_is_the_first_tooltip">
<Button>Test 1</Button>
</Tooltip>
</div>
<Tooltip content="This is the second tooltip">
<Button>Test 2</Button>
</Tooltip>
</Modal.Body>
</Modal>
</div>
);
};
24 changes: 18 additions & 6 deletions @navikt/core/react/src/tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
arrow as flArrow,
FloatingPortal,
autoUpdate,
arrow as flArrow,
flip,
FloatingPortal,
offset,
safePolygon,
shift,
Expand All @@ -14,16 +14,18 @@ import {
} from "@floating-ui/react";
import cl from "clsx";
import React, {
HTMLAttributes,
cloneElement,
forwardRef,
HTMLAttributes,
useContext,
useMemo,
useRef,
useState,
} from "react";
import { ModalContext } from "../modal/ModalContext";
import { useProvider } from "../provider";
import { Detail } from "../typography";
import { mergeRefs, useId } from "../util";
import { useProvider } from "../provider";

export interface TooltipProps extends HTMLAttributes<HTMLDivElement> {
/**
Expand Down Expand Up @@ -110,7 +112,11 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
) => {
const [open, setOpen] = useState(defaultOpen);
const arrowRef = useRef<HTMLDivElement | null>(null);
const rootElement = useProvider()?.rootElement;
const modalContext = useContext(ModalContext);
const providerRootElement = useProvider()?.rootElement;
const rootElement = modalContext
? modalContext.ref.current
: providerRootElement;

const {
x,
Expand All @@ -133,7 +139,13 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
flip({ padding: 5, fallbackPlacements: ["bottom", "top"] }),
flArrow({ element: arrowRef, padding: 5 }),
],
whileElementsMounted: autoUpdate,
whileElementsMounted: modalContext
? (reference, floating, update) =>
// Reduces jumping in Chrome when used in a Modal and it's the first focusable element.
// Can be removed when autofocus starts working on <dialog> in Chrome. See also Modal.tsx
autoUpdate(reference, floating, update, { animationFrame: true })
: autoUpdate,
strategy: modalContext ? "fixed" : undefined,
});

const { getReferenceProps, getFloatingProps } = useInteractions([
Expand Down

0 comments on commit 0c7fc04

Please sign in to comment.