@toptal/picasso-modal 100.0.1 → 100.1.1
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.
|
@@ -22,6 +22,8 @@ export interface Props extends BaseProps, HTMLAttributes<HTMLDivElement> {
|
|
|
22
22
|
container?: ContainerValue;
|
|
23
23
|
/** If `true`, the backdrop is not rendered */
|
|
24
24
|
hideBackdrop?: boolean;
|
|
25
|
+
/** If `true`, the backdrop is rendered even when nested in another dialog */
|
|
26
|
+
forceRenderBackdrop?: boolean;
|
|
25
27
|
/** Position of the modal relative to the browser's viewport */
|
|
26
28
|
align?: Alignment;
|
|
27
29
|
/** Animation lifecycle callbacks. Backed by [react-transition-group/Transition](https://reactcommunity.org/react-transition-group/transition#Transition-props) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/Modal/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAEtD,OAAO,KAAoD,MAAM,OAAO,CAAA;AACxE,OAAO,KAAK,EACV,SAAS,EACT,QAAQ,EACR,eAAe,EAChB,MAAM,wBAAwB,CAAA;AAc/B,KAAK,cAAc,GAAG,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAA;AACvD,KAAK,SAAS,GAAG,KAAK,GAAG,UAAU,CAAA;AAEnC,MAAM,WAAW,KAAM,SAAQ,SAAS,EAAE,cAAc,CAAC,cAAc,CAAC;IACtE,iCAAiC;IACjC,QAAQ,EAAE,SAAS,CAAA;IACnB,wCAAwC;IACxC,IAAI,EAAE,OAAO,CAAA;IACb,qBAAqB;IACrB,IAAI,CAAC,EACD,QAAQ,CAAC,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC,GAC5D,aAAa,CAAA;IACjB,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,oFAAoF;IACpF,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,2GAA2G;IAC3G,SAAS,CAAC,EAAE,cAAc,CAAA;IAC1B,8CAA8C;IAC9C,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,kKAAkK;IAClK,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,gFAAgF;IAChF,UAAU,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAA;IACjD,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;IACD,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;CACF;AAyDD,eAAO,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/Modal/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAEtD,OAAO,KAAoD,MAAM,OAAO,CAAA;AACxE,OAAO,KAAK,EACV,SAAS,EACT,QAAQ,EACR,eAAe,EAChB,MAAM,wBAAwB,CAAA;AAc/B,KAAK,cAAc,GAAG,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAA;AACvD,KAAK,SAAS,GAAG,KAAK,GAAG,UAAU,CAAA;AAEnC,MAAM,WAAW,KAAM,SAAQ,SAAS,EAAE,cAAc,CAAC,cAAc,CAAC;IACtE,iCAAiC;IACjC,QAAQ,EAAE,SAAS,CAAA;IACnB,wCAAwC;IACxC,IAAI,EAAE,OAAO,CAAA;IACb,qBAAqB;IACrB,IAAI,CAAC,EACD,QAAQ,CAAC,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC,GAC5D,aAAa,CAAA;IACjB,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,oFAAoF;IACpF,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,2GAA2G;IAC3G,SAAS,CAAC,EAAE,cAAc,CAAA;IAC1B,8CAA8C;IAC9C,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,+DAA+D;IAC/D,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,kKAAkK;IAClK,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,gFAAgF;IAChF,UAAU,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAA;IACjD,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;IACD,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;CACF;AAyDD,eAAO,MAAM,KAAK,8EAuMhB,CAAA;AAIF,eAAe,KAAK,CAAA"}
|
|
@@ -57,12 +57,14 @@ const generateKey = (() => {
|
|
|
57
57
|
})();
|
|
58
58
|
// eslint-disable-next-line react/display-name
|
|
59
59
|
export const Modal = forwardRef(function Modal(_a, ref) {
|
|
60
|
-
var { hideBackdrop = false, disableBackdropClick = false, size = 'medium', transitionDuration = 300, align = 'centered' } = _a, props = __rest(_a, ["hideBackdrop", "disableBackdropClick", "size", "transitionDuration", "align"]);
|
|
61
|
-
const { children, open, onBackdropClick, onClose, onOpen, onClick, className, style, container, paperProps, testIds, transitionProps, classes } = props, rest = __rest(props, ["children", "open", "onBackdropClick", "onClose", "onOpen", "onClick", "className", "style", "container", "paperProps", "testIds", "transitionProps", "classes"]);
|
|
60
|
+
var { hideBackdrop = false, forceRenderBackdrop = true, disableBackdropClick = false, size = 'medium', transitionDuration = 300, align = 'centered' } = _a, props = __rest(_a, ["hideBackdrop", "forceRenderBackdrop", "disableBackdropClick", "size", "transitionDuration", "align"]);
|
|
61
|
+
const { children, open, onBackdropClick, onClose, onOpen, onClick, onMouseDown, onMouseUp, className, style, container, paperProps, testIds, transitionProps, classes } = props, rest = __rest(props, ["children", "open", "onBackdropClick", "onClose", "onOpen", "onClick", "onMouseDown", "onMouseUp", "className", "style", "container", "paperProps", "testIds", "transitionProps", "classes"]);
|
|
62
62
|
const picassoRootContainer = usePicassoRoot();
|
|
63
63
|
const modalRef = useCombinedRefs(ref, useRef(null));
|
|
64
64
|
const modalId = useRef(generateKey());
|
|
65
65
|
const wasOpen = useRef(false);
|
|
66
|
+
const mouseDownOnPopupRef = useRef(false);
|
|
67
|
+
const mouseUpOnPopupRef = useRef(false);
|
|
66
68
|
const { rootRef } = useContext(RootContext);
|
|
67
69
|
useEffect(() => {
|
|
68
70
|
const handleDocumentFocus = () => {
|
|
@@ -111,8 +113,21 @@ export const Modal = forwardRef(function Modal(_a, ref) {
|
|
|
111
113
|
const durationStyle = { transitionDuration: `${duration}ms` };
|
|
112
114
|
const resolvedContainer = (typeof container === 'function' ? container() : container) ||
|
|
113
115
|
picassoRootContainer;
|
|
116
|
+
const handlePopupMouseDown = (event) => {
|
|
117
|
+
mouseDownOnPopupRef.current = event.target === event.currentTarget;
|
|
118
|
+
onMouseDown === null || onMouseDown === void 0 ? void 0 : onMouseDown(event);
|
|
119
|
+
};
|
|
120
|
+
const handlePopupMouseUp = (event) => {
|
|
121
|
+
mouseUpOnPopupRef.current = event.target === event.currentTarget;
|
|
122
|
+
onMouseUp === null || onMouseUp === void 0 ? void 0 : onMouseUp(event);
|
|
123
|
+
};
|
|
114
124
|
const handlePopupClick = (event) => {
|
|
115
|
-
|
|
125
|
+
// a drag between the backdrop and the content dispatches its click on the
|
|
126
|
+
// common ancestor — this popup — so both ends must have been on it
|
|
127
|
+
if (mouseDownOnPopupRef.current &&
|
|
128
|
+
mouseUpOnPopupRef.current &&
|
|
129
|
+
event.target === event.currentTarget &&
|
|
130
|
+
!disableBackdropClick) {
|
|
116
131
|
onBackdropClick === null || onBackdropClick === void 0 ? void 0 : onBackdropClick();
|
|
117
132
|
onClose === null || onClose === void 0 ? void 0 : onClose();
|
|
118
133
|
}
|
|
@@ -129,13 +144,13 @@ export const Modal = forwardRef(function Modal(_a, ref) {
|
|
|
129
144
|
}
|
|
130
145
|
} },
|
|
131
146
|
React.createElement(BaseUIDialog.Portal, { container: resolvedContainer },
|
|
132
|
-
!hideBackdrop && (React.createElement(BaseUIDialog.Backdrop, { className: 'fixed z-modal inset-0 bg-black/50 transition-opacity data-starting-style:opacity-0 data-ending-style:opacity-0', style: durationStyle })),
|
|
147
|
+
!hideBackdrop && (React.createElement(BaseUIDialog.Backdrop, { forceRender: forceRenderBackdrop, className: 'fixed z-modal inset-0 bg-black/50 transition-opacity data-starting-style:opacity-0 data-ending-style:opacity-0', style: durationStyle })),
|
|
133
148
|
React.createElement(BaseUIDialog.Popup, Object.assign({}, rest, { ref: modalRef, initialFocus: () => {
|
|
134
149
|
var _a;
|
|
135
150
|
return ((_a = modalRef.current) === null || _a === void 0 ? void 0 : _a.contains(document.activeElement))
|
|
136
151
|
? undefined
|
|
137
152
|
: modalRef.current;
|
|
138
|
-
}, className: twMerge('fixed z-modal inset-0 flex flex-col text-lg leading-[normal] justify-center items-center transition-opacity data-starting-style:opacity-0 data-ending-style:opacity-0', className), style: Object.assign(Object.assign({}, style), durationStyle), onClick: handlePopupClick }),
|
|
153
|
+
}, className: twMerge('fixed z-modal inset-0 flex flex-col text-lg leading-[normal] justify-center items-center transition-opacity data-starting-style:opacity-0 data-ending-style:opacity-0', className), style: Object.assign(Object.assign({}, style), durationStyle), onClick: handlePopupClick, onMouseDown: handlePopupMouseDown, onMouseUp: handlePopupMouseUp }),
|
|
139
154
|
React.createElement(ModalPaper, Object.assign({ size: size, align: align, tabIndex: -1 }, paperProps),
|
|
140
155
|
React.createElement(ModalContext.Provider, { value: true },
|
|
141
156
|
children,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../../src/Modal/Modal.tsx"],"names":[],"mappings":";;;;;;;;;;;AACA,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAMxE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EACL,eAAe,EACf,YAAY,EACZ,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,YAAY,MAAM,+BAA+B,CAAA;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../../src/Modal/Modal.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toptal/picasso-modal",
|
|
3
|
-
"version": "100.
|
|
3
|
+
"version": "100.1.1",
|
|
4
4
|
"description": "Toptal UI components library - Modal",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@base-ui/react": "^1.6.0",
|
|
22
22
|
"debounce": "^1.2.1",
|
|
23
|
-
"@toptal/picasso-button": "100.0.1",
|
|
24
|
-
"@toptal/picasso-shared": "100.0.0",
|
|
25
23
|
"@toptal/picasso-paper": "100.0.0",
|
|
26
|
-
"@toptal/picasso-
|
|
24
|
+
"@toptal/picasso-shared": "100.0.0",
|
|
25
|
+
"@toptal/picasso-button": "100.0.1",
|
|
27
26
|
"@toptal/picasso-typography": "100.0.0",
|
|
28
27
|
"@toptal/picasso-utils": "100.0.0",
|
|
29
|
-
"@toptal/picasso-modal-context": "100.0.0"
|
|
28
|
+
"@toptal/picasso-modal-context": "100.0.0",
|
|
29
|
+
"@toptal/picasso-icons": "100.0.0"
|
|
30
30
|
},
|
|
31
31
|
"sideEffects": [
|
|
32
32
|
"**/styles.ts",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
".": "./dist-package/src/index.js"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@toptal/picasso-test-utils": "100.0.0",
|
|
46
45
|
"@toptal/picasso-provider": "100.0.0",
|
|
47
|
-
"@toptal/picasso-tailwind-merge": "100.0.0"
|
|
46
|
+
"@toptal/picasso-tailwind-merge": "100.0.0",
|
|
47
|
+
"@toptal/picasso-test-utils": "100.0.0"
|
|
48
48
|
},
|
|
49
49
|
"files": [
|
|
50
50
|
"dist-package/**",
|
package/src/Modal/Modal.tsx
CHANGED
|
@@ -43,6 +43,8 @@ export interface Props extends BaseProps, HTMLAttributes<HTMLDivElement> {
|
|
|
43
43
|
container?: ContainerValue
|
|
44
44
|
/** If `true`, the backdrop is not rendered */
|
|
45
45
|
hideBackdrop?: boolean
|
|
46
|
+
/** If `true`, the backdrop is rendered even when nested in another dialog */
|
|
47
|
+
forceRenderBackdrop?: boolean
|
|
46
48
|
/** Position of the modal relative to the browser's viewport */
|
|
47
49
|
align?: Alignment
|
|
48
50
|
/** Animation lifecycle callbacks. Backed by [react-transition-group/Transition](https://reactcommunity.org/react-transition-group/transition#Transition-props) */
|
|
@@ -116,6 +118,7 @@ const generateKey = (() => {
|
|
|
116
118
|
export const Modal = forwardRef<HTMLDivElement, Props>(function Modal(
|
|
117
119
|
{
|
|
118
120
|
hideBackdrop = false,
|
|
121
|
+
forceRenderBackdrop = true,
|
|
119
122
|
disableBackdropClick = false,
|
|
120
123
|
size = 'medium',
|
|
121
124
|
transitionDuration = 300,
|
|
@@ -131,6 +134,8 @@ export const Modal = forwardRef<HTMLDivElement, Props>(function Modal(
|
|
|
131
134
|
onClose,
|
|
132
135
|
onOpen,
|
|
133
136
|
onClick,
|
|
137
|
+
onMouseDown,
|
|
138
|
+
onMouseUp,
|
|
134
139
|
className,
|
|
135
140
|
style,
|
|
136
141
|
container,
|
|
@@ -147,6 +152,8 @@ export const Modal = forwardRef<HTMLDivElement, Props>(function Modal(
|
|
|
147
152
|
)
|
|
148
153
|
const modalId = useRef(generateKey())
|
|
149
154
|
const wasOpen = useRef(false)
|
|
155
|
+
const mouseDownOnPopupRef = useRef(false)
|
|
156
|
+
const mouseUpOnPopupRef = useRef(false)
|
|
150
157
|
const { rootRef } = useContext(RootContext)
|
|
151
158
|
|
|
152
159
|
useEffect(() => {
|
|
@@ -215,8 +222,27 @@ export const Modal = forwardRef<HTMLDivElement, Props>(function Modal(
|
|
|
215
222
|
(typeof container === 'function' ? container() : container) ||
|
|
216
223
|
picassoRootContainer
|
|
217
224
|
|
|
225
|
+
const handlePopupMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
|
|
226
|
+
mouseDownOnPopupRef.current = event.target === event.currentTarget
|
|
227
|
+
|
|
228
|
+
onMouseDown?.(event)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const handlePopupMouseUp = (event: React.MouseEvent<HTMLDivElement>) => {
|
|
232
|
+
mouseUpOnPopupRef.current = event.target === event.currentTarget
|
|
233
|
+
|
|
234
|
+
onMouseUp?.(event)
|
|
235
|
+
}
|
|
236
|
+
|
|
218
237
|
const handlePopupClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
|
219
|
-
|
|
238
|
+
// a drag between the backdrop and the content dispatches its click on the
|
|
239
|
+
// common ancestor — this popup — so both ends must have been on it
|
|
240
|
+
if (
|
|
241
|
+
mouseDownOnPopupRef.current &&
|
|
242
|
+
mouseUpOnPopupRef.current &&
|
|
243
|
+
event.target === event.currentTarget &&
|
|
244
|
+
!disableBackdropClick
|
|
245
|
+
) {
|
|
220
246
|
onBackdropClick?.()
|
|
221
247
|
onClose?.()
|
|
222
248
|
}
|
|
@@ -243,6 +269,7 @@ export const Modal = forwardRef<HTMLDivElement, Props>(function Modal(
|
|
|
243
269
|
<BaseUIDialog.Portal container={resolvedContainer}>
|
|
244
270
|
{!hideBackdrop && (
|
|
245
271
|
<BaseUIDialog.Backdrop
|
|
272
|
+
forceRender={forceRenderBackdrop}
|
|
246
273
|
className='fixed z-modal inset-0 bg-black/50 transition-opacity data-starting-style:opacity-0 data-ending-style:opacity-0'
|
|
247
274
|
style={durationStyle}
|
|
248
275
|
/>
|
|
@@ -261,6 +288,8 @@ export const Modal = forwardRef<HTMLDivElement, Props>(function Modal(
|
|
|
261
288
|
)}
|
|
262
289
|
style={{ ...style, ...durationStyle }}
|
|
263
290
|
onClick={handlePopupClick}
|
|
291
|
+
onMouseDown={handlePopupMouseDown}
|
|
292
|
+
onMouseUp={handlePopupMouseUp}
|
|
264
293
|
>
|
|
265
294
|
<ModalPaper size={size} align={align} tabIndex={-1} {...paperProps}>
|
|
266
295
|
<ModalContext.Provider value>
|
package/src/Modal/test.tsx
CHANGED
|
@@ -207,6 +207,146 @@ describe('Modal', () => {
|
|
|
207
207
|
expect(baseElement).toMatchSnapshot()
|
|
208
208
|
})
|
|
209
209
|
|
|
210
|
+
describe('backdrop', () => {
|
|
211
|
+
const getBackdrops = (element: HTMLElement) =>
|
|
212
|
+
element.querySelectorAll('[role="presentation"]')
|
|
213
|
+
|
|
214
|
+
it('renders a backdrop for a nested modal by default', () => {
|
|
215
|
+
const { baseElement } = render(
|
|
216
|
+
<Modal open>
|
|
217
|
+
<p>Outer modal content</p>
|
|
218
|
+
<Modal open>
|
|
219
|
+
<p>Nested modal content</p>
|
|
220
|
+
</Modal>
|
|
221
|
+
</Modal>
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
expect(getBackdrops(baseElement)).toHaveLength(2)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('given forceRenderBackdrop is false, does not render a backdrop for a nested modal', () => {
|
|
228
|
+
const { baseElement } = render(
|
|
229
|
+
<Modal open>
|
|
230
|
+
<p>Outer modal content</p>
|
|
231
|
+
<Modal open forceRenderBackdrop={false}>
|
|
232
|
+
<p>Nested modal content</p>
|
|
233
|
+
</Modal>
|
|
234
|
+
</Modal>
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
expect(getBackdrops(baseElement)).toHaveLength(1)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('given hideBackdrop is true, does not render a backdrop', () => {
|
|
241
|
+
const { baseElement } = render(
|
|
242
|
+
<Modal open hideBackdrop>
|
|
243
|
+
<p>Modal content</p>
|
|
244
|
+
</Modal>
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
expect(getBackdrops(baseElement)).toHaveLength(0)
|
|
248
|
+
})
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
describe('backdrop click', () => {
|
|
252
|
+
const renderModal = (props?: Partial<ModalProps>) => {
|
|
253
|
+
const onClose = jest.fn()
|
|
254
|
+
const onBackdropClick = jest.fn()
|
|
255
|
+
|
|
256
|
+
const { baseElement } = render(
|
|
257
|
+
<Modal
|
|
258
|
+
open
|
|
259
|
+
onClose={onClose}
|
|
260
|
+
onBackdropClick={onBackdropClick}
|
|
261
|
+
{...props}
|
|
262
|
+
>
|
|
263
|
+
<Modal.Content>
|
|
264
|
+
<input />
|
|
265
|
+
</Modal.Content>
|
|
266
|
+
</Modal>
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
onClose,
|
|
271
|
+
onBackdropClick,
|
|
272
|
+
// the popup is the transparent full-screen element over the backdrop
|
|
273
|
+
popup: baseElement.querySelector('[role="dialog"]') as HTMLElement,
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// a pointer interaction fires mousedown → mouseup → click, and the browser
|
|
278
|
+
// dispatches the click on the nearest common ancestor of the press and the
|
|
279
|
+
// release target — the popup itself whenever the two differ
|
|
280
|
+
const press = ({
|
|
281
|
+
from,
|
|
282
|
+
to,
|
|
283
|
+
popup,
|
|
284
|
+
}: {
|
|
285
|
+
from: HTMLElement
|
|
286
|
+
to: HTMLElement
|
|
287
|
+
popup: HTMLElement
|
|
288
|
+
}) => {
|
|
289
|
+
fireEvent.mouseDown(from)
|
|
290
|
+
fireEvent.mouseUp(to)
|
|
291
|
+
fireEvent.click(from === to ? from : popup)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
it('closes when pressing and releasing on the backdrop', () => {
|
|
295
|
+
const { popup, onClose, onBackdropClick } = renderModal()
|
|
296
|
+
|
|
297
|
+
press({ from: popup, to: popup, popup })
|
|
298
|
+
|
|
299
|
+
expect(onBackdropClick).toHaveBeenCalled()
|
|
300
|
+
expect(onClose).toHaveBeenCalled()
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('given the press started inside the content, does not close', () => {
|
|
304
|
+
const { popup, onClose, onBackdropClick } = renderModal()
|
|
305
|
+
|
|
306
|
+
// selecting text in a field and releasing outside the paper
|
|
307
|
+
press({ from: screen.getByRole('textbox'), to: popup, popup })
|
|
308
|
+
|
|
309
|
+
expect(onBackdropClick).not.toHaveBeenCalled()
|
|
310
|
+
expect(onClose).not.toHaveBeenCalled()
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
it('given the press ended inside the content, does not close', () => {
|
|
314
|
+
const { popup, onClose, onBackdropClick } = renderModal()
|
|
315
|
+
|
|
316
|
+
// selecting from outside the paper into a field
|
|
317
|
+
press({ from: popup, to: screen.getByRole('textbox'), popup })
|
|
318
|
+
|
|
319
|
+
expect(onBackdropClick).not.toHaveBeenCalled()
|
|
320
|
+
expect(onClose).not.toHaveBeenCalled()
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('given disableBackdropClick, does not close on a backdrop click', () => {
|
|
324
|
+
const { popup, onClose } = renderModal({ disableBackdropClick: true })
|
|
325
|
+
|
|
326
|
+
press({ from: popup, to: popup, popup })
|
|
327
|
+
|
|
328
|
+
expect(onClose).not.toHaveBeenCalled()
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
it('forwards onMouseUp', () => {
|
|
332
|
+
const onMouseUp = jest.fn()
|
|
333
|
+
const { popup } = renderModal({ onMouseUp })
|
|
334
|
+
|
|
335
|
+
fireEvent.mouseUp(popup)
|
|
336
|
+
|
|
337
|
+
expect(onMouseUp).toHaveBeenCalled()
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
it('forwards onMouseDown', () => {
|
|
341
|
+
const onMouseDown = jest.fn()
|
|
342
|
+
const { popup } = renderModal({ onMouseDown })
|
|
343
|
+
|
|
344
|
+
fireEvent.mouseDown(popup)
|
|
345
|
+
|
|
346
|
+
expect(onMouseDown).toHaveBeenCalled()
|
|
347
|
+
})
|
|
348
|
+
})
|
|
349
|
+
|
|
210
350
|
describe('page scroll lock', () => {
|
|
211
351
|
afterEach(() => {
|
|
212
352
|
cleanup()
|