@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.
@@ -30,6 +30,7 @@ import '@testing-library/jest-dom'
30
30
 
31
31
  import { Modal, ModalHeader, ModalBody, ModalFooter } from '../index'
32
32
  import type { ModalProps } from '../props'
33
+ import { View } from '@instructure/ui-view'
33
34
 
34
35
  describe('<Modal />', () => {
35
36
  let consoleWarningMock: ReturnType<typeof vi.spyOn>
@@ -168,6 +169,23 @@ describe('<Modal />', () => {
168
169
  expect(modalBody).toBeInTheDocument()
169
170
  })
170
171
 
172
+ it('should handle custom children', async () => {
173
+ const bodyText = 'Modal-body-text'
174
+ const { findByText } = render(
175
+ <Modal open label="Modal Dialog" shouldReturnFocus={false}>
176
+ <View>
177
+ This is a custom child
178
+ </View>
179
+ <Modal.Body>{bodyText}</Modal.Body>
180
+ </Modal>
181
+ )
182
+ const modalBody = await findByText(bodyText)
183
+ const customChild = await findByText('This is a custom child')
184
+
185
+ expect(modalBody).toBeInTheDocument()
186
+ expect(customChild).toBeInTheDocument()
187
+ })
188
+
171
189
  it('should apply the aria attributes', async () => {
172
190
  const { findByRole } = render(
173
191
  <Modal open label="Modal Dialog" shouldReturnFocus={false}>
@@ -350,29 +368,6 @@ describe('<Modal />', () => {
350
368
  expect(consoleErrorMock).not.toHaveBeenCalled()
351
369
  })
352
370
 
353
- it('should not pass validation when children are invalid', async () => {
354
- const { findByRole } = render(
355
- <Modal open label="Modal Dialog" shouldReturnFocus={false}>
356
- <Modal.Body>Foo Bar Baz</Modal.Body>
357
- <Modal.Footer>
358
- <button>Cancel</button>
359
- </Modal.Footer>
360
- <Modal.Header>Hello World</Modal.Header>
361
- </Modal>
362
- )
363
- const dialog = await findByRole('dialog')
364
- const expectedErrorMessage =
365
- 'Expected children of Modal in one of the following formats:'
366
-
367
- expect(dialog).toBeInTheDocument()
368
- expect(consoleErrorMock).toHaveBeenCalledWith(
369
- expect.any(String),
370
- expect.any(String),
371
- expect.stringContaining(expectedErrorMessage),
372
- expect.any(String)
373
- )
374
- })
375
-
376
371
  it('should pass inverse variant to children when set', async () => {
377
372
  let headerRef: ModalHeader | null = null
378
373
  let bodyRef: ModalBody | null = null
@@ -23,13 +23,9 @@
23
23
  */
24
24
 
25
25
  /** @jsx jsx */
26
- import React, { Children, Component } from 'react'
26
+ import { Children, Component, isValidElement, ReactElement } from 'react'
27
27
 
28
- import {
29
- passthroughProps,
30
- safeCloneElement,
31
- matchComponentTypes
32
- } from '@instructure/ui-react-utils'
28
+ import { passthroughProps, safeCloneElement } from '@instructure/ui-react-utils'
33
29
  import { createChainedFunction } from '@instructure/ui-utils'
34
30
  import { testable } from '@instructure/ui-testable'
35
31
 
@@ -40,11 +36,8 @@ import { Dialog } from '@instructure/ui-dialog'
40
36
  import { Mask } from '@instructure/ui-overlays'
41
37
 
42
38
  import { ModalHeader } from './ModalHeader'
43
- import type { ModalHeaderProps } from './ModalHeader/props'
44
39
  import { ModalBody } from './ModalBody'
45
- import type { ModalBodyProps } from './ModalBody/props'
46
40
  import { ModalFooter } from './ModalFooter'
47
- import type { ModalFooterProps } from './ModalFooter/props'
48
41
 
49
42
  import { withStyle, jsx } from '@instructure/emotion'
50
43
 
@@ -59,10 +52,6 @@ import type {
59
52
  ModalPropsForTransition
60
53
  } from './props'
61
54
 
62
- type HeaderChild = React.ComponentElement<ModalHeaderProps, ModalHeader>
63
- type BodyChild = React.ComponentElement<ModalBodyProps, ModalBody>
64
- type FooterChild = React.ComponentElement<ModalFooterProps, ModalFooter>
65
-
66
55
  /**
67
56
  ---
68
57
  category: components
@@ -160,23 +149,18 @@ class Modal extends Component<ModalProps, ModalState> {
160
149
  renderChildren() {
161
150
  const { children, variant, overflow } = this.props
162
151
 
163
- return Children.map(
164
- children as (HeaderChild | BodyChild | FooterChild)[],
165
- (child) => {
166
- if (!child) return // ignore null, falsy children
167
-
168
- if (matchComponentTypes<BodyChild>(child, [ModalBody])) {
169
- return safeCloneElement(child, {
170
- variant: variant,
171
- overflow: child.props.overflow || overflow
172
- })
173
- } else {
174
- return safeCloneElement(child, {
175
- variant: variant
176
- })
177
- }
152
+ return Children.map(children as ReactElement, (child) => {
153
+ if (!child) return // ignore null, falsy children
154
+
155
+ if (isValidElement(child)) {
156
+ return safeCloneElement(child, {
157
+ variant: variant,
158
+ overflow: (child?.props as { overflow: string })?.overflow || overflow
159
+ })
160
+ } else {
161
+ return child
178
162
  }
179
- )
163
+ })
180
164
  }
181
165
 
182
166
  renderDialog(
@@ -25,18 +25,11 @@
25
25
  import React from 'react'
26
26
  import PropTypes from 'prop-types'
27
27
 
28
- import {
29
- element,
30
- Children as ChildrenPropTypes
31
- } from '@instructure/ui-prop-types'
28
+ import { element } from '@instructure/ui-prop-types'
32
29
  import { transitionTypePropType } from '@instructure/ui-motion'
33
30
 
34
31
  import { Dialog } from '@instructure/ui-dialog'
35
32
 
36
- import { ModalHeader } from './ModalHeader'
37
- import { ModalBody } from './ModalBody'
38
- import { ModalFooter } from './ModalFooter'
39
-
40
33
  import type {
41
34
  AsElementType,
42
35
  PropValidators,
@@ -154,9 +147,9 @@ type ModalPropsForDialog = {
154
147
 
155
148
  type ModalOwnProps = {
156
149
  /**
157
- * The children to be rendered within the `<Modal />`. Children must be type of: `Modal.Header`, `Modal.Body`, `Modal.Footer`. The `Modal.Body` child is required, and they have to follow this order.
150
+ * Recommended children types are: `Modal.Header`, `Modal.Body`, `Modal.Footer`. Custom children can be used as well. `Variant` and `overflow` properties are always passed down to children.
158
151
  */
159
- children: React.ReactNode // TODO: enforceOrder([ModalHeader, ModalBody, ModalFooter], [ModalHeader, ModalBody], [ModalBody, ModalFooter], [ModalBody])
152
+ children: React.ReactNode
160
153
 
161
154
  /**
162
155
  * The size of the `<Modal />` content
@@ -205,12 +198,7 @@ type ModalState = {
205
198
 
206
199
  const propTypes: PropValidators<PropKeys> = {
207
200
  label: PropTypes.string.isRequired,
208
- children: ChildrenPropTypes.enforceOrder(
209
- [ModalHeader, ModalBody, ModalFooter],
210
- [ModalHeader, ModalBody],
211
- [ModalBody, ModalFooter],
212
- [ModalBody]
213
- ),
201
+ children: PropTypes.node,
214
202
  as: PropTypes.elementType,
215
203
  size: PropTypes.oneOf(['auto', 'small', 'medium', 'large', 'fullscreen']),
216
204
  variant: PropTypes.oneOf(['default', 'inverse']),