Skip to content

Commit 2631016

Browse files
authored
Merge 88e95e5 into 792eeab
2 parents 792eeab + 88e95e5 commit 2631016

File tree

5 files changed

+97
-20
lines changed

5 files changed

+97
-20
lines changed

.changeset/slimy-cameras-reflect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@navikt/ds-react": minor
3+
---
4+
5+
:sparkles: Modal: Støtte for å lukke ved klikk utenfor

@navikt/core/react/src/modal/Modal.tsx

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,13 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
8282
open,
8383
onBeforeClose,
8484
onCancel,
85+
closeOnBackdropClick,
8586
width,
8687
portal,
8788
className,
8889
"aria-labelledby": ariaLabelledby,
8990
style,
91+
onClick,
9092
...rest
9193
}: ModalProps,
9294
ref
@@ -133,30 +135,52 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
133135
const isWidthPreset =
134136
typeof width === "string" && ["small", "medium"].includes(width);
135137

138+
const mergedClassName = cl("navds-modal", className, {
139+
"navds-modal--polyfilled": needPolyfill,
140+
"navds-modal--autowidth": !width,
141+
[`navds-modal--${width}`]: isWidthPreset,
142+
});
143+
144+
const mergedStyle = {
145+
...style,
146+
...(!isWidthPreset ? { width } : {}),
147+
};
148+
149+
const mergedOnCancel: React.DialogHTMLAttributes<HTMLDialogElement>["onCancel"] =
150+
(event) => {
151+
if (onBeforeClose && onBeforeClose() === false) {
152+
event.preventDefault();
153+
} else if (onCancel) onCancel(event);
154+
};
155+
156+
const mergedOnClick =
157+
closeOnBackdropClick && !needPolyfill // closeOnBackdropClick has issues on polyfill when nesting modals (DatePicker)
158+
? (event: React.MouseEvent<HTMLDialogElement>) => {
159+
onClick && onClick(event);
160+
if (
161+
event.target === modalRef.current &&
162+
(!onBeforeClose || onBeforeClose() !== false)
163+
) {
164+
modalRef.current.close();
165+
}
166+
}
167+
: onClick;
168+
169+
const mergedAriaLabelledBy =
170+
!ariaLabelledby && !rest["aria-label"] && header
171+
? ariaLabelId
172+
: ariaLabelledby;
173+
136174
const component = (
175+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
137176
<dialog
138177
{...rest}
139178
ref={mergedRef}
140-
className={cl("navds-modal", className, {
141-
"navds-modal--polyfilled": needPolyfill,
142-
"navds-modal--autowidth": !width,
143-
[`navds-modal--${width}`]: isWidthPreset,
144-
})}
145-
style={{
146-
...style,
147-
...(!isWidthPreset ? { width } : {}),
148-
}}
149-
onCancel={(event) => {
150-
// FYI: onCancel fires when you press Esc
151-
if (onBeforeClose && onBeforeClose() === false) {
152-
event.preventDefault();
153-
} else if (onCancel) onCancel(event);
154-
}}
155-
aria-labelledby={
156-
!ariaLabelledby && !rest["aria-label"] && header
157-
? ariaLabelId
158-
: ariaLabelledby
159-
}
179+
className={mergedClassName}
180+
style={mergedStyle}
181+
onCancel={mergedOnCancel} // FYI: onCancel fires when you press Esc
182+
onClick={mergedOnClick}
183+
aria-labelledby={mergedAriaLabelledBy}
160184
>
161185
<ModalContext.Provider
162186
value={{

@navikt/core/react/src/modal/modal.stories.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const WithUseRef = () => {
2727
heading: "Title",
2828
size: "small",
2929
}}
30+
closeOnBackdropClick
3031
>
3132
<Modal.Body>
3233
<BodyLong spacing>
@@ -45,6 +46,7 @@ export const WithUseRef = () => {
4546
onBeforeClose={() =>
4647
window.confirm("Are you sure you want to close the modal?")
4748
}
49+
closeOnBackdropClick
4850
aria-labelledby="heading123"
4951
>
5052
<Modal.Header>
@@ -111,6 +113,7 @@ export const WithUseState = () => {
111113
e.stopPropagation(); // onClose wil propagate to parent modal if not stopped
112114
setOpen2(false);
113115
}}
116+
closeOnBackdropClick
114117
aria-label="Nested modal"
115118
width={800}
116119
>

@navikt/core/react/src/modal/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ export interface ModalProps
4242
* Called when the user presses the Esc key, unless `onBeforeClose()` returns `false`.
4343
*/
4444
onCancel?: React.ReactEventHandler<HTMLDialogElement>;
45+
/**
46+
* Whether to close when clicking on the backdrop.
47+
*
48+
* **WARNING:** Users may click outside by accident. Don't use if closing can cause data loss, or the modal contains important info.
49+
* @default false
50+
*/
51+
closeOnBackdropClick?: boolean;
4552
/**
4653
* @default fit-content (up to 700px)
4754
* */
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { BodyLong, Button, Modal } from "@navikt/ds-react";
2+
import { withDsExample } from "components/website-modules/examples/withDsExample";
3+
import { useRef } from "react";
4+
5+
const Example = () => {
6+
const ref = useRef<HTMLDialogElement>(null);
7+
8+
return (
9+
<div className="py-16">
10+
<Button onClick={() => ref.current?.showModal()}>Åpne modal</Button>
11+
12+
<Modal ref={ref} header={{ heading: "Overskrift" }} closeOnBackdropClick>
13+
<Modal.Body>
14+
<BodyLong>
15+
Culpa aliquip ut cupidatat laborum minim quis ex in aliqua. Qui
16+
incididunt dolor do ad ut. Incididunt eiusmod nostrud deserunt duis
17+
laborum. Proident aute culpa qui nostrud velit adipisicing minim.
18+
Consequat aliqua aute dolor do sit Lorem nisi mollit velit. Aliqua
19+
exercitation non minim minim pariatur sunt laborum ipsum.
20+
Exercitation nostrud est laborum magna non non aliqua qui esse.
21+
</BodyLong>
22+
</Modal.Body>
23+
</Modal>
24+
</div>
25+
);
26+
};
27+
28+
export default withDsExample(Example);
29+
30+
/* Storybook story */
31+
export const Demo = {
32+
render: Example,
33+
};
34+
35+
export const args = {
36+
index: 5,
37+
desc: "Husk at det er lett å klikke utenfor ved et uhell. Ikke bruk 'closeOnBackdropClick' hvis det kan føre til at brukeren mister data eller går glipp av viktig informasjon.",
38+
};

0 commit comments

Comments
 (0)