@edux-design/modals 0.1.1 → 0.1.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @edux-design/modals
2
2
 
3
+ ## 0.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated design tokens
8
+
3
9
  ## 0.1.1
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -34,9 +34,9 @@ import { Button } from "@edux-design/buttons";
34
34
 
35
35
  export function PublishModal() {
36
36
  return (
37
- <Modal>
37
+ <Modal onClose={() => {}}>
38
38
  <ModalTrigger asChild>
39
- <Button variant="primary">Open Modal</Button>
39
+ <Button>Open Modal</Button>
40
40
  </ModalTrigger>
41
41
  <ModalContent size="medium">
42
42
  <ModalHeader>
@@ -50,9 +50,9 @@ export function PublishModal() {
50
50
  </ModalBody>
51
51
  <ModalFooter>
52
52
  <ModalClose asChild>
53
- <Button variant="secondary">Cancel</Button>
53
+ <Button isSecondary>Cancel</Button>
54
54
  </ModalClose>
55
- <Button variant="primary">Publish</Button>
55
+ <Button>Publish</Button>
56
56
  </ModalFooter>
57
57
  </ModalContent>
58
58
  </Modal>
@@ -60,4 +60,6 @@ export function PublishModal() {
60
60
  }
61
61
  ```
62
62
 
63
- `ModalContent` accepts `size`, `backdropProps`, `backDropProps`, `className`, and `contentProps`. Storybook examples live in `src/demos/Modal.stories.jsx`.
63
+ Passing `onClose` to `Modal` also enables the default close icon inside `ModalHeader`.
64
+
65
+ `ModalContent` accepts `size`, `backdropProps`, the legacy alias `backDropProps`, `className`, and `contentProps`. Storybook examples live in `src/demos/Modal.stories.jsx`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edux-design/modals",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css"
@@ -15,11 +15,15 @@
15
15
  },
16
16
  "scripts": {
17
17
  "lint": "eslint . --max-warnings 0",
18
- "build": "tsup src/index.js --format esm,cjs --external react,react-dom,@edux-design/utils,@radix-ui/react-dialog",
19
- "dev": "tsup src/index.js --watch --format esm,cjs --external react,react-dom,@edux-design/utils,@radix-ui/react-dialog",
18
+ "build": "tsup src/index.js --format esm,cjs --external react,react-dom,@edux-design/utils,@edux-design/buttons,@edux-design/icons,@radix-ui/react-dialog",
19
+ "dev": "tsup src/index.js --watch --format esm,cjs --external react,react-dom,@edux-design/utils,@edux-design/buttons,@edux-design/icons,@radix-ui/react-dialog",
20
20
  "generate:component": "turbo gen react-component",
21
21
  "check-types": "tsc --noEmit"
22
22
  },
23
+ "dependencies": {
24
+ "@edux-design/buttons": "*",
25
+ "@edux-design/icons": "*"
26
+ },
23
27
  "peerDependencies": {
24
28
  "react": ">=18",
25
29
  "react-dom": ">=18",
@@ -12,6 +12,21 @@ import {
12
12
  } from "../index";
13
13
  import { Button } from "@edux-design/buttons";
14
14
 
15
+ const loremIpsumParagraphs = [
16
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
17
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
18
+ ];
19
+
20
+ const BodyCopy = () => (
21
+ <div className="space-y-12">
22
+ {loremIpsumParagraphs.map((paragraph) => (
23
+ <p key={paragraph} className="text-sm text-fg-subtle">
24
+ {paragraph}
25
+ </p>
26
+ ))}
27
+ </div>
28
+ );
29
+
15
30
  const meta = {
16
31
  title: "Components/Modal",
17
32
  component: Modal,
@@ -43,7 +58,7 @@ export const Basic = {
43
58
  render: (args) => (
44
59
  <Modal>
45
60
  <ModalTrigger asChild>
46
- <Button variant="primary">Open Modal</Button>
61
+ <Button>Open Modal</Button>
47
62
  </ModalTrigger>
48
63
  <ModalContent {...args}>
49
64
  <ModalHeader>
@@ -53,15 +68,13 @@ export const Basic = {
53
68
  </ModalDescription>
54
69
  </ModalHeader>
55
70
  <ModalBody>
56
- <p className="text-sm text-fg-subtle">
57
- This modal supports title, description, body, and footer regions.
58
- </p>
71
+ <BodyCopy />
59
72
  </ModalBody>
60
73
  <ModalFooter>
61
74
  <ModalClose asChild>
62
- <Button variant="secondary">Cancel</Button>
75
+ <Button isSecondary>Cancel</Button>
63
76
  </ModalClose>
64
- <Button variant="primary">Publish</Button>
77
+ <Button>Publish</Button>
65
78
  </ModalFooter>
66
79
  </ModalContent>
67
80
  </Modal>
@@ -78,7 +91,7 @@ export const Controlled = {
78
91
  return (
79
92
  <Modal open={open} onOpenChange={setOpen}>
80
93
  <ModalTrigger asChild>
81
- <Button variant="primary">
94
+ <Button>
82
95
  {open ? "Close Modal" : "Open Modal"}
83
96
  </Button>
84
97
  </ModalTrigger>
@@ -90,12 +103,10 @@ export const Controlled = {
90
103
  </ModalDescription>
91
104
  </ModalHeader>
92
105
  <ModalBody>
93
- <p className="text-sm text-fg-subtle">
94
- Use this variant when the parent controls the workflow.
95
- </p>
106
+ <BodyCopy />
96
107
  </ModalBody>
97
108
  <ModalFooter>
98
- <Button variant="bare" onClick={() => setOpen(false)}>
109
+ <Button isBare onClick={() => setOpen(false)}>
99
110
  Dismiss
100
111
  </Button>
101
112
  </ModalFooter>
@@ -114,7 +125,7 @@ export const Sizes = {
114
125
  {["small", "medium", "large", "full"].map((size) => (
115
126
  <Modal key={size}>
116
127
  <ModalTrigger asChild>
117
- <Button variant="secondary">Size: {size}</Button>
128
+ <Button isSecondary>Size: {size}</Button>
118
129
  </ModalTrigger>
119
130
  <ModalContent size={size}>
120
131
  <ModalHeader>
@@ -124,10 +135,7 @@ export const Sizes = {
124
135
  </ModalDescription>
125
136
  </ModalHeader>
126
137
  <ModalBody>
127
- <p className="text-sm text-fg-subtle">
128
- These presets use existing spacing, border, shadow, and
129
- foreground/background tokens.
130
- </p>
138
+ <BodyCopy />
131
139
  </ModalBody>
132
140
  </ModalContent>
133
141
  </Modal>
@@ -140,7 +148,7 @@ export const CustomBackdrop = {
140
148
  render: (args) => (
141
149
  <Modal>
142
150
  <ModalTrigger asChild>
143
- <Button variant="primary">Open With Backdrop</Button>
151
+ <Button>Open With Backdrop</Button>
144
152
  </ModalTrigger>
145
153
  <ModalContent
146
154
  {...args}
@@ -155,9 +163,7 @@ export const CustomBackdrop = {
155
163
  </ModalDescription>
156
164
  </ModalHeader>
157
165
  <ModalBody>
158
- <p className="text-sm text-fg-subtle">
159
- Override color and opacity without changing the modal surface.
160
- </p>
166
+ <BodyCopy />
161
167
  </ModalBody>
162
168
  </ModalContent>
163
169
  </Modal>
@@ -166,3 +172,133 @@ export const CustomBackdrop = {
166
172
  size: "medium",
167
173
  },
168
174
  };
175
+
176
+ export const FooterActions = {
177
+ render: (args) => (
178
+ <Modal>
179
+ <ModalTrigger asChild>
180
+ <Button isSecondary>Open Action Footer</Button>
181
+ </ModalTrigger>
182
+ <ModalContent {...args}>
183
+ <ModalHeader>
184
+ <ModalTitle>Save draft</ModalTitle>
185
+ <ModalDescription>
186
+ Footer actions can group secondary and primary outcomes.
187
+ </ModalDescription>
188
+ </ModalHeader>
189
+ <ModalBody>
190
+ <BodyCopy />
191
+ </ModalBody>
192
+ <ModalFooter>
193
+ <ModalClose asChild>
194
+ <Button isBare>Back</Button>
195
+ </ModalClose>
196
+ <Button isSecondary>Save Draft</Button>
197
+ <Button>Continue</Button>
198
+ </ModalFooter>
199
+ </ModalContent>
200
+ </Modal>
201
+ ),
202
+ args: {
203
+ size: "medium",
204
+ },
205
+ };
206
+
207
+ export const DestructiveFooter = {
208
+ render: (args) => (
209
+ <Modal>
210
+ <ModalTrigger asChild>
211
+ <Button>Open Destructive Footer</Button>
212
+ </ModalTrigger>
213
+ <ModalContent {...args}>
214
+ <ModalHeader>
215
+ <ModalTitle>Delete manuscript version</ModalTitle>
216
+ <ModalDescription>
217
+ Use a footer to separate destructive actions from cancellation.
218
+ </ModalDescription>
219
+ </ModalHeader>
220
+ <ModalBody>
221
+ <BodyCopy />
222
+ </ModalBody>
223
+ <ModalFooter>
224
+ <ModalClose asChild>
225
+ <Button isSecondary>Cancel</Button>
226
+ </ModalClose>
227
+ <Button isDanger>Delete Version</Button>
228
+ </ModalFooter>
229
+ </ModalContent>
230
+ </Modal>
231
+ ),
232
+ args: {
233
+ size: "small",
234
+ },
235
+ };
236
+
237
+ export const HeaderCloseButton = {
238
+ render: (args) => (
239
+ <Modal onClose={() => {}}>
240
+ <ModalTrigger asChild>
241
+ <Button isSecondary>Open Header Close</Button>
242
+ </ModalTrigger>
243
+ <ModalContent {...args}>
244
+ <ModalHeader>
245
+ <ModalTitle>Review publish settings</ModalTitle>
246
+ <ModalDescription>
247
+ Passing onClose to Modal automatically adds the header close action.
248
+ </ModalDescription>
249
+ </ModalHeader>
250
+ <ModalBody>
251
+ <BodyCopy />
252
+ </ModalBody>
253
+ <ModalFooter>
254
+ <ModalClose asChild>
255
+ <Button isSecondary>Cancel</Button>
256
+ </ModalClose>
257
+ <Button>Save</Button>
258
+ </ModalFooter>
259
+ </ModalContent>
260
+ </Modal>
261
+ ),
262
+ args: {
263
+ size: "medium",
264
+ },
265
+ };
266
+
267
+ export const ControlledHeaderCloseButton = {
268
+ render: (args) => {
269
+ const [open, setOpen] = useState(false);
270
+
271
+ return (
272
+ <Modal
273
+ open={open}
274
+ onOpenChange={setOpen}
275
+ onClose={() => setOpen(false)}
276
+ >
277
+ <ModalTrigger asChild>
278
+ <Button>
279
+ {open ? "Modal Open" : "Open Controlled Header Close"}
280
+ </Button>
281
+ </ModalTrigger>
282
+ <ModalContent {...args}>
283
+ <ModalHeader>
284
+ <ModalTitle>Controlled header close</ModalTitle>
285
+ <ModalDescription>
286
+ The built-in close icon also works when modal state is controlled.
287
+ </ModalDescription>
288
+ </ModalHeader>
289
+ <ModalBody>
290
+ <BodyCopy />
291
+ </ModalBody>
292
+ <ModalFooter>
293
+ <Button isBare onClick={() => setOpen(false)}>
294
+ Dismiss
295
+ </Button>
296
+ </ModalFooter>
297
+ </ModalContent>
298
+ </Modal>
299
+ );
300
+ },
301
+ args: {
302
+ size: "medium",
303
+ },
304
+ };
@@ -1,22 +1,56 @@
1
- import React, { forwardRef, useCallback } from "react";
1
+ import React, {
2
+ createContext,
3
+ forwardRef,
4
+ useCallback,
5
+ useContext,
6
+ useMemo,
7
+ } from "react";
2
8
  import * as DialogPrimitive from "@radix-ui/react-dialog";
3
9
  import { cx } from "@edux-design/utils";
10
+ import { IconButton } from "@edux-design/buttons";
11
+ import { Close } from "@edux-design/icons";
4
12
 
5
13
  const MODAL_OVERLAY_Z_INDEX = 2147483645;
6
14
  const MODAL_CONTENT_Z_INDEX = 2147483646;
15
+ const ModalContext = createContext({ hasOnClose: false });
16
+ const MODAL_SECTION_PADDING_X = "px-16";
17
+ const MODAL_SECTION_PADDING_Y = "py-16";
18
+ const MODAL_SURFACE_CLASSES = [
19
+ "fixed left-1/2 top-1/2 max-h-[92vh] -translate-x-1/2 -translate-y-1/2",
20
+ "bg-bg-base text-fg-base shadow-xl outline-none rounded-md border-2 border-border-subtle",
21
+ "flex flex-col overflow-hidden",
22
+ "data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out",
23
+ ].join(" ");
24
+ const MODAL_HEADER_CLASSES = [
25
+ "flex justify-between gap-12",
26
+ MODAL_SECTION_PADDING_X,
27
+ MODAL_SECTION_PADDING_Y,
28
+ "border-b border-border-subtle",
29
+ ].join(" ");
30
+ const MODAL_HEADER_CONTENT_CLASSES = "flex min-w-0 flex-1 flex-col gap-4";
31
+ const MODAL_BODY_CLASSES = [
32
+ "flex-1 overflow-auto",
33
+ MODAL_SECTION_PADDING_X,
34
+ MODAL_SECTION_PADDING_Y,
35
+ ].join(" ");
36
+ const MODAL_FOOTER_CLASSES = [
37
+ "flex items-center justify-end gap-8",
38
+ MODAL_SECTION_PADDING_X,
39
+ MODAL_SECTION_PADDING_Y,
40
+ "border-t border-border-subtle",
41
+ ].join(" ");
42
+ const MODAL_TITLE_CLASSES = "text-lg font-semibold text-fg-base";
43
+ const MODAL_DESCRIPTION_CLASSES = "text-sm text-fg-subtle";
44
+ const MODAL_CLOSE_LABEL = "Close modal";
45
+ const MODAL_SIZE_CLASSES = {
46
+ small: "w-[min(92vw,420px)]",
47
+ medium: "w-[min(92vw,640px)]",
48
+ large: "w-[min(92vw,840px)]",
49
+ full: "w-[min(96vw,1200px)] h-[min(92vh,900px)]",
50
+ };
7
51
 
8
52
  const getSizeClasses = (size) => {
9
- switch (size) {
10
- case "small":
11
- return "w-[min(92vw,420px)]";
12
- case "large":
13
- return "w-[min(92vw,840px)]";
14
- case "full":
15
- return "w-[min(96vw,1200px)] h-[min(92vh,900px)]";
16
- case "medium":
17
- default:
18
- return "w-[min(92vw,640px)]";
19
- }
53
+ return MODAL_SIZE_CLASSES[size] ?? MODAL_SIZE_CLASSES.medium;
20
54
  };
21
55
 
22
56
  export const Modal = ({
@@ -28,6 +62,11 @@ export const Modal = ({
28
62
  children,
29
63
  ...props
30
64
  }) => {
65
+ const contextValue = useMemo(
66
+ () => ({ hasOnClose: typeof onClose === "function" }),
67
+ [onClose]
68
+ );
69
+
31
70
  const handleOpenChange = useCallback(
32
71
  (nextOpen) => {
33
72
  onOpenChange?.(nextOpen);
@@ -41,14 +80,16 @@ export const Modal = ({
41
80
  );
42
81
 
43
82
  return (
44
- <DialogPrimitive.Root
45
- open={open}
46
- defaultOpen={defaultOpen}
47
- onOpenChange={handleOpenChange}
48
- {...props}
49
- >
50
- {children}
51
- </DialogPrimitive.Root>
83
+ <ModalContext.Provider value={contextValue}>
84
+ <DialogPrimitive.Root
85
+ open={open}
86
+ defaultOpen={defaultOpen}
87
+ onOpenChange={handleOpenChange}
88
+ {...props}
89
+ >
90
+ {children}
91
+ </DialogPrimitive.Root>
92
+ </ModalContext.Provider>
52
93
  );
53
94
  };
54
95
 
@@ -56,6 +97,17 @@ export const ModalTrigger = DialogPrimitive.Trigger;
56
97
  export const ModalClose = DialogPrimitive.Close;
57
98
  export const ModalPortal = DialogPrimitive.Portal;
58
99
 
100
+ const ModalHeaderCloseButton = () => (
101
+ <ModalClose asChild>
102
+ <IconButton
103
+ size="small"
104
+ isBare
105
+ icon={<Close aria-hidden="true" />}
106
+ aria-label={MODAL_CLOSE_LABEL}
107
+ />
108
+ </ModalClose>
109
+ );
110
+
59
111
  export const ModalOverlay = forwardRef(
60
112
  ({ className, style, ...props }, ref) => (
61
113
  <DialogPrimitive.Overlay
@@ -88,21 +140,20 @@ export const ModalContent = forwardRef(
88
140
  ref
89
141
  ) => {
90
142
  const resolvedBackdropProps = backdropProps ?? backDropProps ?? {};
143
+ const { className: backdropClassName, style: backdropStyle, ...overlayProps } =
144
+ resolvedBackdropProps;
91
145
 
92
146
  return (
93
147
  <ModalPortal>
94
148
  <ModalOverlay
95
- {...resolvedBackdropProps}
96
- className={cx(resolvedBackdropProps.className)}
97
- style={{ ...resolvedBackdropProps.style }}
149
+ {...overlayProps}
150
+ className={cx(backdropClassName)}
151
+ style={{ ...backdropStyle }}
98
152
  />
99
153
  <DialogPrimitive.Content
100
154
  ref={ref}
101
155
  className={cx(
102
- "fixed left-1/2 top-1/2 max-h-[92vh] -translate-x-1/2 -translate-y-1/2",
103
- "bg-bg-base text-fg-base shadow-xl outline-none rounded-2xl border border-border-subtle",
104
- "flex flex-col overflow-hidden",
105
- "data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out",
156
+ MODAL_SURFACE_CLASSES,
106
157
  getSizeClasses(size),
107
158
  className
108
159
  )}
@@ -119,23 +170,27 @@ export const ModalContent = forwardRef(
119
170
 
120
171
  ModalContent.displayName = "ModalContent";
121
172
 
122
- export const ModalHeader = forwardRef(({ className, ...props }, ref) => (
123
- <div
124
- ref={ref}
125
- className={cx(
126
- "flex flex-col gap-4 px-20 py-16 border-b border-border-subtle",
127
- className
128
- )}
129
- {...props}
130
- />
131
- ));
173
+ export const ModalHeader = forwardRef(({ className, children, ...props }, ref) => {
174
+ const { hasOnClose } = useContext(ModalContext);
175
+
176
+ return (
177
+ <div
178
+ ref={ref}
179
+ className={cx(MODAL_HEADER_CLASSES, className)}
180
+ {...props}
181
+ >
182
+ <div className={MODAL_HEADER_CONTENT_CLASSES}>{children}</div>
183
+ {hasOnClose ? <ModalHeaderCloseButton /> : null}
184
+ </div>
185
+ );
186
+ });
132
187
 
133
188
  ModalHeader.displayName = "ModalHeader";
134
189
 
135
190
  export const ModalBody = forwardRef(({ className, ...props }, ref) => (
136
191
  <div
137
192
  ref={ref}
138
- className={cx("flex-1 overflow-auto px-20 py-16", className)}
193
+ className={cx(MODAL_BODY_CLASSES, className)}
139
194
  {...props}
140
195
  />
141
196
  ));
@@ -145,10 +200,7 @@ ModalBody.displayName = "ModalBody";
145
200
  export const ModalFooter = forwardRef(({ className, ...props }, ref) => (
146
201
  <div
147
202
  ref={ref}
148
- className={cx(
149
- "flex items-center justify-end gap-8 px-20 py-16 border-t border-border-subtle",
150
- className
151
- )}
203
+ className={cx(MODAL_FOOTER_CLASSES, className)}
152
204
  {...props}
153
205
  />
154
206
  ));
@@ -158,7 +210,7 @@ ModalFooter.displayName = "ModalFooter";
158
210
  export const ModalTitle = forwardRef(({ className, ...props }, ref) => (
159
211
  <DialogPrimitive.Title
160
212
  ref={ref}
161
- className={cx("text-lg font-semibold text-fg-base", className)}
213
+ className={cx(MODAL_TITLE_CLASSES, className)}
162
214
  {...props}
163
215
  />
164
216
  ));
@@ -168,7 +220,7 @@ ModalTitle.displayName = "ModalTitle";
168
220
  export const ModalDescription = forwardRef(({ className, ...props }, ref) => (
169
221
  <DialogPrimitive.Description
170
222
  ref={ref}
171
- className={cx("text-sm text-fg-subtle", className)}
223
+ className={cx(MODAL_DESCRIPTION_CLASSES, className)}
172
224
  {...props}
173
225
  />
174
226
  ));
@@ -13,9 +13,9 @@ import {
13
13
  let lastRootProps;
14
14
 
15
15
  vi.mock("@radix-ui/react-dialog", () => {
16
- const Passthrough = ({ children, ...props }) => (
17
- <div {...props}>{children}</div>
18
- );
16
+ const Passthrough = ({ children, ...props }) => <div {...props}>{children}</div>;
17
+ const AsChildPassthrough = ({ children, asChild, ...props }) =>
18
+ asChild ? children : <button type="button" {...props}>{children}</button>;
19
19
  const Buttonish = ({ children, ...props }) => (
20
20
  <button type="button" {...props}>
21
21
  {children}
@@ -29,18 +29,18 @@ vi.mock("@radix-ui/react-dialog", () => {
29
29
  <div ref={ref} data-testid="radix-overlay" {...props} />
30
30
  ));
31
31
  Overlay.displayName = "RadixOverlay";
32
- const Root = ({ children, ...props }) => {
33
- lastRootProps = props;
34
- return <div {...props}>{children}</div>;
32
+ const Root = ({ children, open, defaultOpen, onOpenChange }) => {
33
+ lastRootProps = { open, defaultOpen, onOpenChange };
34
+ return <div>{children}</div>;
35
35
  };
36
36
 
37
37
  return {
38
38
  Root,
39
39
  Trigger: Buttonish,
40
- Portal: Passthrough,
40
+ Portal: ({ children }) => <div>{children}</div>,
41
41
  Overlay,
42
42
  Content,
43
- Close: Buttonish,
43
+ Close: AsChildPassthrough,
44
44
  Title: Passthrough,
45
45
  Description: Passthrough,
46
46
  };
@@ -89,6 +89,34 @@ describe("ModalContent", () => {
89
89
  expect(screen.getByText("Body")).toHaveClass("overflow-auto");
90
90
  expect(screen.getByText("Footer")).toHaveClass("border-t");
91
91
  });
92
+
93
+ it("adds a close icon button to the header when Modal receives onClose", () => {
94
+ render(
95
+ <Modal onClose={vi.fn()}>
96
+ <ModalContent>
97
+ <ModalHeader>
98
+ <ModalTitle>Header</ModalTitle>
99
+ </ModalHeader>
100
+ </ModalContent>
101
+ </Modal>
102
+ );
103
+
104
+ expect(screen.getByLabelText("Close modal")).toBeInTheDocument();
105
+ });
106
+
107
+ it("does not add a close icon button to the header without onClose", () => {
108
+ render(
109
+ <Modal>
110
+ <ModalContent>
111
+ <ModalHeader>
112
+ <ModalTitle>Header</ModalTitle>
113
+ </ModalHeader>
114
+ </ModalContent>
115
+ </Modal>
116
+ );
117
+
118
+ expect(screen.queryByLabelText("Close modal")).not.toBeInTheDocument();
119
+ });
92
120
  });
93
121
 
94
122
  describe("Modal", () => {