@navikt/ds-react 5.7.6 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_docs.json +30 -9
- package/cjs/accordion/AccordionHeader.js +2 -2
- package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js +1 -2
- package/cjs/form/combobox/Input/Input.js +13 -5
- package/cjs/layout/sidemal-test/Sidebar.js +1 -1
- package/cjs/loader/Loader.js +1 -1
- package/cjs/modal/Modal.js +35 -14
- package/cjs/tooltip/Tooltip.js +14 -3
- package/esm/accordion/AccordionHeader.js +2 -2
- package/esm/accordion/AccordionHeader.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js +1 -2
- package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
- package/esm/form/combobox/Input/Input.js +13 -5
- package/esm/form/combobox/Input/Input.js.map +1 -1
- package/esm/layout/bleed/Bleed.d.ts +1 -1
- package/esm/layout/bleed/Bleed.js +1 -1
- package/esm/layout/bleed/Bleed.js.map +1 -1
- package/esm/layout/box/Box.d.ts +1 -2
- package/esm/layout/box/Box.js +1 -1
- package/esm/layout/box/Box.js.map +1 -1
- package/esm/layout/grid/HGrid.d.ts +1 -1
- package/esm/layout/grid/HGrid.js +1 -1
- package/esm/layout/grid/HGrid.js.map +1 -1
- package/esm/layout/responsive/Responsive.d.ts +1 -1
- package/esm/layout/sidemal-test/Sidebar.js +1 -1
- package/esm/layout/sidemal-test/Sidebar.js.map +1 -1
- package/esm/layout/stack/Stack.d.ts +1 -1
- package/esm/layout/stack/Stack.js +1 -1
- package/esm/layout/stack/Stack.js.map +1 -1
- package/esm/layout/utilities/css.d.ts +1 -8
- package/esm/layout/utilities/css.js.map +1 -1
- package/esm/layout/utilities/types.d.ts +9 -0
- package/esm/loader/Loader.d.ts +1 -1
- package/esm/loader/Loader.js +1 -1
- package/esm/modal/Modal.js +35 -14
- package/esm/modal/Modal.js.map +1 -1
- package/esm/modal/ModalContext.d.ts +1 -0
- package/esm/modal/ModalContext.js.map +1 -1
- package/esm/modal/types.d.ts +7 -0
- package/esm/tooltip/Tooltip.js +16 -5
- package/esm/tooltip/Tooltip.js.map +1 -1
- package/package.json +3 -3
- package/src/accordion/AccordionHeader.tsx +3 -3
- package/src/form/combobox/FilteredOptions/filteredOptionsContext.tsx +1 -2
- package/src/form/combobox/Input/Input.tsx +19 -4
- package/src/form/combobox/combobox.stories.tsx +44 -0
- package/src/layout/bleed/Bleed.tsx +2 -5
- package/src/layout/box/Box.tsx +1 -3
- package/src/layout/grid/HGrid.tsx +2 -6
- package/src/layout/responsive/Responsive.tsx +1 -1
- package/src/layout/sidemal-test/Sidebar.tsx +1 -1
- package/src/layout/stack/Stack.tsx +2 -6
- package/src/layout/utilities/css.ts +1 -36
- package/src/layout/utilities/types.ts +16 -0
- package/src/loader/Loader.tsx +1 -1
- package/src/modal/Modal.tsx +50 -20
- package/src/modal/ModalContext.ts +1 -0
- package/src/modal/modal.stories.tsx +30 -2
- package/src/modal/types.ts +7 -0
- package/src/tooltip/Tooltip.tsx +18 -6
package/src/modal/Modal.tsx
CHANGED
|
@@ -82,11 +82,13 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
|
|
|
82
82
|
open,
|
|
83
83
|
onBeforeClose,
|
|
84
84
|
onCancel,
|
|
85
|
+
closeOnBackdropClick,
|
|
85
86
|
width,
|
|
86
87
|
portal,
|
|
87
88
|
className,
|
|
88
89
|
"aria-labelledby": ariaLabelledby,
|
|
89
90
|
style,
|
|
91
|
+
onClick,
|
|
90
92
|
...rest
|
|
91
93
|
}: ModalProps,
|
|
92
94
|
ref
|
|
@@ -108,6 +110,11 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
|
|
|
108
110
|
if (needPolyfill && modalRef.current && portalNode) {
|
|
109
111
|
dialogPolyfill.registerDialog(modalRef.current);
|
|
110
112
|
}
|
|
113
|
+
// We set autofocus on the dialog element to prevent the default behavior where first focusable element gets focus when modal is opened.
|
|
114
|
+
// 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.
|
|
115
|
+
// We have to use JS because it doesn't work to set it with a prop (React bug?)
|
|
116
|
+
// Currently doesn't seem to work in Chrome. See also Tooltip.tsx
|
|
117
|
+
if (modalRef.current && portalNode) modalRef.current.autofocus = true;
|
|
111
118
|
}, [modalRef, portalNode]);
|
|
112
119
|
|
|
113
120
|
useEffect(() => {
|
|
@@ -128,34 +135,57 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
|
|
|
128
135
|
const isWidthPreset =
|
|
129
136
|
typeof width === "string" && ["small", "medium"].includes(width);
|
|
130
137
|
|
|
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
|
+
|
|
131
174
|
const component = (
|
|
175
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
|
|
132
176
|
<dialog
|
|
133
177
|
{...rest}
|
|
134
178
|
ref={mergedRef}
|
|
135
|
-
className={
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
style={{
|
|
141
|
-
...style,
|
|
142
|
-
...(!isWidthPreset ? { width } : {}),
|
|
143
|
-
}}
|
|
144
|
-
onCancel={(event) => {
|
|
145
|
-
// FYI: onCancel fires when you press Esc
|
|
146
|
-
if (onBeforeClose && onBeforeClose() === false) {
|
|
147
|
-
event.preventDefault();
|
|
148
|
-
} else if (onCancel) onCancel(event);
|
|
149
|
-
}}
|
|
150
|
-
aria-labelledby={
|
|
151
|
-
!ariaLabelledby && !rest["aria-label"] && header
|
|
152
|
-
? ariaLabelId
|
|
153
|
-
: ariaLabelledby
|
|
154
|
-
}
|
|
179
|
+
className={mergedClassName}
|
|
180
|
+
style={mergedStyle}
|
|
181
|
+
onCancel={mergedOnCancel} // FYI: onCancel fires when you press Esc
|
|
182
|
+
onClick={mergedOnClick}
|
|
183
|
+
aria-labelledby={mergedAriaLabelledBy}
|
|
155
184
|
>
|
|
156
185
|
<ModalContext.Provider
|
|
157
186
|
value={{
|
|
158
187
|
closeHandler: getCloseHandler(modalRef, header, onBeforeClose),
|
|
188
|
+
ref: modalRef,
|
|
159
189
|
}}
|
|
160
190
|
>
|
|
161
191
|
{header && (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React, { useRef, useState } from "react";
|
|
2
1
|
import { FileIcon } from "@navikt/aksel-icons";
|
|
3
|
-
import {
|
|
2
|
+
import React, { useRef, useState } from "react";
|
|
3
|
+
import { BodyLong, Button, Heading, Tooltip } from "..";
|
|
4
4
|
import Modal from "./Modal";
|
|
5
5
|
|
|
6
6
|
export default {
|
|
@@ -27,6 +27,7 @@ export const WithUseRef = () => {
|
|
|
27
27
|
heading: "Title",
|
|
28
28
|
size: "small",
|
|
29
29
|
}}
|
|
30
|
+
closeOnBackdropClick
|
|
30
31
|
>
|
|
31
32
|
<Modal.Body>
|
|
32
33
|
<BodyLong spacing>
|
|
@@ -45,6 +46,7 @@ export const WithUseRef = () => {
|
|
|
45
46
|
onBeforeClose={() =>
|
|
46
47
|
window.confirm("Are you sure you want to close the modal?")
|
|
47
48
|
}
|
|
49
|
+
closeOnBackdropClick
|
|
48
50
|
aria-labelledby="heading123"
|
|
49
51
|
>
|
|
50
52
|
<Modal.Header>
|
|
@@ -111,6 +113,7 @@ export const WithUseState = () => {
|
|
|
111
113
|
e.stopPropagation(); // onClose wil propagate to parent modal if not stopped
|
|
112
114
|
setOpen2(false);
|
|
113
115
|
}}
|
|
116
|
+
closeOnBackdropClick
|
|
114
117
|
aria-label="Nested modal"
|
|
115
118
|
width={800}
|
|
116
119
|
>
|
|
@@ -165,3 +168,28 @@ export const MediumWithPortal = () => (
|
|
|
165
168
|
<Modal.Body>Lorem ipsum dolor sit amet.</Modal.Body>
|
|
166
169
|
</Modal>
|
|
167
170
|
);
|
|
171
|
+
|
|
172
|
+
export const WithTooltip = () => {
|
|
173
|
+
const ref = useRef<HTMLDialogElement>(null);
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<div>
|
|
177
|
+
<Button onClick={() => ref.current?.showModal()}>Open Modal</Button>
|
|
178
|
+
<Modal
|
|
179
|
+
open={ref.current ? undefined : true /* initially open */}
|
|
180
|
+
ref={ref}
|
|
181
|
+
>
|
|
182
|
+
<Modal.Body>
|
|
183
|
+
<div style={{ marginBottom: "1rem" }}>
|
|
184
|
+
<Tooltip content="This_is_the_first_tooltip">
|
|
185
|
+
<Button>Test 1</Button>
|
|
186
|
+
</Tooltip>
|
|
187
|
+
</div>
|
|
188
|
+
<Tooltip content="This is the second tooltip">
|
|
189
|
+
<Button>Test 2</Button>
|
|
190
|
+
</Tooltip>
|
|
191
|
+
</Modal.Body>
|
|
192
|
+
</Modal>
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
};
|
package/src/modal/types.ts
CHANGED
|
@@ -42,6 +42,13 @@ export interface ModalProps
|
|
|
42
42
|
* Called when the user presses the Esc key, unless `onBeforeClose()` returns `false`.
|
|
43
43
|
*/
|
|
44
44
|
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;
|
|
45
52
|
/**
|
|
46
53
|
* @default fit-content (up to 700px)
|
|
47
54
|
* */
|
package/src/tooltip/Tooltip.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
FloatingPortal,
|
|
3
3
|
autoUpdate,
|
|
4
|
+
arrow as flArrow,
|
|
4
5
|
flip,
|
|
5
|
-
FloatingPortal,
|
|
6
6
|
offset,
|
|
7
7
|
safePolygon,
|
|
8
8
|
shift,
|
|
@@ -14,16 +14,18 @@ import {
|
|
|
14
14
|
} from "@floating-ui/react";
|
|
15
15
|
import cl from "clsx";
|
|
16
16
|
import React, {
|
|
17
|
+
HTMLAttributes,
|
|
17
18
|
cloneElement,
|
|
18
19
|
forwardRef,
|
|
19
|
-
|
|
20
|
+
useContext,
|
|
20
21
|
useMemo,
|
|
21
22
|
useRef,
|
|
22
23
|
useState,
|
|
23
24
|
} from "react";
|
|
25
|
+
import { ModalContext } from "../modal/ModalContext";
|
|
26
|
+
import { useProvider } from "../provider";
|
|
24
27
|
import { Detail } from "../typography";
|
|
25
28
|
import { mergeRefs, useId } from "../util";
|
|
26
|
-
import { useProvider } from "../provider";
|
|
27
29
|
|
|
28
30
|
export interface TooltipProps extends HTMLAttributes<HTMLDivElement> {
|
|
29
31
|
/**
|
|
@@ -110,7 +112,11 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
|
|
|
110
112
|
) => {
|
|
111
113
|
const [open, setOpen] = useState(defaultOpen);
|
|
112
114
|
const arrowRef = useRef<HTMLDivElement | null>(null);
|
|
113
|
-
const
|
|
115
|
+
const modalContext = useContext(ModalContext);
|
|
116
|
+
const providerRootElement = useProvider()?.rootElement;
|
|
117
|
+
const rootElement = modalContext
|
|
118
|
+
? modalContext.ref.current
|
|
119
|
+
: providerRootElement;
|
|
114
120
|
|
|
115
121
|
const {
|
|
116
122
|
x,
|
|
@@ -133,7 +139,13 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
|
|
|
133
139
|
flip({ padding: 5, fallbackPlacements: ["bottom", "top"] }),
|
|
134
140
|
flArrow({ element: arrowRef, padding: 5 }),
|
|
135
141
|
],
|
|
136
|
-
whileElementsMounted:
|
|
142
|
+
whileElementsMounted: modalContext
|
|
143
|
+
? (reference, floating, update) =>
|
|
144
|
+
// Reduces jumping in Chrome when used in a Modal and it's the first focusable element.
|
|
145
|
+
// Can be removed when autofocus starts working on <dialog> in Chrome. See also Modal.tsx
|
|
146
|
+
autoUpdate(reference, floating, update, { animationFrame: true })
|
|
147
|
+
: autoUpdate,
|
|
148
|
+
strategy: modalContext ? "fixed" : undefined,
|
|
137
149
|
});
|
|
138
150
|
|
|
139
151
|
const { getReferenceProps, getFloatingProps } = useInteractions([
|