@instructure/ui-modal 10.3.1-snapshot-9 → 10.4.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/CHANGELOG.md +5 -2
- package/es/Modal/__new-tests__/Modal.test.js +33 -31
- package/es/Modal/index.js +6 -7
- package/es/Modal/props.js +2 -5
- package/lib/Modal/__new-tests__/Modal.test.js +33 -31
- package/lib/Modal/index.js +5 -8
- package/lib/Modal/props.js +1 -5
- package/package.json +19 -19
- package/src/Modal/README.md +94 -0
- package/src/Modal/__new-tests__/Modal.test.tsx +18 -23
- package/src/Modal/index.tsx +13 -29
- package/src/Modal/props.ts +4 -16
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/Modal/index.d.ts +2 -8
- package/types/Modal/index.d.ts.map +1 -1
- package/types/Modal/props.d.ts +1 -1
- package/types/Modal/props.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
# [10.4.0](https://github.com/instructure/instructure-ui/compare/v10.3.0...v10.4.0) (2024-10-16)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **ui-modal:** modify modal to support less strict children ([40f8ca2](https://github.com/instructure/instructure-ui/commit/40f8ca24e80ceb41e8c5d05d1f9d5e8f77113370))
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
|
|
2
2
|
const _excluded = ["label"];
|
|
3
|
-
var _Modal, _Modal2, _Modal$Body, _Modal3, _Modal4, _Modal5, _Modal$Body2, _Modal$Body3, _Modal$Body4, _Modal$Body5, _Modal$Body6, _Modal$Body7, _Modal6, _Modal7,
|
|
3
|
+
var _Modal, _Modal2, _Modal$Body, _Modal3, _Modal4, _View, _Modal5, _Modal$Body2, _Modal$Body3, _Modal$Body4, _Modal$Body5, _Modal$Body6, _Modal$Body7, _Modal6, _Modal7, _input, _Modal$Header, _Modal$Body8, _Modal$Footer;
|
|
4
4
|
/*
|
|
5
5
|
* The MIT License (MIT)
|
|
6
6
|
*
|
|
@@ -31,6 +31,7 @@ import { vi } from 'vitest';
|
|
|
31
31
|
import userEvent from '@testing-library/user-event';
|
|
32
32
|
import '@testing-library/jest-dom';
|
|
33
33
|
import { Modal } from '../index';
|
|
34
|
+
import { View } from '@instructure/ui-view';
|
|
34
35
|
describe('<Modal />', () => {
|
|
35
36
|
let consoleWarningMock;
|
|
36
37
|
let consoleErrorMock;
|
|
@@ -146,13 +147,26 @@ describe('<Modal />', () => {
|
|
|
146
147
|
const modalBody = await findByText(bodyText);
|
|
147
148
|
expect(modalBody).toBeInTheDocument();
|
|
148
149
|
});
|
|
150
|
+
it('should handle custom children', async () => {
|
|
151
|
+
const bodyText = 'Modal-body-text';
|
|
152
|
+
const _render7 = render(/*#__PURE__*/React.createElement(Modal, {
|
|
153
|
+
open: true,
|
|
154
|
+
label: "Modal Dialog",
|
|
155
|
+
shouldReturnFocus: false
|
|
156
|
+
}, _View || (_View = /*#__PURE__*/React.createElement(View, null, "This is a custom child")), /*#__PURE__*/React.createElement(Modal.Body, null, bodyText))),
|
|
157
|
+
findByText = _render7.findByText;
|
|
158
|
+
const modalBody = await findByText(bodyText);
|
|
159
|
+
const customChild = await findByText('This is a custom child');
|
|
160
|
+
expect(modalBody).toBeInTheDocument();
|
|
161
|
+
expect(customChild).toBeInTheDocument();
|
|
162
|
+
});
|
|
149
163
|
it('should apply the aria attributes', async () => {
|
|
150
|
-
const
|
|
164
|
+
const _render8 = render(_Modal5 || (_Modal5 = /*#__PURE__*/React.createElement(Modal, {
|
|
151
165
|
open: true,
|
|
152
166
|
label: "Modal Dialog",
|
|
153
167
|
shouldReturnFocus: false
|
|
154
168
|
}, /*#__PURE__*/React.createElement(Modal.Body, null, "Foo Bar Baz")))),
|
|
155
|
-
findByRole =
|
|
169
|
+
findByRole = _render8.findByRole;
|
|
156
170
|
const dialog = await findByRole('dialog');
|
|
157
171
|
expect(dialog).toBeInTheDocument();
|
|
158
172
|
expect(dialog).toHaveAttribute('aria-label', 'Modal Dialog');
|
|
@@ -161,7 +175,7 @@ describe('<Modal />', () => {
|
|
|
161
175
|
const onEnter = vi.fn();
|
|
162
176
|
const onEntering = vi.fn();
|
|
163
177
|
const onEntered = vi.fn();
|
|
164
|
-
const
|
|
178
|
+
const _render9 = render(/*#__PURE__*/React.createElement(Modal, {
|
|
165
179
|
open: true,
|
|
166
180
|
onEnter: onEnter,
|
|
167
181
|
onEntering: onEntering,
|
|
@@ -170,7 +184,7 @@ describe('<Modal />', () => {
|
|
|
170
184
|
label: "Modal Dialog",
|
|
171
185
|
shouldReturnFocus: false
|
|
172
186
|
}, _Modal$Body2 || (_Modal$Body2 = /*#__PURE__*/React.createElement(Modal.Body, null, "Foo Bar Baz")))),
|
|
173
|
-
findByRole =
|
|
187
|
+
findByRole = _render9.findByRole;
|
|
174
188
|
const dialog = await findByRole('dialog');
|
|
175
189
|
expect(dialog).toBeInTheDocument();
|
|
176
190
|
await waitFor(() => {
|
|
@@ -181,13 +195,13 @@ describe('<Modal />', () => {
|
|
|
181
195
|
});
|
|
182
196
|
it('should support onOpen prop', async () => {
|
|
183
197
|
const onOpen = vi.fn();
|
|
184
|
-
const
|
|
198
|
+
const _render10 = render(/*#__PURE__*/React.createElement(Modal, {
|
|
185
199
|
open: true,
|
|
186
200
|
onOpen: onOpen,
|
|
187
201
|
label: "Modal Dialog",
|
|
188
202
|
shouldReturnFocus: false
|
|
189
203
|
}, _Modal$Body3 || (_Modal$Body3 = /*#__PURE__*/React.createElement(Modal.Body, null, "Foo Bar Baz")))),
|
|
190
|
-
findByRole =
|
|
204
|
+
findByRole = _render10.findByRole;
|
|
191
205
|
const dialog = await findByRole('dialog');
|
|
192
206
|
expect(dialog).toBeInTheDocument();
|
|
193
207
|
await waitFor(() => {
|
|
@@ -196,14 +210,14 @@ describe('<Modal />', () => {
|
|
|
196
210
|
});
|
|
197
211
|
it('should support onClose prop', async () => {
|
|
198
212
|
const onClose = vi.fn();
|
|
199
|
-
const
|
|
213
|
+
const _render11 = render(/*#__PURE__*/React.createElement(Modal, {
|
|
200
214
|
open: true,
|
|
201
215
|
onClose: onClose,
|
|
202
216
|
label: "Modal Dialog",
|
|
203
217
|
shouldReturnFocus: false
|
|
204
218
|
}, _Modal$Body4 || (_Modal$Body4 = /*#__PURE__*/React.createElement(Modal.Body, null, "Foo Bar Baz")))),
|
|
205
|
-
findByRole =
|
|
206
|
-
rerender =
|
|
219
|
+
findByRole = _render11.findByRole,
|
|
220
|
+
rerender = _render11.rerender;
|
|
207
221
|
const dialog = await findByRole('dialog');
|
|
208
222
|
expect(dialog).toBeInTheDocument();
|
|
209
223
|
rerender(/*#__PURE__*/React.createElement(Modal, {
|
|
@@ -218,13 +232,13 @@ describe('<Modal />', () => {
|
|
|
218
232
|
});
|
|
219
233
|
it('should dismiss when overlay clicked by default', async () => {
|
|
220
234
|
const onDismiss = vi.fn();
|
|
221
|
-
const
|
|
235
|
+
const _render12 = render(/*#__PURE__*/React.createElement(Modal, {
|
|
222
236
|
open: true,
|
|
223
237
|
onDismiss: onDismiss,
|
|
224
238
|
label: "Modal Dialog",
|
|
225
239
|
shouldReturnFocus: false
|
|
226
240
|
}, _Modal$Body6 || (_Modal$Body6 = /*#__PURE__*/React.createElement(Modal.Body, null, "Modal Text")))),
|
|
227
|
-
findByText =
|
|
241
|
+
findByText = _render12.findByText;
|
|
228
242
|
const modalBody = await findByText('Modal Text');
|
|
229
243
|
expect(modalBody).toBeInTheDocument();
|
|
230
244
|
await waitFor(() => {
|
|
@@ -235,7 +249,7 @@ describe('<Modal />', () => {
|
|
|
235
249
|
it('should NOT dismiss when overlay clicked with shouldCloseOnDocumentClick=false', async () => {
|
|
236
250
|
const onDismiss = vi.fn();
|
|
237
251
|
const onClickOuter = vi.fn();
|
|
238
|
-
const
|
|
252
|
+
const _render13 = render(/*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("button", {
|
|
239
253
|
"data-testid": "outer-element",
|
|
240
254
|
onClick: onClickOuter
|
|
241
255
|
}, "for dismiss"), /*#__PURE__*/React.createElement(Modal, {
|
|
@@ -245,8 +259,8 @@ describe('<Modal />', () => {
|
|
|
245
259
|
shouldReturnFocus: false,
|
|
246
260
|
shouldCloseOnDocumentClick: false
|
|
247
261
|
}, _Modal$Body7 || (_Modal$Body7 = /*#__PURE__*/React.createElement(Modal.Body, null, "Foo Bar Baz ", /*#__PURE__*/React.createElement("button", null, "click me")))))),
|
|
248
|
-
findByRole =
|
|
249
|
-
getByTestId =
|
|
262
|
+
findByRole = _render13.findByRole,
|
|
263
|
+
getByTestId = _render13.getByTestId;
|
|
250
264
|
const dialog = await findByRole('dialog');
|
|
251
265
|
expect(dialog).toBeInTheDocument();
|
|
252
266
|
userEvent.click(getByTestId('outer-element'));
|
|
@@ -257,38 +271,26 @@ describe('<Modal />', () => {
|
|
|
257
271
|
});
|
|
258
272
|
});
|
|
259
273
|
it('should render children', async () => {
|
|
260
|
-
const
|
|
274
|
+
const _render14 = render(_Modal6 || (_Modal6 = /*#__PURE__*/React.createElement(Modal, {
|
|
261
275
|
open: true,
|
|
262
276
|
label: "Modal Dialog",
|
|
263
277
|
shouldReturnFocus: false
|
|
264
278
|
}, /*#__PURE__*/React.createElement(Modal.Body, null, /*#__PURE__*/React.createElement("button", null, "Cancel"))))),
|
|
265
|
-
findByText =
|
|
279
|
+
findByText = _render14.findByText;
|
|
266
280
|
const cancelButton = await findByText('Cancel');
|
|
267
281
|
expect(cancelButton).toBeInTheDocument();
|
|
268
282
|
});
|
|
269
283
|
describe('children validation', () => {
|
|
270
284
|
it('should pass validation when children are valid', async () => {
|
|
271
|
-
const
|
|
285
|
+
const _render15 = render(_Modal7 || (_Modal7 = /*#__PURE__*/React.createElement(Modal, {
|
|
272
286
|
open: true,
|
|
273
287
|
label: "Modal Dialog",
|
|
274
288
|
shouldReturnFocus: false
|
|
275
289
|
}, /*#__PURE__*/React.createElement(Modal.Header, null, "Hello World"), /*#__PURE__*/React.createElement(Modal.Body, null, "Foo Bar Baz"), /*#__PURE__*/React.createElement(Modal.Footer, null, /*#__PURE__*/React.createElement("button", null, "Cancel"))))),
|
|
276
|
-
findByRole = _render14.findByRole;
|
|
277
|
-
const dialog = await findByRole('dialog');
|
|
278
|
-
expect(dialog).toBeInTheDocument();
|
|
279
|
-
expect(consoleErrorMock).not.toHaveBeenCalled();
|
|
280
|
-
});
|
|
281
|
-
it('should not pass validation when children are invalid', async () => {
|
|
282
|
-
const _render15 = render(_Modal8 || (_Modal8 = /*#__PURE__*/React.createElement(Modal, {
|
|
283
|
-
open: true,
|
|
284
|
-
label: "Modal Dialog",
|
|
285
|
-
shouldReturnFocus: false
|
|
286
|
-
}, /*#__PURE__*/React.createElement(Modal.Body, null, "Foo Bar Baz"), /*#__PURE__*/React.createElement(Modal.Footer, null, /*#__PURE__*/React.createElement("button", null, "Cancel")), /*#__PURE__*/React.createElement(Modal.Header, null, "Hello World")))),
|
|
287
290
|
findByRole = _render15.findByRole;
|
|
288
291
|
const dialog = await findByRole('dialog');
|
|
289
|
-
const expectedErrorMessage = 'Expected children of Modal in one of the following formats:';
|
|
290
292
|
expect(dialog).toBeInTheDocument();
|
|
291
|
-
expect(consoleErrorMock).
|
|
293
|
+
expect(consoleErrorMock).not.toHaveBeenCalled();
|
|
292
294
|
});
|
|
293
295
|
it('should pass inverse variant to children when set', async () => {
|
|
294
296
|
let headerRef = null;
|
package/es/Modal/index.js
CHANGED
|
@@ -26,8 +26,8 @@ var _dec, _dec2, _class, _Modal;
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
/** @jsx jsx */
|
|
29
|
-
import
|
|
30
|
-
import { passthroughProps, safeCloneElement
|
|
29
|
+
import { Children, Component, isValidElement } from 'react';
|
|
30
|
+
import { passthroughProps, safeCloneElement } from '@instructure/ui-react-utils';
|
|
31
31
|
import { createChainedFunction } from '@instructure/ui-utils';
|
|
32
32
|
import { testable } from '@instructure/ui-testable';
|
|
33
33
|
import { Transition } from '@instructure/ui-motion';
|
|
@@ -114,15 +114,14 @@ let Modal = (_dec = withStyle(generateStyle, generateComponentTheme), _dec2 = te
|
|
|
114
114
|
return Children.map(children, child => {
|
|
115
115
|
if (!child) return; // ignore null, falsy children
|
|
116
116
|
|
|
117
|
-
if (
|
|
117
|
+
if (/*#__PURE__*/isValidElement(child)) {
|
|
118
|
+
var _child$props;
|
|
118
119
|
return safeCloneElement(child, {
|
|
119
120
|
variant: variant,
|
|
120
|
-
overflow: child.props.overflow || overflow
|
|
121
|
+
overflow: (child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.overflow) || overflow
|
|
121
122
|
});
|
|
122
123
|
} else {
|
|
123
|
-
return
|
|
124
|
-
variant: variant
|
|
125
|
-
});
|
|
124
|
+
return child;
|
|
126
125
|
}
|
|
127
126
|
});
|
|
128
127
|
}
|
package/es/Modal/props.js
CHANGED
|
@@ -23,14 +23,11 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import PropTypes from 'prop-types';
|
|
26
|
-
import { element
|
|
26
|
+
import { element } from '@instructure/ui-prop-types';
|
|
27
27
|
import { transitionTypePropType } from '@instructure/ui-motion';
|
|
28
|
-
import { ModalHeader } from './ModalHeader';
|
|
29
|
-
import { ModalBody } from './ModalBody';
|
|
30
|
-
import { ModalFooter } from './ModalFooter';
|
|
31
28
|
const propTypes = {
|
|
32
29
|
label: PropTypes.string.isRequired,
|
|
33
|
-
children:
|
|
30
|
+
children: PropTypes.node,
|
|
34
31
|
as: PropTypes.elementType,
|
|
35
32
|
size: PropTypes.oneOf(['auto', 'small', 'medium', 'large', 'fullscreen']),
|
|
36
33
|
variant: PropTypes.oneOf(['default', 'inverse']),
|
|
@@ -8,8 +8,9 @@ var _vitest = require("vitest");
|
|
|
8
8
|
var _userEvent = _interopRequireDefault(require("@testing-library/user-event"));
|
|
9
9
|
require("@testing-library/jest-dom");
|
|
10
10
|
var _index = require("../index");
|
|
11
|
+
var _View2 = require("@instructure/ui-view/lib/View");
|
|
11
12
|
const _excluded = ["label"];
|
|
12
|
-
var _Modal, _Modal2, _Modal$Body, _Modal3, _Modal4, _Modal5, _Modal$Body2, _Modal$Body3, _Modal$Body4, _Modal$Body5, _Modal$Body6, _Modal$Body7, _Modal6, _Modal7,
|
|
13
|
+
var _Modal, _Modal2, _Modal$Body, _Modal3, _Modal4, _View, _Modal5, _Modal$Body2, _Modal$Body3, _Modal$Body4, _Modal$Body5, _Modal$Body6, _Modal$Body7, _Modal6, _Modal7, _input, _Modal$Header, _Modal$Body8, _Modal$Footer;
|
|
13
14
|
/*
|
|
14
15
|
* The MIT License (MIT)
|
|
15
16
|
*
|
|
@@ -148,13 +149,26 @@ describe('<Modal />', () => {
|
|
|
148
149
|
const modalBody = await findByText(bodyText);
|
|
149
150
|
expect(modalBody).toBeInTheDocument();
|
|
150
151
|
});
|
|
152
|
+
it('should handle custom children', async () => {
|
|
153
|
+
const bodyText = 'Modal-body-text';
|
|
154
|
+
const _render7 = (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_index.Modal, {
|
|
155
|
+
open: true,
|
|
156
|
+
label: "Modal Dialog",
|
|
157
|
+
shouldReturnFocus: false
|
|
158
|
+
}, _View || (_View = /*#__PURE__*/_react.default.createElement(_View2.View, null, "This is a custom child")), /*#__PURE__*/_react.default.createElement(_index.Modal.Body, null, bodyText))),
|
|
159
|
+
findByText = _render7.findByText;
|
|
160
|
+
const modalBody = await findByText(bodyText);
|
|
161
|
+
const customChild = await findByText('This is a custom child');
|
|
162
|
+
expect(modalBody).toBeInTheDocument();
|
|
163
|
+
expect(customChild).toBeInTheDocument();
|
|
164
|
+
});
|
|
151
165
|
it('should apply the aria attributes', async () => {
|
|
152
|
-
const
|
|
166
|
+
const _render8 = (0, _react2.render)(_Modal5 || (_Modal5 = /*#__PURE__*/_react.default.createElement(_index.Modal, {
|
|
153
167
|
open: true,
|
|
154
168
|
label: "Modal Dialog",
|
|
155
169
|
shouldReturnFocus: false
|
|
156
170
|
}, /*#__PURE__*/_react.default.createElement(_index.Modal.Body, null, "Foo Bar Baz")))),
|
|
157
|
-
findByRole =
|
|
171
|
+
findByRole = _render8.findByRole;
|
|
158
172
|
const dialog = await findByRole('dialog');
|
|
159
173
|
expect(dialog).toBeInTheDocument();
|
|
160
174
|
expect(dialog).toHaveAttribute('aria-label', 'Modal Dialog');
|
|
@@ -163,7 +177,7 @@ describe('<Modal />', () => {
|
|
|
163
177
|
const onEnter = _vitest.vi.fn();
|
|
164
178
|
const onEntering = _vitest.vi.fn();
|
|
165
179
|
const onEntered = _vitest.vi.fn();
|
|
166
|
-
const
|
|
180
|
+
const _render9 = (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_index.Modal, {
|
|
167
181
|
open: true,
|
|
168
182
|
onEnter: onEnter,
|
|
169
183
|
onEntering: onEntering,
|
|
@@ -172,7 +186,7 @@ describe('<Modal />', () => {
|
|
|
172
186
|
label: "Modal Dialog",
|
|
173
187
|
shouldReturnFocus: false
|
|
174
188
|
}, _Modal$Body2 || (_Modal$Body2 = /*#__PURE__*/_react.default.createElement(_index.Modal.Body, null, "Foo Bar Baz")))),
|
|
175
|
-
findByRole =
|
|
189
|
+
findByRole = _render9.findByRole;
|
|
176
190
|
const dialog = await findByRole('dialog');
|
|
177
191
|
expect(dialog).toBeInTheDocument();
|
|
178
192
|
await (0, _react2.waitFor)(() => {
|
|
@@ -183,13 +197,13 @@ describe('<Modal />', () => {
|
|
|
183
197
|
});
|
|
184
198
|
it('should support onOpen prop', async () => {
|
|
185
199
|
const onOpen = _vitest.vi.fn();
|
|
186
|
-
const
|
|
200
|
+
const _render10 = (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_index.Modal, {
|
|
187
201
|
open: true,
|
|
188
202
|
onOpen: onOpen,
|
|
189
203
|
label: "Modal Dialog",
|
|
190
204
|
shouldReturnFocus: false
|
|
191
205
|
}, _Modal$Body3 || (_Modal$Body3 = /*#__PURE__*/_react.default.createElement(_index.Modal.Body, null, "Foo Bar Baz")))),
|
|
192
|
-
findByRole =
|
|
206
|
+
findByRole = _render10.findByRole;
|
|
193
207
|
const dialog = await findByRole('dialog');
|
|
194
208
|
expect(dialog).toBeInTheDocument();
|
|
195
209
|
await (0, _react2.waitFor)(() => {
|
|
@@ -198,14 +212,14 @@ describe('<Modal />', () => {
|
|
|
198
212
|
});
|
|
199
213
|
it('should support onClose prop', async () => {
|
|
200
214
|
const onClose = _vitest.vi.fn();
|
|
201
|
-
const
|
|
215
|
+
const _render11 = (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_index.Modal, {
|
|
202
216
|
open: true,
|
|
203
217
|
onClose: onClose,
|
|
204
218
|
label: "Modal Dialog",
|
|
205
219
|
shouldReturnFocus: false
|
|
206
220
|
}, _Modal$Body4 || (_Modal$Body4 = /*#__PURE__*/_react.default.createElement(_index.Modal.Body, null, "Foo Bar Baz")))),
|
|
207
|
-
findByRole =
|
|
208
|
-
rerender =
|
|
221
|
+
findByRole = _render11.findByRole,
|
|
222
|
+
rerender = _render11.rerender;
|
|
209
223
|
const dialog = await findByRole('dialog');
|
|
210
224
|
expect(dialog).toBeInTheDocument();
|
|
211
225
|
rerender(/*#__PURE__*/_react.default.createElement(_index.Modal, {
|
|
@@ -220,13 +234,13 @@ describe('<Modal />', () => {
|
|
|
220
234
|
});
|
|
221
235
|
it('should dismiss when overlay clicked by default', async () => {
|
|
222
236
|
const onDismiss = _vitest.vi.fn();
|
|
223
|
-
const
|
|
237
|
+
const _render12 = (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_index.Modal, {
|
|
224
238
|
open: true,
|
|
225
239
|
onDismiss: onDismiss,
|
|
226
240
|
label: "Modal Dialog",
|
|
227
241
|
shouldReturnFocus: false
|
|
228
242
|
}, _Modal$Body6 || (_Modal$Body6 = /*#__PURE__*/_react.default.createElement(_index.Modal.Body, null, "Modal Text")))),
|
|
229
|
-
findByText =
|
|
243
|
+
findByText = _render12.findByText;
|
|
230
244
|
const modalBody = await findByText('Modal Text');
|
|
231
245
|
expect(modalBody).toBeInTheDocument();
|
|
232
246
|
await (0, _react2.waitFor)(() => {
|
|
@@ -237,7 +251,7 @@ describe('<Modal />', () => {
|
|
|
237
251
|
it('should NOT dismiss when overlay clicked with shouldCloseOnDocumentClick=false', async () => {
|
|
238
252
|
const onDismiss = _vitest.vi.fn();
|
|
239
253
|
const onClickOuter = _vitest.vi.fn();
|
|
240
|
-
const
|
|
254
|
+
const _render13 = (0, _react2.render)(/*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("button", {
|
|
241
255
|
"data-testid": "outer-element",
|
|
242
256
|
onClick: onClickOuter
|
|
243
257
|
}, "for dismiss"), /*#__PURE__*/_react.default.createElement(_index.Modal, {
|
|
@@ -247,8 +261,8 @@ describe('<Modal />', () => {
|
|
|
247
261
|
shouldReturnFocus: false,
|
|
248
262
|
shouldCloseOnDocumentClick: false
|
|
249
263
|
}, _Modal$Body7 || (_Modal$Body7 = /*#__PURE__*/_react.default.createElement(_index.Modal.Body, null, "Foo Bar Baz ", /*#__PURE__*/_react.default.createElement("button", null, "click me")))))),
|
|
250
|
-
findByRole =
|
|
251
|
-
getByTestId =
|
|
264
|
+
findByRole = _render13.findByRole,
|
|
265
|
+
getByTestId = _render13.getByTestId;
|
|
252
266
|
const dialog = await findByRole('dialog');
|
|
253
267
|
expect(dialog).toBeInTheDocument();
|
|
254
268
|
_userEvent.default.click(getByTestId('outer-element'));
|
|
@@ -259,38 +273,26 @@ describe('<Modal />', () => {
|
|
|
259
273
|
});
|
|
260
274
|
});
|
|
261
275
|
it('should render children', async () => {
|
|
262
|
-
const
|
|
276
|
+
const _render14 = (0, _react2.render)(_Modal6 || (_Modal6 = /*#__PURE__*/_react.default.createElement(_index.Modal, {
|
|
263
277
|
open: true,
|
|
264
278
|
label: "Modal Dialog",
|
|
265
279
|
shouldReturnFocus: false
|
|
266
280
|
}, /*#__PURE__*/_react.default.createElement(_index.Modal.Body, null, /*#__PURE__*/_react.default.createElement("button", null, "Cancel"))))),
|
|
267
|
-
findByText =
|
|
281
|
+
findByText = _render14.findByText;
|
|
268
282
|
const cancelButton = await findByText('Cancel');
|
|
269
283
|
expect(cancelButton).toBeInTheDocument();
|
|
270
284
|
});
|
|
271
285
|
describe('children validation', () => {
|
|
272
286
|
it('should pass validation when children are valid', async () => {
|
|
273
|
-
const
|
|
287
|
+
const _render15 = (0, _react2.render)(_Modal7 || (_Modal7 = /*#__PURE__*/_react.default.createElement(_index.Modal, {
|
|
274
288
|
open: true,
|
|
275
289
|
label: "Modal Dialog",
|
|
276
290
|
shouldReturnFocus: false
|
|
277
291
|
}, /*#__PURE__*/_react.default.createElement(_index.Modal.Header, null, "Hello World"), /*#__PURE__*/_react.default.createElement(_index.Modal.Body, null, "Foo Bar Baz"), /*#__PURE__*/_react.default.createElement(_index.Modal.Footer, null, /*#__PURE__*/_react.default.createElement("button", null, "Cancel"))))),
|
|
278
|
-
findByRole = _render14.findByRole;
|
|
279
|
-
const dialog = await findByRole('dialog');
|
|
280
|
-
expect(dialog).toBeInTheDocument();
|
|
281
|
-
expect(consoleErrorMock).not.toHaveBeenCalled();
|
|
282
|
-
});
|
|
283
|
-
it('should not pass validation when children are invalid', async () => {
|
|
284
|
-
const _render15 = (0, _react2.render)(_Modal8 || (_Modal8 = /*#__PURE__*/_react.default.createElement(_index.Modal, {
|
|
285
|
-
open: true,
|
|
286
|
-
label: "Modal Dialog",
|
|
287
|
-
shouldReturnFocus: false
|
|
288
|
-
}, /*#__PURE__*/_react.default.createElement(_index.Modal.Body, null, "Foo Bar Baz"), /*#__PURE__*/_react.default.createElement(_index.Modal.Footer, null, /*#__PURE__*/_react.default.createElement("button", null, "Cancel")), /*#__PURE__*/_react.default.createElement(_index.Modal.Header, null, "Hello World")))),
|
|
289
292
|
findByRole = _render15.findByRole;
|
|
290
293
|
const dialog = await findByRole('dialog');
|
|
291
|
-
const expectedErrorMessage = 'Expected children of Modal in one of the following formats:';
|
|
292
294
|
expect(dialog).toBeInTheDocument();
|
|
293
|
-
expect(consoleErrorMock).
|
|
295
|
+
expect(consoleErrorMock).not.toHaveBeenCalled();
|
|
294
296
|
});
|
|
295
297
|
it('should pass inverse variant to children when set', async () => {
|
|
296
298
|
let headerRef = null;
|
package/lib/Modal/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
4
|
-
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
|
|
5
4
|
Object.defineProperty(exports, "__esModule", {
|
|
6
5
|
value: true
|
|
7
6
|
});
|
|
@@ -26,10 +25,9 @@ Object.defineProperty(exports, "ModalHeader", {
|
|
|
26
25
|
});
|
|
27
26
|
exports.default = void 0;
|
|
28
27
|
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
|
|
29
|
-
var _react =
|
|
28
|
+
var _react = require("react");
|
|
30
29
|
var _passthroughProps = require("@instructure/ui-react-utils/lib/passthroughProps.js");
|
|
31
30
|
var _safeCloneElement = require("@instructure/ui-react-utils/lib/safeCloneElement.js");
|
|
32
|
-
var _matchComponentTypes = require("@instructure/ui-react-utils/lib/matchComponentTypes.js");
|
|
33
31
|
var _createChainedFunction = require("@instructure/ui-utils/lib/createChainedFunction.js");
|
|
34
32
|
var _testable = require("@instructure/ui-testable/lib/testable.js");
|
|
35
33
|
var _Transition = require("@instructure/ui-motion/lib/Transition");
|
|
@@ -142,15 +140,14 @@ let Modal = exports.Modal = (_dec = (0, _emotion.withStyle)(_styles.default, _th
|
|
|
142
140
|
return _react.Children.map(children, child => {
|
|
143
141
|
if (!child) return; // ignore null, falsy children
|
|
144
142
|
|
|
145
|
-
if ((0,
|
|
143
|
+
if (/*#__PURE__*/(0, _react.isValidElement)(child)) {
|
|
144
|
+
var _child$props;
|
|
146
145
|
return (0, _safeCloneElement.safeCloneElement)(child, {
|
|
147
146
|
variant: variant,
|
|
148
|
-
overflow: child.props.overflow || overflow
|
|
147
|
+
overflow: (child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.overflow) || overflow
|
|
149
148
|
});
|
|
150
149
|
} else {
|
|
151
|
-
return
|
|
152
|
-
variant: variant
|
|
153
|
-
});
|
|
150
|
+
return child;
|
|
154
151
|
}
|
|
155
152
|
});
|
|
156
153
|
}
|
package/lib/Modal/props.js
CHANGED
|
@@ -7,11 +7,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.propTypes = exports.allowedProps = void 0;
|
|
8
8
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
9
9
|
var _element = require("@instructure/ui-prop-types/lib/element.js");
|
|
10
|
-
var _Children = require("@instructure/ui-prop-types/lib/Children.js");
|
|
11
10
|
var _uiMotion = require("@instructure/ui-motion");
|
|
12
|
-
var _ModalHeader = require("./ModalHeader");
|
|
13
|
-
var _ModalBody = require("./ModalBody");
|
|
14
|
-
var _ModalFooter = require("./ModalFooter");
|
|
15
11
|
/*
|
|
16
12
|
* The MIT License (MIT)
|
|
17
13
|
*
|
|
@@ -38,7 +34,7 @@ var _ModalFooter = require("./ModalFooter");
|
|
|
38
34
|
|
|
39
35
|
const propTypes = exports.propTypes = {
|
|
40
36
|
label: _propTypes.default.string.isRequired,
|
|
41
|
-
children:
|
|
37
|
+
children: _propTypes.default.node,
|
|
42
38
|
as: _propTypes.default.elementType,
|
|
43
39
|
size: _propTypes.default.oneOf(['auto', 'small', 'medium', 'large', 'fullscreen']),
|
|
44
40
|
variant: _propTypes.default.oneOf(['default', 'inverse']),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/ui-modal",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.4.0",
|
|
4
4
|
"description": "A component for displaying content in a dialog overlay",
|
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
6
|
"module": "./es/index.js",
|
|
@@ -24,30 +24,30 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@babel/runtime": "^7.25.6",
|
|
27
|
-
"@instructure/console": "10.
|
|
28
|
-
"@instructure/emotion": "10.
|
|
29
|
-
"@instructure/shared-types": "10.
|
|
30
|
-
"@instructure/ui-buttons": "10.
|
|
31
|
-
"@instructure/ui-dialog": "10.
|
|
32
|
-
"@instructure/ui-dom-utils": "10.
|
|
33
|
-
"@instructure/ui-motion": "10.
|
|
34
|
-
"@instructure/ui-overlays": "10.
|
|
35
|
-
"@instructure/ui-portal": "10.
|
|
36
|
-
"@instructure/ui-prop-types": "10.
|
|
37
|
-
"@instructure/ui-react-utils": "10.
|
|
38
|
-
"@instructure/ui-testable": "10.
|
|
39
|
-
"@instructure/ui-utils": "10.
|
|
40
|
-
"@instructure/ui-view": "10.
|
|
27
|
+
"@instructure/console": "10.4.0",
|
|
28
|
+
"@instructure/emotion": "10.4.0",
|
|
29
|
+
"@instructure/shared-types": "10.4.0",
|
|
30
|
+
"@instructure/ui-buttons": "10.4.0",
|
|
31
|
+
"@instructure/ui-dialog": "10.4.0",
|
|
32
|
+
"@instructure/ui-dom-utils": "10.4.0",
|
|
33
|
+
"@instructure/ui-motion": "10.4.0",
|
|
34
|
+
"@instructure/ui-overlays": "10.4.0",
|
|
35
|
+
"@instructure/ui-portal": "10.4.0",
|
|
36
|
+
"@instructure/ui-prop-types": "10.4.0",
|
|
37
|
+
"@instructure/ui-react-utils": "10.4.0",
|
|
38
|
+
"@instructure/ui-testable": "10.4.0",
|
|
39
|
+
"@instructure/ui-utils": "10.4.0",
|
|
40
|
+
"@instructure/ui-view": "10.4.0",
|
|
41
41
|
"prop-types": "^15.8.1"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"react": ">=16.8 <=18"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@instructure/ui-babel-preset": "10.
|
|
48
|
-
"@instructure/ui-color-utils": "10.
|
|
49
|
-
"@instructure/ui-position": "10.
|
|
50
|
-
"@instructure/ui-themes": "10.
|
|
47
|
+
"@instructure/ui-babel-preset": "10.4.0",
|
|
48
|
+
"@instructure/ui-color-utils": "10.4.0",
|
|
49
|
+
"@instructure/ui-position": "10.4.0",
|
|
50
|
+
"@instructure/ui-themes": "10.4.0",
|
|
51
51
|
"@testing-library/jest-dom": "^6.4.6",
|
|
52
52
|
"@testing-library/react": "^16.0.1",
|
|
53
53
|
"@testing-library/user-event": "^14.5.2",
|
package/src/Modal/README.md
CHANGED
|
@@ -1051,6 +1051,100 @@ On smaller viewports (like mobile devices or scaled-up UI), we don't want to los
|
|
|
1051
1051
|
render(<Example />)
|
|
1052
1052
|
```
|
|
1053
1053
|
|
|
1054
|
+
### Using custom children
|
|
1055
|
+
|
|
1056
|
+
Occasionally, you might find it useful to incorporate custom components within a `Modal`, such as a higher-order component for `Modal.Header` or `Modal.Body` or not using built in child components at all. Although this approach is typically not advised, it can sometimes aid in code splitting or achieving more streamlined code, especially for more intricate and sizable `Modal`s.
|
|
1057
|
+
|
|
1058
|
+
Below example demonstrates how to use a higher-order component for `Modal.Body`. `Modal` consists of a `Modal.Header`, a custom `WrappedModalBody` component, and a `View` component. Properties `variant` and `overflow` are passed down to child components. While the original `Modal.Header`, `Modal.Body` and `Modal.Footer` components use these properties, please note that these might cause unpredictable side effects for custom components.
|
|
1059
|
+
|
|
1060
|
+
```js
|
|
1061
|
+
---
|
|
1062
|
+
type: example
|
|
1063
|
+
---
|
|
1064
|
+
|
|
1065
|
+
class Example extends React.Component {
|
|
1066
|
+
constructor (props) {
|
|
1067
|
+
super(props)
|
|
1068
|
+
|
|
1069
|
+
this.state = {
|
|
1070
|
+
open: false
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
handleButtonClick = () => {
|
|
1075
|
+
this.setState(function (state) {
|
|
1076
|
+
return { open: !state.open }
|
|
1077
|
+
})
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
renderCloseButton () {
|
|
1081
|
+
return (
|
|
1082
|
+
<CloseButton
|
|
1083
|
+
color="primary-inverse"
|
|
1084
|
+
placement="end"
|
|
1085
|
+
offset="small"
|
|
1086
|
+
onClick={this.handleButtonClick}
|
|
1087
|
+
screenReaderLabel="Close"
|
|
1088
|
+
/>
|
|
1089
|
+
)
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
render () {
|
|
1093
|
+
return (
|
|
1094
|
+
<div style={{ padding: '0 0 11rem 0', margin: '0 auto' }}>
|
|
1095
|
+
<Button onClick={this.handleButtonClick}>
|
|
1096
|
+
{this.state.open ? 'Close' : 'Open'} the Modal
|
|
1097
|
+
</Button>
|
|
1098
|
+
<Modal
|
|
1099
|
+
as="form"
|
|
1100
|
+
open={this.state.open}
|
|
1101
|
+
onDismiss={() => { this.setState({ open: false }) }}
|
|
1102
|
+
size="large"
|
|
1103
|
+
label="Modal Dialog: Hello World"
|
|
1104
|
+
shouldCloseOnDocumentClick
|
|
1105
|
+
variant='inverse'
|
|
1106
|
+
overflow='scroll'
|
|
1107
|
+
>
|
|
1108
|
+
<Modal.Header>
|
|
1109
|
+
{this.renderCloseButton()}
|
|
1110
|
+
<Heading>This is a Modal with a Modal.Body wrapped in to a HOC</Heading>
|
|
1111
|
+
</Modal.Header>
|
|
1112
|
+
<WrappedModalBody>
|
|
1113
|
+
<Heading level='h3'>WrappedModalBody inherits the variant and overflow properties automatically</Heading>
|
|
1114
|
+
<Text lineHeight="double">{lorem.paragraphs(5)}</Text>
|
|
1115
|
+
</WrappedModalBody>
|
|
1116
|
+
<View
|
|
1117
|
+
as="div"
|
|
1118
|
+
margin="small"
|
|
1119
|
+
padding="large"
|
|
1120
|
+
background="primary">
|
|
1121
|
+
<Heading level='h3'>This View child does not inherit the variant and overflow properties</Heading>
|
|
1122
|
+
<Text>{lorem.paragraphs(5)}</Text>
|
|
1123
|
+
</View>
|
|
1124
|
+
</Modal>
|
|
1125
|
+
</div>
|
|
1126
|
+
)
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
const withLogger = (WrappedComponent) => {
|
|
1131
|
+
class WithLogger extends React.Component {
|
|
1132
|
+
componentDidMount() {
|
|
1133
|
+
console.log('WrappedModelBody mounted');
|
|
1134
|
+
}
|
|
1135
|
+
render() {
|
|
1136
|
+
return <WrappedComponent {...this.props} />;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
return WithLogger;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
const WrappedModalBody = withLogger(Modal.Body)
|
|
1144
|
+
|
|
1145
|
+
render(<Example />)
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1054
1148
|
### Guidelines
|
|
1055
1149
|
|
|
1056
1150
|
```js
|